ゲーム制作勉強中!あこがれだったプログラマーに今からなろう!

昔、あこがれていたプログラマー。今からでも勉強してみようと思い立ち、チャレンジ開始! 勉強メモや、悪戦苦闘な日々の記録です。

3D脱出ゲーム:イベントシステムを作成しよう。

今日こそは、ゲームのメイン部分である、イベントの仕組みを作成していきます。
しまづさんの動画で、すでにDrag.csとDrop.cs、それをベースにアイテムをドラッグするためのDragObj.csと
カバンからアイテムをDragするためのDragObjUI.csを作成しているのですが、
単純にアイテムをクリックした時にメッセージ表示やBoolを変化させる、いわゆるゲームイベントの働きをする機能も欲しいと思ったので、
それを作成することにします。

せっかくなので汎用的に色々な機能を持たせたいと思いますので、まずは、代表的なイベントを定義します。
定義したイベントは3つ。
・メッセージを表示するイベント。
・アイテムを取得するイベント。
・flagを操作するイベント。
です。

イベントが決まったので、このイベントを定義するクラス。ObjEvent.csを作成します。

using System;

[Serializable]
public class ObjEvent
{
public enum Type
{
Message,//メッセージを表示するイベント
GetItem,//アイテムを取得するイベント
FlagSet,//Flagを操作するイベント
}

public Type type;//イベントの種類
public Item.Type item;//獲得するアイテムの種類
public String message;//メッセージ

public int True_messageNo;//Trueの時のメッセージNo、もしくは通常のメッセージNo
public int False_messageNo;//falseの時のメッセージNo

public int BoolNo;//FlagのNo
public bool EventOutcome;//イベントの結果でフラグに設定される値
public int SoundNo1;//イベントで鳴らすSEのNo
public int SoundNo2;//イベントで鳴らすSEのNo

public ObjEvent(Type type, Item.Type item,String message,int True_messageNo,int False_messageNo,int BoolNo,bool EventOutcome,int SoundNo1,int SoundNo2)
{
    this.type = type;
    this.item = item;
    this.message = message;
    this.True_messageNo = True_messageNo;
    this.False_messageNo = False_messageNo;
    this.BoolNo = BoolNo;
    this.EventOutcome = EventOutcome;
    this.SoundNo1 = SoundNo1;
    this.SoundNo2 = SoundNo2;
}
}

ObjEventの定義ができたので、次はアイテムにこのObjEventを設定するクラス、EventObj.csを作成します。
名前が似ているのは、センスのなさを感じますが、自分的には、これが一番しっくりきました😅

EventObjは、そのアイテムで発生するイベントを設定し、イベントを起動する働きを持たせます。
ただ、イベントを数多く作るときに、EventObj全部にイベント処理を実行する機能まで持たせるのは、ちょっと無駄なので、
EventObj.csはあくまでイベントの設定と、イベントの起動だけを行い、
イベントの処理は共通クラスのEvent Controllerで行うことにしました。

using UnityEngine;
using Cysharp.Threading.Tasks;

public class EventObj : MonoBehaviour, IEvent //ここに定義していないイベント用にIEventインターフェースも用意
{
[Header("イベント発動に必要な距離")][SerializeField] float EventDistance;
[SerializeField] private Transform SetPos;
[Header("クリックしたときのイベント")][SerializeField] ObjEvent Click_event;
[SerializeField]bool IsSerchEvent=false;//サーチモードでイベントを変えるかどうか
[Header("サーチした時のイベント")][SerializeField] ObjEvent Serch_event;

public void Event()
{
    Transform PlayerTransform=  GameObject.Find("Player").transform;
    GameManager.instance.beforeGameMode=GameManager.instance.gameMode;//ゲームモードを保存

    if(Vector3.Distance(PlayerTransform.position,transform.position)>EventDistance)//イベント発動に必要な距離以下か確認
    {
        if(GameManager.instance.gameMode==GameMode.Type.Normal || GameManager.instance.gameMode==GameMode.Type.Search)
        {
            GameManager.instance.gameMode=GameMode.Type.Message;//ゲームモードをメッセージモードに変更
            MessageManager.instance.ShowGetMessage(2).Forget();
        }
        return;//プレイヤーが近くにいない場合は処理を抜ける。
    }

    if(GameManager.instance.gameMode==GameMode.Type.Normal)
    {
        EventController.instance.Event(Click_event);
    }
    else if(GameManager.instance.gameMode==GameMode.Type.Search)
    {
        if(IsSerchEvent)
        {
            EventController.instance.Event(Serch_event);
        }
        else
        {
            EventController.instance.Event(Click_event);
        }
    }
}
}

EventObj.cs自身は単純なもので、ノーマルモードでクリックした時のイベントとサーチモードでクリックした時の
2つのイベントを格納しておき、それぞれのゲームモードに応じたイベントをEventControllerに送るだけの機能です。
その際、Playerとの距離を確認して、遠すぎる場合は近づくことを促すメッセージを表示するように作成しました。

メッセージ関係は全く別の仕組みのMessageManager.csで制御しています。
この時、メッセージウィンドウの開閉状態で、二重にメッセージを表示させようとするとおかしな動きをするため
ゲームモードをMessagaモードに変更し、この時にはイベント発動ができないように制限しています。

ここに定義しきれないイベントを作成するときに備えて、IEventというEventメソッドを保証するインターフェースも用意して、
次は、イベントを実際に処理するクラスEventContorollerです。

using UnityEngine;
using TMPro;
using DG.Tweening;
using Cysharp.Threading.Tasks;
using System.Threading;
using UnityEngine.UI;
using System;

public class EventController : MonoBehaviour
{
    ObjEvent.Type eventType=ObjEvent.Type.Message;
    Item.Type item;
    String message;

    //Trueの時のメッセージNoもしくは通常のメッセージ
    int True_messageNo;
    int False_messageNo;//falseの時のメッセージNo

    int BoolNo;//FlagのNo
    bool EventOutcome;
    int SoundNo;

    [SerializeField] ZoomPanel zoomPanel;
    public static EventController instance;

    private void Awake() 
    {
        instance=this;
    }

    public void Event(ObjEvent objEvent)
    {
        eventType=objEvent.type;
        switch(eventType)
        {
            case ObjEvent.Type.Message://メッセージを表示するイベント
                    GameManager.instance.beforeGameMode=GameManager.instance.gameMode;
                    GameManager.instance.gameMode=GameMode.Type.Message;
                    if(GameManager.instance.GameFlag[objEvent.BoolNo])
                    {
                        MessageManager.instance.ShowGetMessage(objEvent.True_messageNo).Forget();
                    }
                    else
                    {
                        MessageManager.instance.ShowGetMessage(objEvent.False_messageNo).Forget();
                    }

                break;

            case ObjEvent.Type.GetItem://アイテムを取得するイベント
                    if(GameManager.instance.GameFlag[objEvent.BoolNo])
                    {
                        GameManager.instance.gameMode=GameMode.Type.Message;
                        MessageManager.instance.ShowGetMessage(objEvent.True_messageNo).Forget();
                        return;
                    }

                    var GetItem = ItemGenerator.instance.Spawn(objEvent.item);
                    GameManager.instance.GameFlag[objEvent.BoolNo]=true;

                    //**将来ここで音を鳴らす
                    ItemBox.instance.SetItem(GetItem);
                    zoomPanel.ShowPanel(GetItem);

                    GameManager.instance.gameMode=GameMode.Type.Message;

                    var GetMessage=GetItem.ItemName+"を手に入れた";
                    MessageManager.instance.ShowMessage(GetMessage).Forget();

                break;

            case ObjEvent.Type.FlagSet://Flagを立てるイベント
                    GameManager.instance.beforeGameMode=GameManager.instance.gameMode;
                    GameManager.instance.gameMode=GameMode.Type.Message;
                    if(GameManager.instance.GameFlag[objEvent.BoolNo])
                    {
                        MessageManager.instance.ShowGetMessage(objEvent.True_messageNo).Forget();
                    }
                    else
                    {
                        //**将来ここで音を鳴らす

                        MessageManager.instance.ShowGetMessage(objEvent.False_messageNo).Forget();
                        GameManager.instance.GameFlag[objEvent.BoolNo]=objEvent.EventOutcome;
                    }

                break;

        }
    }
}

テストしながら作成しているので、実はまだ未完成だったりします😅
ただ、これで一応、アイテム取得やメッセージを表示することはできるので、続きはのんびり作成していこうと思っています。

さて、これでイベントシステムまで完成したので、ちょっとここらで一旦休憩して
次は、前々から興味のあったGodotでちょっと遊んでみようと思っています。