Menu
CoddyTech

Transições

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

Quem decide quando o estado muda? Perguntar ao proprietário coloca todas as regras de volta em um único Update, como o switch fazia. A resposta usual é que cada estado sabe como sair: em seu Tick, ele verifica sua condição e pede à máquina o próximo estado. Esta bomba espera até que o jogador se aproxime, então queima seu pavio e volta a esperar se o jogador fugir:

public class Bomb : MonoBehaviour
{
    public Transform player;
    public StateMachine machine = new StateMachine();
    public IState idle;
    public IState fuse;

    void Start()
    {
        idle = new IdleState(this);
        fuse = new FuseState(this);
        machine.ChangeState(idle);
    }

    void Update() { machine.Tick(); }

    public bool PlayerNear()
    {
        return Vector3.Distance(transform.position, player.position) < 3f;
    }
}

public class IdleState : IState
{
    private Bomb bomb;
    public IdleState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { }
    public void Exit() { }

    public void Tick()
    {
        if (bomb.PlayerNear()) bomb.machine.ChangeState(bomb.fuse);
    }
}

public class FuseState : IState
{
    private Bomb bomb;
    private float timer;
    public FuseState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { timer = 2f; }
    public void Exit() { }

    public void Tick()
    {
        if (!bomb.PlayerNear())
        {
            bomb.machine.ChangeState(bomb.idle);
            return;
        }
        timer -= Time.deltaTime;
        if (timer <= 0f)
        {
            Debug.Log("Boom!");
            Object.Destroy(bomb.gameObject);
        }
    }
}

Cada estado recebe a bomba em seu construtor e acessa por meio dela a máquina, o teste do jogador e os outros estados. A bomba cria cada estado uma única vez, em Start, então um estado nunca cria outro com new: dois estados que criassem um ao outro em seus construtores entrariam em um loop até que a pilha transbordasse. O Update da bomba apenas atualiza a máquina, e as regras ficam nos estados aos quais pertencem: o pavio de 2 segundos é responsabilidade exclusiva de FuseState.

ChangeState executa Exit de FuseState e Enter de IdleState imediatamente, mas o restante de Tick de FuseState ainda seria executado após a chamada. O return interrompe isso; caso contrário, o pavio queimaria por mais um frame em um estado que já terminou. Destroy é escrito como Object.Destroy: um MonoBehaviour herda esse método da classe Object do Unity, e uma classe simples precisa especificar o nome da classe.

challenge icon

Desafio

Médio

Escreva as transições da planta-papa-moscas em cada Tick de FlytrapStates.cs.

  1. Open: quando trap.PlayerInReach(), chame trap.machine.ChangeState(trap.snap);.
  2. Snap: some Time.deltaTime a timer; em 0.5, mude para trap.chew se o jogador ainda estiver ao alcance; caso contrário, para trap.open.
  3. Chew: faça a contagem regressiva de timer a partir de 3; em 0, mude para trap.open.

Pressione “Jogar”, caminhe até a armadilha e desvie dela. Em seguida, pressione “Verificar”.

Experimente você mesmo

using UnityEngine;

public class OpenState : IState
{
    private Flytrap trap;

    public OpenState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        trap.Paint(new Color(0.3f, 0.8f, 0.2f));
    }

    public void Tick()
    {
        // Fechar quando o jogador estiver ao alcance
    }

    public void Exit()
    {
    }
}

public class SnapState : IState
{
    private Flytrap trap;
    private float timer;

    public SnapState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 0f;
        trap.Paint(new Color(1f, 0.55f, 0.1f));
    }

    public void Tick()
    {
        // Após 0.5 segundos: mastigar se o jogador ainda estiver ao alcance; caso contrário, abrir novamente
    }

    public void Exit()
    {
    }
}

public class ChewState : IState
{
    private Flytrap trap;
    private float timer;

    public ChewState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 3f;
        trap.Paint(new Color(0.5f, 0.05f, 0.1f));
        Debug.Log("Chomp!");
    }

    public void Tick()
    {
        // Após 3 segundos: abrir novamente
    }

    public void Exit()
    {
    }
}

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

Pratique por conta própria: Playground de Unity