今日も今日とて自作パチスロを作っていきます!
今回は状態管理についてプログラムを作っていきます。
この記事はPart4になります。自作パチスロについて記事のまとめはこちらです。
状態管理ってどういう事?
まずは「状態管理ってなんやねん」という辺りについて簡単に説明します。
例えば、「レバーオン=入力」となるわけですが、
- メダルを入れていない状態でレバーオン → これは無反応となって欲しい。
- メダルを3枚入れた状態でレバーオン → これはリールが始動して欲しい。
- リールが始動している状態でレバーオン → これは無反応となって欲しい。
同じレバーオンでも状態によって走る処理は違って欲しいですよね。
でも、そういう条件分岐をIF文で実装するとスパゲティコードが完成しちゃうわけです。
地獄への入り口です。
そこで今回はステートパターンとういうデザインパターンを使用して実現します。
Stateパターンの実装
状態管理はStateControllerというスクリプトで一括管理したいと思います。
それぞれの状態をState(ステート)と呼びます。
どのStateもOnEnter、OnUpdate、OnCkick、OnExitという関数を持っています。
でもステートパターンを使えば、同じ「OnClick」という関数でも、状態(State)によって処理を変える事ができます。
StateはChangeStateという関数を呼ぶと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「自作リール図柄」がテーマになると思います。
早くリール動かしてボタンで止まるように動かしてみたいです!
コメント