Menu
CoddyTech

Estados como classes

Parte da seção Arquitetura de Jogos no Unity do Journey de Unity da Coddy. Lição 35 de 64.

O switch sobre um enum da Seção 3 mantém o trabalho de cada estado em um único Update. Isso deixa de funcionar bem quando os estados têm dados próprios: um timer usado por apenas um estado acaba como um campo do MonoBehaviour, ao lado dos campos de todos os outros estados, e cada case precisa se lembrar de redefinir o que usa. O padrão de estados do C# dá a cada estado sua própria classe, e cada classe implementa uma interface. Este sinalizador rodoviário pisca ou permanece aceso, e a tecla Espaço alterna entre os dois:

public interface IState
{
    void Enter();
    void Tick();
    void Exit();
}

public class BlinkState : IState
{
    private Renderer lamp;
    private float timer;

    public BlinkState(Renderer lamp)
    {
        this.lamp = lamp;
    }

    public void Enter() { timer = 0f; }

    public void Tick()
    {
        timer += Time.deltaTime;
        if (timer >= 0.5f)
        {
            timer = 0f;
            lamp.enabled = !lamp.enabled;
        }
    }

    public void Exit() { lamp.enabled = true; }
}

public class SteadyState : IState
{
    public void Enter() { }
    public void Tick() { }
    public void Exit() { }
}

public class Beacon : MonoBehaviour
{
    public Renderer lamp;
    private IState blink;
    private IState steady;
    private IState current;

    void Start()
    {
        blink = new BlinkState(lamp);
        steady = new SteadyState();
        SetState(steady);
    }

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame) SetState(current == steady ? blink : steady);
        current.Tick();
    }

    void SetState(IState next)
    {
        if (current != null) current.Exit();
        current = next;
        current.Enter();
    }
}

Os estados são classes simples de C#, não MonoBehaviours. Você os cria com new e passa a eles o que precisam pelo construtor — aqui, o Renderer da lâmpada — porque não são componentes e não podem chamar GetComponent nem exibir campos no Inspector. A Unity também nunca chama os métodos deles: Beacon é o MonoBehaviour, então seu Update chama current.Tick() a cada quadro.

O timer pertence apenas a BlinkState, e seu Enter o define como 0 sempre que o sinalizador começa a piscar. Exit faz a limpeza quando o estado termina: a tecla Espaço pode ser pressionada enquanto a lâmpada está apagada, e Exit a acende novamente, para que SteadyState nunca comece com a lâmpada apagada. SetState executa o Exit do estado antigo e, em seguida, o Enter do novo estado. Um terceiro estado, como um pulso lento, é mais uma classe, e o Update de Beacon não muda.

challenge icon

Desafio

Médio

Escreva o estado de varredura da torre.

  1. Em IState.cs, declare public interface IState com void Enter();, void Tick(); e void Exit();.
  2. Em TurretStates.cs, escreva ScanState : IState, recebendo a cabeça. Ao entrar: ângulo 0, cabeça Color.green. No Tick: faça uma varredura com o ângulo y local da cabeça entre -45 e 45, a 90 graus por segundo.
  3. Turret: entre em um estado no Start e atualize-o no Update.

Pressione “Jogar”: a cabeça verde faz a varredura. Em seguida, pressione “Verificar”.

Experimente você mesmo

using UnityEngine;

// ScanState

Todas as lições de Arquitetura de Jogos no Unity

Pratique por conta própria: Playground de Unity