パチスロを作ってみたRart4(状態管理コードを図解で説明)

この記事は約9分で読めます。

今日も今日とて自作パチスロを作っていきます!

Part1は筐体の作成Part2はリールを作成Part3はレバーとボタンを作成しました。

今回は状態管理についてプログラムを作っていきます。

今回のテーマ
  • パチスロのプログラム部分を作っていこう。
  • 状態をわかりやすく管理しよう
  • ステートパターンを実装
  • 言語はC#、Unity

状態管理ってどういう事?

まずは「状態管理ってなんやねん」という辺りについて簡単に説明します。

例えば、「レバーオン=入力」となるわけですが、

  1. メダルを入れていない状態でレバーオン → これは無反応となって欲しい。
  2. メダルを3枚入れた状態でレバーオン → これはリールが始動して欲しい。
  3. リールが始動している状態でレバーオン → これは無反応となって欲しい。

同じレバーオンでも状態によって走る処理は違って欲しいですよね。

でも、そういう条件分岐をIF文で実装するとスパゲティコードが完成しちゃうわけです。

地獄への入り口です。

そこで今回はステートパターンとういうデザインパターンを使用して実現します。

Stateパターンの実装

状態管理はStateControllerというスクリプトで一括管理したいと思います。

それぞれの状態をState(ステート)と呼びます。

どのStateもOnEnter、OnUpdate、OnCkick、OnExitという関数を持っています。

でもステートパターンを使えば、同じ「OnClick」という関数でも、状態(State)によって処理を変える事ができます。

StateChangeStateという関数を呼ぶとStateが遷移します。

OnClickという関数を呼び出す側は、相手の事を気にせずただOnClickを呼び出すだけで良いわけです。

状態(ステート)の遷移について

このStateはクリックするたびに状態が遷移するようにしました。

分かりづらいですが、画面をクリックしています。

あとはそれぞれのStateに処理を記入すればOKです。

ステートの状態遷移UML

クリックするとこの順番で状態が遷移します。

実際のコード

これが状態を管理するための StateController クラスです。

このクラスのOnClickで各Stateごとに実装したOnClickを呼び出す事ができます。

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

public partial class StateController : MonoBehaviour
{

    // ステートのインスタンス  readonlyにすると値の変更がされない変数にできる(コンストラクタ内でのみ値を変更できる)
    private static readonly StateMaxBet stateMaxBet = new StateMaxBet();
    private static readonly StateLeverOn stateLeverOn = new StateLeverOn();
    private static readonly StateWaitTime stateWaitTime = new StateWaitTime();
    private static readonly StateFirstStopButton stateFirstStopButton = new StateFirstStopButton();
    private static readonly StateSecondStopButton stateSecondStopButton = new StateSecondStopButton();
    private static readonly StateThirdStopButton stateThirdStopButton = new StateThirdStopButton();
    private static readonly StateWinner stateWinner = new StateWinner();
    private static readonly StatePayOut statePayOut = new StatePayOut();



    /// <summary>
    /// 現在のステート
    /// </summary>
    private StateBase currentState = stateMaxBet;


    private void Start()
    {
        currentState.OnEnter(this, null);
    }


    private void Update()
    {
        currentState.OnUpdate(this);


        if (Input.GetMouseButtonUp(0))
        {
            OnClick();
        }

    }


    // クリック時に呼ばれる
    public void OnClick()
    {
        currentState.OnClick(this);
    }



    // ステート変更
    public void ChangeState(StateBase nextState)
    {
        currentState.OnExit(this, nextState);
        nextState.OnEnter(this, currentState);
        currentState = nextState;
    }

}

こちらが↓状態を表現するための基底クラスです。

全ての状態はこのStateBaseを派生させたクラスとなります。

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

namespace State
{
	public abstract class StateBase
	{
		// 「virtual キーワード」は派生クラスでオーバーライド可能という意味


		/// <summary>
		/// ステート開始時に呼ばれる処理
		/// </summary>
		/// <param name="owner"> 誰が呼んだか </param>
		/// <param name="prevState"> 一つ前のステート </param>
		public virtual void OnEnter(StateController owner, StateBase prevState){ }


		/// <summary>
		/// 毎フレームのUpDateで呼ばれる処理  
		/// </summary>
		/// <param name="owner"> 誰が呼んだか </param>
		public virtual void OnUpdate(StateController owner) { }


		/// <summary>
		/// ボタンクリック時に呼ばれる処理
		/// </summary>
		/// <param name="owner"> 誰が呼んだか </param>
		/// <param name="nextState"> 次に遷移するステート </param>
		public virtual void OnClick(StateController owner) { }


		/// <summary>
		/// ステート終了時に呼ばれる処理
		/// </summary>
		/// <param name="owner"> 誰が呼んだか </param>
		/// <param name="nextState"> 次に遷移するステート </param>
		public virtual void OnExit(StateController owner, StateBase nextState) { }

	}
}

そしてこちら↓が状態ごとのクラスになります。

これはMaxBetを待っているステート状態)です。

スクリプトを分けてコードを書いていますが「partial」で書いていますので、実際はStateControllerクラスの中に書いている扱いとなります。

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

public partial class StateController
{
    /// <summary>
    /// MaxBet待ちのステート
    /// </summary>
    public class StateMaxBet : StateBase
    {
        public override void OnEnter(StateController owner, StateBase prevState)
        {
            Debug.Log(this.GetType().Name + " に移行しました");
        }


        public override void OnClick(StateController owner)
        {
            // ステート変更します。次は stateLeverOn
            owner.ChangeState(stateLeverOn);
        }

    }
}

次がレバーオンのステート(状態)です。

まだ中に処理を書いていませんので、MaxBetステートと全く同じですね。

他のステートも同じなので割愛します。

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

public partial class StateController
{
    /// <summary>
    /// レバーオン待ちのステート
    /// </summary>
    public class StateLeverOn : StateBase
    {
        public override void OnEnter(StateController owner, StateBase prevState)
        {
            Debug.Log(this.GetType().Name + " に移行しました");
        }


        public override void OnClick(StateController owner)
        {
            owner.ChangeState(stateWaitTime);
        }
    }
}

今回はこんな感じで終了です。

次回は「各状態での処理」or「自作リール図柄」がテーマになると思います。

早くリール動かしてボタンで止まるように動かしてみたいです!

コメント

タイトルとURLをコピーしました