Menu
CoddyTech

Transições

Parte da seção Arquitetura de jogos no Unity do Journey de Unity da Coddy. Lição 30 de 50.

Quem decide quando o estado muda? Perguntar ao proprietário coloca todas as regras de volta em um único Update, como fazia o switch. A resposta usual é que cada estado conhece sua própria saída: em seu Tick, ele verifica a 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 a máquina, a verificação do jogador e os outros estados por meio dela. 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é a pilha transbordar. O Update da bomba apenas executa o Tick da 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 depois da chamada. O return interrompe essa execução; 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 comum precisa especificar o nome da classe.

challenge icon

Desafio

Médio

Uma dioneia dá um bote em qualquer pessoa que se aproxime dela. Flytrap.cs está pronto: ele cria a máquina e os três estados (os campos públicos open, snap e chew), inicia em Open e atualiza a máquina a cada quadro, e PlayerInReach() informa se o jogador está a menos de 2 unidades de distância pelo chão. Em FlytrapStates.cs, cada Enter já está escrito (as cores, os temporizadores, o log Chomp!). Escreva as transições no Tick de cada estado:

  • Open: quando o jogador está ao alcance, a armadilha dá um bote.
  • Snap dura 0.5 segundos. Em seguida, a armadilha mastiga se o jogador ainda estiver ao alcance e volta a abrir se ele tiver escapado.
  • Chew dura 3 segundos, independentemente de quem estiver por perto; depois, a armadilha volta a abrir.

As setas movem o Player. Pressione Play, caminhe até a armadilha e tente desviar dela; depois, pressione Check.

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()
    {
    }
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

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