今日は、ゲームのメイン部分である、イベントの仕組みを作成していきます。
イベントの定義などを作成していくのですが、まずその前に、昨日の説明の中で唐突に出てきたGameModeを整理しておこうと思います。
ゲーム中、Playerが今どのような状態なのかを区分けすることで、その時にできる事、できない事の判定がしやすくなります。
また、メッセージを表示している最中に、もう一度メッセージを表示しようとすると、メッセージウィンドウの挙動がおかしくなってしまうなどの
動作異常を防止することもできます。
各クラスから参照させたいので、GameModeはGameManager に保持させるようにしています。
現段階で、どれくらいのModeを用意するか、実はまだ決まりきってはいません😅
とりあえず、以下の4つで作成を始めています。
・Normal:
通常状態。
移動ができる。
ItemBoxからアイテムをドラッグして使用できる。
・Search:
虫眼鏡をクリックして、マウスポインタが虫眼鏡になった状態。通常より詳しく調べるモード。
移動はできない。
ItemBoxのアイテムをクリックして拡大表示できる。
ItemBoxからアイテムをドラッグして使用できる。
・Message:
メッセージウィンドウが開いている状態。移動や他のクリックで、一つ前のNormalもしくはSearchに戻る。
・Terminal:
端末操作中の状態。
移動はできない。
ItemBoxの操作はできない。
こんな感じです。
そして、イベントの中心的な機能といえば、やはりメッセージ表示。
これは、3Dアクションでも作成したので、その応用というか、ほぼ丸コピーです。
追加機能として、今回はメッセージをスクリプタブルオブジェクト(SO)から取得してくる方法以外に、メッセージを表示させるイベントから
直接メッセージを送り込んで表示することもできるようにしています。
これは、アイテムを取得した時に、Item名付きの「***を手に入れた。」というメッセージを表示させるのに、SOからの定型分だけでは
どうしても都合が悪いと思ったからです。
実際には、SO内のメッセージに変数名を割り当てる方法があるのかもしれませんが、僕は残念ながら、まだその方法を知らないので、
パッと思いついた方法で作成することにしました。
作成するといっても、
相変わらず、この記事のコピーもじりではありますが😅
ほんと、いつもお世話になっております。🙇
using UnityEngine; using TMPro; using DG.Tweening; using Cysharp.Threading.Tasks; using System.Threading; using System; public class MessageManager : MonoBehaviour { private CancellationTokenSource cts; CancellationToken token; CancellationToken Destroytoken; [SerializeField] TMP_Text MessageText; public GameObject MessageImage; // int message_no=0; int NextMessageNo; [SerializeField] RectTransform panel; private PlayerInput inputSystemSC; public static MessageManager instance; private void Awake() { if(instance==null) { instance=this; } } private void Start() { inputSystemSC=new PlayerInput(); inputSystemSC.Enable(); cts = new CancellationTokenSource(); token = cts.Token; Destroytoken=this.GetCancellationTokenOnDestroy(); } private async UniTask FadeIn(int nextMessageNo) { // script上でテキストを更新した場合、TMPの更新が終わっていない場合があるので再生成 MessageText.ForceMeshUpdate(true); TMP_TextInfo textInfo = MessageText.textInfo; TMP_CharacterInfo[] charInfos = textInfo.characterInfo; // 全ての文字を一度非表示にする(特殊文字の兼ね合いで要素と文字の数が一致しない場合がある) for (var i = 0; i < charInfos.Length; i++) { SetTextAlpha(MessageText, i, 0); } // charInfosの要素数分ループ for (var i = 0; i < charInfos.Length; i++) { // 空白または改行文字の場合は無視 if (char.IsWhiteSpace(charInfos[i].character)) continue; // 一文字ごとに0.05秒待機 await UniTask.Delay(50); SetTextAlpha(MessageText, i, 255); } await UniTask.WaitUntil(() => Input.GetMouseButtonDown(0)|| inputSystemSC.Player.Move.ReadValue<Vector2>() != Vector2.zero|| inputSystemSC.Player.Look.ReadValue<Vector2>() != Vector2.zero , cancellationToken: Destroytoken); if(nextMessageNo!=0) //次のメッセージがある際は、もう一回メッセージ取得から繰り返す。 { foreach(Message i in MessageSO.Entity.MessageList) { if (i.MessageNo != nextMessageNo) continue; MessageText.text=i.MessageText+"\n"+"-- Click to close --"; message_no=i.MessageNo; NextMessageNo=i.NextMessageNo; //messagetype=i.type; } FadeIn(NextMessageNo).Forget(); return; } //次のメッセージがないなら、メッセージウィンドウを閉じる。 MessageText.text=""; await panel.DOScale(new Vector2(1,0.01f), 0.3f).AsyncWaitForCompletion(); await panel.DOScale(new Vector2(0,0), 0.3f).AsyncWaitForCompletion(); GameManager.instance.gameMode=GameManager.instance.beforeGameMode;//GameModeを戻す。 MessageImage.SetActive(false); } // charIndexで指定した文字の透明度を変更 private void SetTextAlpha(TMP_Text text, int charIndex, byte alpha) { // charIndex番目の文字のデータ構造体を取得 TMP_TextInfo textInfo = text.textInfo; TMP_CharacterInfo charInfo = textInfo.characterInfo[charIndex]; // 文字を構成するメッシュ(矩形)を取得 TMP_MeshInfo meshInfo = textInfo.meshInfo[charInfo.materialReferenceIndex]; // 矩形なので4頂点 var rectVerticesNum = 4; for (var i = 0; i < rectVerticesNum; ++i) { // 一文字を構成する矩形の頂点の透明度を変更 meshInfo.colors32[charInfo.vertexIndex + i].a = alpha; } // 頂点カラーを変更したことを通知 text.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32); } public async UniTask ShowGetMessage(int message_no)//メッセージをSOから取得する際に呼ばれるメソッド。 { // メッセージウィンドウを開く MessageImage.SetActive(true); await panel.DOScale(new Vector2(1,0.01f), 0.3f).AsyncWaitForCompletion(); await panel.DOScale(new Vector2(1,1), 0.3f).AsyncWaitForCompletion(); // SOからメッセージを取得 foreach(Message i in MessageSO.Entity.MessageList) { if (i.MessageNo != message_no) continue; MessageText.text=i.MessageText+"\n"+"-- Click to close --"; message_no=i.MessageNo; NextMessageNo=i.NextMessageNo; Debug.Log(NextMessageNo); } FadeIn(NextMessageNo).Forget(); } public async UniTask ShowMessage(String message)//メッセージ自体を受け取って表示する際に呼ばれるメソッド。 { // メッセージウィンドウを開く MessageImage.SetActive(true); await panel.DOScale(new Vector2(1,0.01f), 0.3f).AsyncWaitForCompletion(); await panel.DOScale(new Vector2(1,1), 0.3f).AsyncWaitForCompletion(); MessageText.text=message+"\n"+"-- Click to close --"; FadeIn(0).Forget(); } private void OnDisable() { cts.Cancel(); } private void OnDestroy() { cts.Cancel(); } }
次は今度こそ、イベント本体の作成をしよう😅