🎮 UnityDocs

Máquina de estados (State Machine)

Cómo organizar comportamientos que cambian (IA que patrulla, persigue y ataca; el estado del jugador) sin llenar Update de ifs. El patrón State explicado y aplicado a Unity.

🧩 PatrónAvanzado✅ RevisadoUnity 6.0 LTSActualizado 2026-07-15
DificultadConceptual Código Matemáticas
Tiempo📖 Lectura 8 min · ⌨️ Práctica 30 min

El problema#

Un enemigo patrulla, persigue al jugador cuando lo ve, ataca de cerca y huye si le queda poca vida. La versión ingenua mete todo en Update:

C#
// ❌ Crece sin control y se vuelve ilegible
void Update()
{
    if (huyendo) { /* ... */ }
    else if (viendoJugador && cerca) { /* atacar */ }
    else if (viendoJugador) { /* perseguir */ }
    else { /* patrullar */ }
    // ...y cada rama con sus propios flags y condiciones cruzadas
}

En cuanto hay varios estados con transiciones entre ellos, este bloque se convierte en un nido de if y booleanos imposible de mantener.

La solución: patrón State#

Una máquina de estados finita (FSM) separa cada comportamiento en su propia clase. En cada momento hay un estado activo, que sabe qué hacer y cuándo pasar a otro.

C#
public interface IEstado
{
    void Entrar();       // se ejecuta al entrar al estado
    void Actualizar();   // cada frame, mientras es el estado activo
    void Salir();        // se ejecuta al salir del estado
}

El "motor" que gestiona el estado actual:

C#
public class MaquinaEstados
{
    private IEstado actual;

    public void CambiarA(IEstado nuevo)
    {
        actual?.Salir();
        actual = nuevo;
        actual.Entrar();
    }

    public void Tick() => actual?.Actualizar();
}

Un estado concreto#

Cada comportamiento es una clase que implementa IEstado:

C#
using UnityEngine;

public class EstadoPerseguir : IEstado
{
    private readonly Enemigo enemigo;
    public EstadoPerseguir(Enemigo e) => enemigo = e;

    public void Entrar()  => enemigo.PonerAnimacion("Correr");

    public void Actualizar()
    {
        enemigo.MoverHacia(enemigo.Jugador.position);

        if (!enemigo.VeAlJugador())
            enemigo.Maquina.CambiarA(new EstadoPatrullar(enemigo));
        else if (enemigo.DistanciaAlJugador() < 1.5f)
            enemigo.Maquina.CambiarA(new EstadoAtacar(enemigo));
    }

    public void Salir() { }
}

Y el enemigo solo delega:

C#
public class Enemigo : MonoBehaviour
{
    public MaquinaEstados Maquina { get; private set; }

    void Awake()
    {
        Maquina = new MaquinaEstados();
        Maquina.CambiarA(new EstadoPatrullar(this));
    }

    void Update() => Maquina.Tick();
}
Cada estado, una responsabilidad

Ahora "perseguir" vive en EstadoPerseguir, "atacar" en EstadoAtacar, etc. Añadir un comportamiento nuevo es crear una clase, no ampliar un Update gigante. Y las transiciones quedan explícitas dentro de cada estado.

Relación con el Animator#

El Animator es una máquina de estados (visual, para animaciones). Este patrón es la versión en código para la lógica. A menudo van en paralelo: el estado lógico "Atacar" también pone el parámetro del Animator para la animación de ataque.

Cuándo usarla (y cuándo no)#

🛠️ Decisión de ingeniería
  • Pocos estados (2-3) muy simples → quizá un par de if o un enum con switch basta; no te compliques
  • Varios estados con transiciones claras (IA, estado del jugador) → máquina de estados con clases
  • Estados muy complejos con jerarquías y comportamientos combinados → considera una FSM jerárquica o un behaviour tree

Errores frecuentes#

  • Aplicar el patrón a algo con dos estados triviales (sobreingeniería).
  • Estados que se conocen entre sí de más; deja que las transiciones vivan dentro de cada estado.
  • Olvidar Salir()/Entrar() para limpiar y preparar (animaciones, temporizadores).
  • Guardar estado mutable en el sitio equivocado (el estado del enemigo va en el enemigo, no duplicado en cada IEstado).

Ponte a prueba#

📝 Pon a prueba lo aprendido

Resumen#

La máquina de estados separa cada comportamiento (patrullar, perseguir, atacar) en su clase IEstado, con un motor que gestiona el estado activo y sus transiciones Entrar/Actualizar/Salir. Sustituye el Update lleno de if por algo mantenible y ampliable. Úsala cuando haya varios estados con transiciones; para dos triviales, no te compliques. Combínala con el Animator para lógica + animación.

Fuentes y para profundizar