見出し画像

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

前回の記事はこちら
前回はグリッド座標と向きを反映させて、カメラのスクリプトをお借りし、文字列をゲーム上に表示するところまでを行いました。

Messageクラスの実装

Messageクラスを作っていきます。これはどこのスクリプトからでも参照できるように、staticなクラスにします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public static class Message
{
   private static Queue<string> texts = new Queue<string>();

   /**
   * 文字列をキューに加える
   */
   public static void add(string m)
   {
       texts.Enqueue(m);
   }

   /**
   * キューから文字列を取り出す
   */
   public static string get()
   {
       if (texts.Count > 0)
       {
           return texts.Dequeue();
       }
       return null;
   }

  /**
   * キューに格納されている文字列の数を返す
   */
   public static int getCount()
   {
       return texts.Count;
   }
}

キューは先入れ先出し(つまり先に入れたものから取り出される)方式なので、メッセージログを実装するのに向いています。

メッセージを表示するための前準備

データを格納する準備は整いましたが、肝心の表示部分を作っていませんでした。Unityエディターに戻りましょう。
まず、Textをまとめるゲームオブジェクトを作成します。ヒエラルキータブ内で右クリック→「空のオブジェクトを作成」を選択し、名前を「MessageWindow」とします。それをCanvas階層下に移動させ、前回作成したTextをMessageWindowの階層下に配置して下さい。
そして、Textの表示テキストを空に、位置を全て0にしておきましょう。
次に、Textをプレハブ化します。「Message」に名前を変えて、Resources/Prefabsフォルダにドラッグアンドドロップして下さい。
そして、ヒエラルキータブ内のMessageを削除します。

スクリーンショット 2020-04-27 19.18.23

後、MessageWindowオブジェクトを以下のように設定して下さい。

スクリーンショット 2020-04-27 20.37.17

MessageAnimationクラスの実装

せっかくなので少しアニメーションさせてみたいと思います。その為に、Messageオブジェクトに「MessageAnimation」コンポーネントを追加したいと思います。
以下のコードを書いて下さい。

using UnityEngine;

public class MessageAnimation : MonoBehaviour
{
   public float waitTime = 8.0f;
   public float maxPerFrameD = 1.0f;

   private bool isMoving = false;
   private bool isDeleting = false;
   private Vector3 prevPos;
   private int frame = 0;

   // Start is called before the first frame update
   void Start()
   {
       prevPos = transform.position;
   }

   // Update is called once per frame
   void Update()
   {
       Invoke("DeleteMessage", waitTime);
   }

   /**
   * 補完で計算してアニメーションさせる
   */
   public bool MoveMessage(Vector3 p2, float maxPerFrame)
   {
       isMoving = !isDeleting;
       frame += 1;
       float c = maxPerFrame / Time.deltaTime;
       float t = frame / c;
       transform.position = prevPos + (p2 - prevPos) * t;
       if (c <= frame)
       {
           frame = 0;
           transform.position = p2;
           prevPos = p2;
           isMoving = false;
           return true;
       }
       return false;
   }

   /**
   * 削除アニメーション中ではないか
   */
   public bool IsDeleting() => isDeleting;

   /**
   * 削除アニメーション
   */
   private void DeleteMessage()
   {
       if (isMoving) return;
       isDeleting = true;
       MoveMessage(prevPos + new Vector3(0, -1000, 0), maxPerFrameD);
       if (transform.position.y < -100.0f)
       {
           Destroy(gameObject);
       }
   }
}

一定時間後にアタッチされているオブジェクトをアニメーション、削除するのは、もしかしたら別クラスにした方がいいかもしれません。それは各自のご判断にお任せしたいと思います。

MessageWindowクラスの実装

ようやくメッセージを表示するところまで来ました。それではMessageWindowスクリプトを作成します。

using UnityEngine;
using UnityEngine.UI;

public class MessageWindow : MonoBehaviour
{
   public Text text;
   public float maxPerFrameH = 0.5f;
   public float maxPerFrameV = 1.0f;
   private bool isAdding = false;
   private bool isFalling = false;

   // Start is called before the first frame update
   void Start()
   {

   }

   // Update is called once per frame
   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()) continue;
               isFalling = !anim.MoveMessage(transform.position + new Vector3(0, -100*(transform.childCount-i-1), 0), maxPerFrameV);
           }
       }
       else ShowMessage();
   }

   /**
   * メッセージを追加(表示)する
   */
   private void ShowMessage()
   {
       if (Message.GetCount() > 0)
       {
           isAdding = true;
           isFalling = transform.childCount > 0;
           string m = Message.Get();
           Text msg = Instantiate(text, transform);
           msg.transform.position = transform.position + new Vector3(-400, 0, 0);
           msg.text = m;
       }
   }
}

メッセージが追加された時、既に別のオブジェクトがMessageWindowの階層下にある場合は下にずらしてから、一番上にメッセージを追加します。また、アニメーションが終わるまで新しいMessageオブジェクトは追加されません。
テストしてみましょう。PlayerMovementクラスのUpdateメソッドに以下を記述して下さい。

private void Update()
{
   /* 省略 */
   direction = d;
   // 次の一行を追記
   Message.Add(direction.ToString());
   /* 省略 */
}

テストプレイしてみて、以下のようになればOKです。

メッセージの表示

グリッド移動について

少し話を戻します。先日作成したグリッド移動のスクリプトですが、FPSを考慮することを忘れていました。
ここに、書き直した分を載せておきます。気になる方は差し替えておいて下さい。

// maxFrameパラメーターは削除して、以下のパラメーターを代わりに追記して下さい
public float maxPerFrame = 1.67f;
private float complementFrame;

// 以下のメソッドを次のように書き換えて下さい
void Start()
{
    complementFrame = maxPerFrame / Time.deltaTime;
}

private Pos2D Move(Pos2D currentPos, Pos2D newPos, ref int frame)
{
   float px1 = ToWorldX(currentPos.x);
   float pz1 = ToWorldZ(currentPos.z);
   float px2 = ToWorldX(newPos.x);
   float pz2 = ToWorldZ(newPos.z);
   frame += 1;
   float t = frame / complementFrame;
   float newX = px1 + (px2 - px1) * t;
   float newZ = pz1 + (pz2 - pz1) * t;
   transform.position = new Vector3(newX, 0, newZ);
   animator.SetFloat(hashSpeedPara, speed, speedDampTime, Time.deltaTime);
   if (complementFrame <= frame)
   {
       frame = 0;
       return newPos;
   }
   return currentPos;
}


とりあえずStep3はこれで終了です。ここまでお付き合い下さりありがとうございました。

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