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.
Desafio
MédioUma 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()
{
}
}
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
1Componentes, não hierarquias
Componentes pequenosUm script-base para inimigosComponentes abstratosComponentes trabalhando juntosRevisão: Torre modular4Dados Serializados
Classes SerializáveisListas no InspectorEnums no InspectorRevisão: Tabela de Loot7Máquinas de Estados
Estados como ClassesUma Máquina de EstadosTransiçõesInteligências dos InimigosRevisão: Estados da Porta10Projeto: Sobrevivente da Arena
O HeróiTipos de Inimigos5ScriptableObjects
Assets de dadosAtributos das armasConfigurações compartilhadasOndas como dadosRevisão: banco de dados de itens