Menu
CoddyTech

Transiciones

Parte de la sección Arquitectura de juegos en Unity del Journey de Unity de Coddy. Lección 38 de 64.

¿Quién decide cuándo cambia el estado? Dejarlo en manos del objeto propietario vuelve a poner todas las reglas en un solo Update, como ocurría con el switch. La respuesta habitual es que cada estado conoce su propia salida: en su Tick comprueba la condición y le pide a la máquina el siguiente estado. Esta bomba espera hasta que el jugador se acerca, entonces enciende la mecha y vuelve a esperar si el jugador se aleja corriendo:

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 recibe la bomba en su constructor y accede a la máquina, la comprobación del jugador y los demás estados a través de ella. La bomba crea cada estado una sola vez, en Start, así que un estado nunca crea otro con new: dos estados que se crearan mutuamente en sus constructores entrarían en un bucle hasta que se desbordara la pila. El Update de la bomba solo actualiza la máquina, y las reglas están en los estados a los que pertenecen: la mecha de 2 segundos es responsabilidad exclusiva de FuseState.

ChangeState ejecuta Exit de FuseState y Enter de IdleState de inmediato, pero el resto del Tick de FuseState seguiría ejecutándose después de la llamada. return lo detiene; de lo contrario, la mecha ardería un fotograma más aunque el estado ya hubiera terminado. Destroy se escribe Object.Destroy: un MonoBehaviour lo hereda de la clase Object de Unity, y una clase normal tiene que indicar el nombre de la clase.

challenge icon

Desafío

Intermedio

Escribe las transiciones de la atrapamoscas en cada Tick de FlytrapStates.cs.

  1. Abierta: cuando trap.PlayerInReach(), llama a trap.machine.ChangeState(trap.snap);.
  2. Chasquido: suma Time.deltaTime a timer; al llegar a 0.5, cambia a trap.chew si el jugador todavía está al alcance; de lo contrario, a trap.open.
  3. Masticar: cuenta timer regresivamente desde 3; al llegar a 0, cambia a trap.open.

Presiona «Jugar», camina hacia la trampa y luego esquívala. Después, «Comprobar».

Pruébalo tú mismo

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()
    {
        // Cerrar de golpe cuando el jugador esté al 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()
    {
        // Después de 0.5 segundos: masticar si el jugador sigue al alcance; si no, abrir de nuevo
    }

    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()
    {
        // Después de 3 segundos: abrir de nuevo
    }

    public void Exit()
    {
    }
}

Todas las lecciones de Arquitectura de juegos en Unity

Practica por tu cuenta: Playground de Unity