Skip to content

State pattern

Jiufen edited this page Apr 4, 2021 · 2 revisions

❔ Purpouse ❔

The state pattern is used when, as the name says, we know we will have certain states in our game and those states will share a behaviour but will implement that behaviour different.

Example: Pokemon game. The behaviours can be attack, heal, use item, etc. For the states of the game there could be a PlayerTurn and an EnenyTurn, also a beggining and end state.

🏗 Implementation 🏗

🧱 Abstracts 🧱

We will use 2 abstract classes

  • StateManager The instance of this abstract class will have the state for invoking the behaviours and will change this state with the SetState method.
public abstract class StateManager : MonoBehaviour
{
    protected State currentState;

    public void SetState(State state){
        currentState = state;
        StartCoroutine(currentState.Start());
    }
}
  • State

The state will have the previosly described behaviours, but each instance of the abstract class will override the behaviours with the nedeed logic.

public abstract class StateManager : MonoBehaviour
{
    protected State currentState;

    public void SetState(State state){
        currentState = state;
        StartCoroutine(currentState.Start());
    }
}

⚒ Abstracts implementations ⚒

  • BattleSystem (StateManager)

This class will manage the initial flow and call of behaviours of the state machine.

public class BattleSystem : StateManager
{
    [SerializeField] public UIManagerState UIManager;
    private void Start() {
        SetState(new Begin(this));
    }
    
    public void Attack(){
        StartCoroutine(currentState.Attack());
    }

    public void Heal(){
        StartCoroutine(currentState.Heal());
    }
}
  • PlayerTurn (State)

When we execute the attack method in the BattleSystem only the classes that override this attack behaviour will execute it. In this case we simply get the enemy life and subtract a random number. After we finish we check if the game it's finished so we can change the state to End, if not then we continue setting the state to EnemyTurn.

public class PlayerTurn : State
{
    public PlayerTurn(BattleSystem paramBattleSystem) : base(paramBattleSystem) {}

    public override IEnumerator Start() {
        battleSystem.UIManager.changeTextOfUIElement("StateValue", "PlayerTurn");
        battleSystem.UIManager.changeTextOfUIElement("InfoText", "What are you going to do: ");
        yield break;
    }
    public override IEnumerator Attack(){
        TextMeshProUGUI enemyLife;        
        int enemyLifeInt;
        if(battleSystem.UIManager.GetUITextElement("EnemyLife",out enemyLife))
            if(int.TryParse(enemyLife.text, out enemyLifeInt)){
                if(enemyLifeInt > 0){
                    enemyLifeInt-=Random.Range(5,20);
                    battleSystem.UIManager.changeTextOfUIElement("EnemyLife", "" + enemyLifeInt);
                    if(enemyLifeInt <= 0){
                        battleSystem.UIManager.changeTextOfUIElement("InfoText", "Player Won :D !!!");
                        battleSystem.SetState(new End(battleSystem));
                        yield break;
                    }
                }
                battleSystem.SetState(new EnemyTurn(battleSystem));
            }
        yield break;
    }
}

Clone this wiki locally