見出し画像

【Unity】3Dローグライクゲームの作り方〜Step14-1〜

前回の記事はこちら
前回はゲームオーバー画面などを実装しました。

本ステップの予定

さて、本ステップでは主にお店や拠点を実装していきたいと思っています。
大体思いつく拠点内施設は以下の通りです。

・ショップ(コマンドで売買)
・倉庫
・アイテム合成屋
・宿屋
・クエスト受注場所(ギルドなど)
・情報収集場(民家や酒場など)

一応上から順に実装する可能性の高いものとなっております。
恐らくアイテム合成屋までは確実に実装する......とは思いますが、こればかりは断言できないのでご了承下さい。
また、ダンジョン内でもショップを実装したいのですが、現在手元にローグライクゲームがないのでショップの有用な資料がございません。何か資料などご存知の方がいらっしゃいましたらお教えくださると幸いです。
(教えて頂く以上できる限りは頑張りますが、資料があっても実装難度が高そうと判断した場合は、一部仕様を改変するかコマンド式にするかもしれません。その際は申し訳ありません......!)
筆者もダンジョン内ショップに挑戦してみたいのでどうぞよろしくお願い致します。

拠点のマップ

拠点を作成する前に一度何かで拠点のマップを作成しておいた方がいいです(もちろん手書きでも構いませんが、個人的にはデジタルで作成しておいた方がゲームでも使用できるかもしれないのでいいのではないかと思います)。
何故かというと、これはRPGなどを作成する時もそうなのですが、いきなり街を作成しようとしてもどこから手をつけたらいいかわからなくなりがちだからです。それに変更が容易な段階を踏むことで、どこに何の施設を置けばプレイヤーにストレスなくプレイして貰えるかなど考えを練ることができるので、レベルデザインにおいても有利です。
という訳で筆者も作成しました。それがこちらです。

マップサンプル

拠点の作成

マップが作成できたところで拠点シーンを作成していきましょう。
まずは新しく「TownScene」という名前でシーンを作成し、そこに拠点を作成していきます。
筆者は以下のアセットを使用します。

スクリーンショット 2020-11-04 10.04.23

「Fantasy Kindom - Building Pack Lite」

スクリーンショット 2020-11-04 13.15.39

「Baker's House」

スクリーンショット 2020-11-04 13.17.14

「Stylized Fantasy House」

スクリーンショット 2020-10-21 0.57.06

「Low Poly Tree Pack」

そして最終的にこんな感じになりました。

スクリーンショット 2020-11-04 16.03.25

当初の予定より建物の数が減っていますが、まあいいでしょう。また、看板のいいアセットが見つからなかったので吊り看板を看板に見立てました。......ちょっと変ですかね?
ヒエラルキータブはこんな感じです。

スクリーンショット 2020-11-04 14.30.57

プレイヤーを拠点内で動かす

最後にプレイヤーを拠点内で動かしてみたいと思います。
「PlayerMovementInTown」スクリプトを作成し、以下を記述して下さい。

using UnityEngine;

public class PlayerMovementInTown : MonoBehaviour
{
   public Animator animator;
   public Transform canMoveFloor;
   public float speedDampTime = 0.01f;
   public float speed = 0.1f;

   private readonly int hashSpeedPara = Animator.StringToHash("Speed");

   void Update()
   {
       float x = Input.GetAxis("Horizontal");
       float z = Input.GetAxis("Vertical");

       Vector3 playerPos = new Vector3(x, 0, z);

       if (playerPos.magnitude > 0.1)
       {
           transform.rotation = Quaternion.LookRotation(playerPos);
           transform.Translate(Vector3.forward * speed);
           transform.position = new Vector3(Mathf.Clamp(transform.position.x,
               canMoveFloor.position.x - canMoveFloor.localScale.x * 5,
               canMoveFloor.position.x + canMoveFloor.localScale.x * 5),
               transform.position.y, Mathf.Clamp(transform.position.z,
               canMoveFloor.position.z - canMoveFloor.localScale.z * 5,
               canMoveFloor.position.z + canMoveFloor.localScale.z * 5));
           animator.SetFloat(hashSpeedPara, speed * 8, speedDampTime, Time.deltaTime);
       }
       else
       {
           animator.SetFloat(hashSpeedPara, 0.0f, speedDampTime, Time.deltaTime);
       }
   }
}

以下のサイトを参考にさせて頂きました。

canMoveFloorには移動できる範囲に拡大したPlaneオブジェクトを指定します。
実行してみます。拠点内をプレイヤーが歩き回ればOKです。

拠点内移動

おまけ:メッセージが表示されない?

まれにメッセージが表示されなくなる件について、かなり前から把握していたのですが、再現が難しくそのまま放置していました。
ですがそのままだとゲーム自体がしづらくなりそうだったので、原因調査を行ったところ、恐らくここかなという場所が見つかったので紹介しておきます。
MessageWindowクラスを以下のように変更して下さい。

void Update()
{
   if (isAdding)
   {
       MessageAnimation anim;
       if (!isFalling)
       {
           anim = transform.GetChild(transform.childCount - 1).GetComponent<MessageAnimation>();
           isAdding = !anim.MoveMessage(transform.position + new Vector3(0, 0, 0), maxPerFrameH);
           return;
       }
           
       for (int i = 0; i < transform.childCount - 1; i++)
       {
           anim = transform.GetChild(i).GetComponent<MessageAnimation>();
           if (anim.IsDeleting())
           {
               isFalling = false;
               continue;
           }
           float height = anim.GetComponent<RectTransform>().sizeDelta.y;
           isFalling =
               !anim.MoveMessage(transform.position + new Vector3(0, -height * (transform.childCount - i - 1), 0), maxPerFrameV);
       }
   }
   else ShowMessage();
}

予想では削除アニメーションと被った時にisFallingをfalseにしていなかったことが原因ではないかと考えています。
ただ、完全に起きないことを証明するのは難しいので本当にここが原因かは分かりません。今後再発したらまた調査致します(今の所は筆者の方では再発していません)。
ついでにこの前宝箱が開かなくなるバグにも1回遭遇したので、また再発したら原因調査しますね。

おまけ:ランダム生成階でマップ読み込み時にエラーになる

その時は作成したMapDataを開きましょう。
すると読み込みできるはずです。
あるいはMapDataクラスのReadMapFileメソッドの該当箇所を変更します。

// Array2D data = null; を下記に書き換え
Array2D data = new Array2D(0, 0);

というところで短めですが今回はここまでにします。
次回はアイテムに売値をつけるところから始めたいと思います。

この記事が気に入ったらサポートをしてみませんか?