気ままなUnityエンジニアブログ

新米Unityエンジニアが送る解説アウトプットブログです。Twitter : @UjinUnity

MENU

【Unity ゲーム制作】神経衰弱ゲームを作ろう!〜その10 (Enumを使ってゲームの進行を管理しよう!)

スポンサーリンク

前回は、タイマー機能を追加しました。
今回は「Enum」を使用してゲームの進行 ( ステート ) を管理したいと思います、。

1. カードの種類を増やそう

まずは神経衰弱で使うカードを増やしましょう。

現時点では12枚のカードが表示されていると思うので、新たに「3つ」の新規カードを導入します。


f:id:Wojtek:20191014134149p:plain


今回は「いらすとや」さんにて、フリー画像を拝借しました。

その後、「CardCreateManager.cs」を以下のように変更します。

        // Resources/Imageフォルダ内にある画像を取得する
        imgList.Add (Resources.Load<Sprite> ("Image/card_image_000"));
        imgList.Add (Resources.Load<Sprite> ("Image/card_image_001"));
        imgList.Add (Resources.Load<Sprite> ("Image/card_image_002"));
        imgList.Add (Resources.Load<Sprite> ("Image/card_image_003"));
        imgList.Add (Resources.Load<Sprite> ("Image/card_image_004"));
        imgList.Add (Resources.Load<Sprite> ("Image/card_image_005"));

        // forを回す回数を取得する
        int loopCnt = imgList.Count;

        for (int i = 0; i < loopCnt; i++) {
            // カード情報を生成する
            CardData cardata = new CardData (i, imgList[i]);
            cardDataList.Add (cardata);
        }

        this.mIndex = 0;
        this.mHelgthIdx = 0;
        this.mWidthIdx = 0;

        // 生成したカードリスト2つ分のリストを生成する
        List<CardData> SumCardDataList = new List<CardData> ();
        SumCardDataList.AddRange (cardDataList);

読み込むカードの種類を増やしました。

では、次はゲーム進行部分を作成したいと思います。

2 . Enum で 「EGameState」を作成する


新規で「EGameState.cs」を作成します

/// <summary>
/// ゲームの進行ステート
/// </summary>
public enum EGameState {

    START = 0,
    READY = 1,
    GAME = 2,
    RESULT = 3
}

今回は enum を使用しました。
enum は「列挙型」と呼ばれるもので、状態管理などで使用されます。

それでは、作成した EGameState をゲームロジックに組み合わせましょう。


3 . GameSceneManager .cs でゲームステート関数を作成する


まずは「GameSceneManager.cs 」にEGameState の変数を追加します。

public class GameSceneManager : MonoBehaviour {

    // 一致したカードIDリスト
    private List<int> mContainCardIdList = new List<int> ();

    // カード生成マネージャクラス
    public CardCreateManager CardCreate;

    // 時間管理クラス
    public TimerManager timerManager;

    // 経過時間
    private float mElapsedTime;

    // ゲームステート管理
    private EGameState mEGameState;


そして、Start関数で初期化しましょう。

    void Start () {

        // 一致したカードIDリストを初期化
        this.mContainCardIdList.Clear ();

        // カードリストを生成する
        this.CardCreate.CreateCard ();

        // 時間を初期化
        this.mElapsedTime = 0f;

        // ゲームステートを初期化

        this.mEGameState = EGameState.START;
    }


そして、「ゲームのステートによって処理を振り分ける関数」を作成します。

    /// <summary>
    /// ゲームステートで処理を変更する
    /// </summary>
    private void mSetGameState () {

        switch (this.mEGameState) {
            // スタート画面
            case EGameState.START:
                break;
                // ゲーム準備期間
            case EGameState.READY:
                break;
                // ゲーム中
            case EGameState.GAME:
                break;
                // 結果画面
            case EGameState.RESULT:
                break;
        }
    }

内容はEnum ( 列挙型 ) のEGameState をSwitch文で切り分けます。

先ほどのStart関数にmSetGameState を宣言しましょう。

    void Start () {

        // 一致したカードIDリストを初期化
        this.mContainCardIdList.Clear ();

        // カードリストを生成する
        this.CardCreate.CreateCard ();

        // 時間を初期化
        this.mElapsedTime = 0f;

        // ゲームステートを初期化
        this.mEGameState = EGameState.START;

        // ゲームのステート管理
        this.mSetGameState ();
    }


それでは、mSetGameState 関数の Switch文に処理を追加していきましょう。


4. ゲームの準備ステート用の関数を作成し、mSetGameStateに追加する

今回は、「ゲームの準備ステート」から作成していきます。

まずは 「mSetGameReady」関数を作成し、今までStart 関数で初期化していた部分をこの関数の中に移しましょう。

    /// <summary>
    /// ゲームの準備ステートを開始する
    /// </summary>
    private void mSetGameReady () {

        // 一致したカードIDリストを初期化
        this.mContainCardIdList.Clear ();

        // カードリストを生成する
        this.CardCreate.CreateCard ();

        // 時間を初期化
        this.mElapsedTime = 0f;

    }

そしてこの関数をmSetGameState の EGameState.Ready のswitch文に宣言します。

    /// <summary>
    /// ゲームステートで処理を変更する
    /// </summary>
    private void mSetGameState () {

        switch (this.mEGameState) {
            // スタート画面
            case EGameState.START:
                break;
                // ゲーム準備期間
            case EGameState.READY:
                // ゲームの準備ステートを開始する
                this.mSetGameReady ();
                break;
                // ゲーム中
            case EGameState.GAME:
                break;
                // 結果画面
            case EGameState.RESULT:
                break;
        }
    }

これを行うことで、今までStart関数で行なっていた初期化処理を、EGameState が READY になった時に行えるようになりました。

次にStart関数で初期化していたmEGameState の初期化を「Start」から「Ready」に変更しましょう。

    void Start () {

        // ゲームステートを初期化
        this.mEGameState = EGameState.READY;

        // ゲームのステート管理
        this.mSetGameState ();
    }

Start関数がサッパリしたと思います。

次は、EGameStateが 「Game」の時の処理を実装しましょう

5. UpdateにEGameState が GAME の時のみ、処理を実行するように変更しよう!


では、Update 関数に 「ゲームステートが GAME の状態の時のみ、カードの判別処理を実装する」処理を実装しましょう。

    void Update () {

        // GameState が GAME状態なら
        if (this.mEGameState == EGameState.GAME) {
            this.mElapsedTime += Time.deltaTime;

            this.timerManager.SetText ((int) this.mElapsedTime);

            // 選択したカードが2枚以上になったら
            if (GameStateController.Instance.SelectedCardIdList.Count >= 2) {

                // 最初に選択したCardIDを取得する
                int selectedId = GameStateController.Instance.SelectedCardIdList[0];

                // 2枚目にあったカードと一緒だったら
                if (selectedId == GameStateController.Instance.SelectedCardIdList[1]) {

                    Debug.Log ($"Contains! {selectedId}");
                    // 一致したカードIDを保存する
                    this.mContainCardIdList.Add (selectedId);
                }

                // カードの表示切り替えを行う
                this.CardCreate.HideCardList (this.mContainCardIdList);

                // 選択したカードリストを初期化する
                GameStateController.Instance.SelectedCardIdList.Clear ();
            }
        }

    }

これで、GAMEステートの時 ( ゲーム中 )のみ、タイマーが加算され、カード処理が実装されるようになりました。

最後にREADYの処理が完了したら、GameStateをGAME に変更する処理を実装しましょう。


まずはCardCreateManager.csにカードのアニメーションが終了した時に呼ぶコールバック関数を作成しましょう。

public class CardCreateManager : MonoBehaviour {

    // 生成するCardオブジェクト
    public Card CardPrefab;

    // 「カード」を生成する親オブジェクト
    public RectTransform CardCreateParent;

    // 生成したカードオブジェクトを保存する
    public List<Card> CardList = new List<Card> ();

    // カード情報の順位をランダムに変更したリスト
    private List<CardData> mRandomCardDataList = new List<CardData> ();

    // GridLayoutGroup
    public GridLayoutGroup GridLayout;

    // カードの生成アニメーションが終わった時
    public Action OnCardAnimeComp;


そして、「mSetDealCardAnime」関数のアニメーションが終了した処理部分に、コールバックが呼ばれる処理を追加しましょう。

        // DOAnchorPosでアニメーションを行う
        card.mRt.DOAnchorPos (new Vector2 (posX, posY), this.DEAL_CAED_TIME)
            // アニメーションが終了したら
            .OnComplete (() => {
                // 生成したカードオブジェクトを保存する
                this.CardList.Add (card);

                // 生成するカードデータリストのインデックスを更新
                this.mIndex++;
                this.mWidthIdx++;

                // 生成インデックスがリストの最大値を迎えたら
                if (this.mIndex >= this.mRandomCardDataList.Count) {
                    // GridLayoutを有効にし、生成処理を終了する
                    this.GridLayout.enabled = true;

                    // アニメーション終了時の関数を宣言する
                    if (this.OnCardAnimeComp != null) {
                        this.OnCardAnimeComp ();
                    }
                } 


そして、Ready の準備関数に「カード配布アニメーションが終了した時のコールバック処理」を作成しましょう。

    private void mSetGameReady () {

        // カード配布アニメーションが終了した後のコールバック処理を実装する
        this.CardCreate.OnCardAnimeComp = null;
        this.CardCreate.OnCardAnimeComp = () => {

            // ゲームステートをGAME状態に変更する
            this.mEGameState = EGameState.GAME;
            this.mSetGameState ();
        };

        // 一致したカードIDリストを初期化
        this.mContainCardIdList.Clear ();

        // カードリストを生成する
        this.CardCreate.CreateCard ();

        // 時間を初期化
        this.mElapsedTime = 0f;
    }


ここまで出来たら UnityでGameを実行してみましょう。



f:id:Wojtek:20191014150103g:plain


カードの配布アニメーションが終了してからタイマーが起動したら成功です!