Menu
CoddyTech

Estados como clases

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

El cambio basado en un enum de la sección 3 mantiene el trabajo de cada estado en un solo Update. Esto deja de ser escalable cuando los estados tienen sus propios datos: un temporizador que solo usa un estado acaba como campo de MonoBehaviour, junto a los campos de todos los demás estados, y cada caso debe acordarse de restablecer lo que utiliza. El patrón de estados de C# da a cada estado su propia clase, y todas las clases implementan una interfaz. Esta baliza vial parpadea o permanece encendida, y la barra espaciadora alterna entre los dos estados:

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();
    }
}

Los estados son clases normales de C#, no MonoBehaviours. Los creas con new y les pasas lo que necesitan mediante el constructor; en este caso, el Renderer de la lámpara, porque no son componentes y no pueden llamar a GetComponent ni mostrar campos en el Inspector. Unity tampoco llama a sus métodos: Beacon es el MonoBehaviour, así que su Update llama a current.Tick() en cada fotograma.

El timer pertenece únicamente a BlinkState, y su Enter lo establece en 0 cada vez que la baliza empieza a parpadear. Exit se encarga de la limpieza cuando termina el estado: la barra espaciadora puede pulsarse mientras la lámpara está apagada, y Exit vuelve a encenderla, así que SteadyState nunca empieza con la lámpara apagada. SetState ejecuta Exit del estado anterior y después Enter del nuevo estado. Un tercer estado, como un pulso lento, sería una clase más, y el Update de Beacon no cambiaría.

challenge icon

Desafío

Intermedio

Escribe el estado de escaneo de la torreta.

  1. En IState.cs, declara public interface IState con void Enter();, void Tick(); y void Exit();.
  2. En TurretStates.cs, escribe ScanState : IState, con la cabeza como argumento. Enter: ángulo 0, cabeza Color.green. Tick: mueve la cabeza de un lado a otro, entre los ángulos y locales del eje y -45 y 45, a 90 grados por segundo.
  3. Turret: inicia uno en Start y actualízalo en Update.

Pulsa «Jugar»: la cabeza verde se mueve de un lado a otro. Después, pulsa «Comprobar».

Pruébalo tú mismo

using UnityEngine;

// ScanState

Todas las lecciones de Arquitectura de juegos en Unity

Practica por tu cuenta: Playground de Unity