【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を削除します。
後、MessageWindowオブジェクトを以下のように設定して下さい。
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はこれで終了です。ここまでお付き合い下さりありがとうございました。
この記事が気に入ったらサポートをしてみませんか?