top of page
Foto del escritorBraulio Madrid

Máquina de estados práctico y simple.




Un problema muy común en el desarrollo de videojuegos es cuando un objeto tiene demasiados comportamientos que cambia dependiendo de ciertas condiciones, esto al final del dia acaba creando unas scripts muy grandes, difíciles de leer y de corregir.


Esto es un problema tan habitual que hizo que tirara demasiados proyectos al traste por no haber sabido que existían patrones de diseño que pudieron haber resueltos muchos de los problemas comunes, como hacer inteligencia artificial decente, un character controller bien estructurado y organizado, o los estados del mismo entorno donde se mueve el personaje, como por ejemplo una puerta que permanece sellada hasta que el personaje cumpla con una misión y le permita avanzar o programar objetos con los que interactúa el personaje con más de un estado, permitiendo más mecánicas y por ende mejor inmersión.


Pero a todo esto que es una máquina de estado, todos los que han programado lo han hecho alguna vez sin darse cuenta, desde que se usa un if else o un switch, ya estás usando una máquina de estado. las condiciones al ser cumplidas ejecutan un conjunto de instrucciones que ya has establecido. Una máquina de estado es simplemente poder cambiar de un conjunto de instrucciones a otro cumpliendo condiciones, lo que lo hace especial es que es más organizado, mejor estructurado.


La estructura de una máquina de estados es simple. Un objeto se encarga de controlar todos los estados que se le asignan, toma un estado inicial para empezar, mientras el estado que inicia se ejecuta espera a que su condición se cumpla para decirle a la máquina de estados que ya cumplió su objetivo y que cambie por el siguiente que el mismo estado ya tiene referenciado. El siguiente estado cumplirá su labor y cuando cumpla sus condiciones le dirá a la máquina que cambia al siguiente estado y así sucesivamente.


Máquina de estados.

Agregamos dos variables del tipo State, la primera será una variable será publica y nos servirá para indicarle al juego que este estado será con el que arranquemos. La segunda variable privada está para guardar el estado actual del objeto.

public State initialState;
private State currentState;

Seguido crearemos una función pública que permita ser activada desde cualquier estado, que tenga como parámetro otro estado. Las instrucciones dentro se encarga de deshabilitar el estado actual antes de pasar al siguiente y se actualiza con el siguiente estado. solo hay que tener cuidado que la variable current state nunca esté vacío o soltará un error de referencia nula.

public void ActivateState(State state) {
        if(currentState)currentState.enabled = false;
        currentState = state;
        currentState.enabled = true;
    }

Esta la opcion de destruir por completo el estado en vez de deshabilitar, pero considero que puede dar problemas haciendo fluctuar a la memoria, colgando y descolgando codigo, prefiero que mientras esté la escena activa, los scripts estén en memoria.


En el método Start llamamos al método para asignar el estado inicial. y eso seria todo para la máquina de estados.

private void Start()
    {
        ActivateState(initialState);
    }

aqui el codigo completo de la máquina de estados.


using UnityEngine;

public class StateMachine : MonoBehaviour
{
    public State initialState;
    private State currentState;

    private void Start()
    {
        ActivateState(initialState);
    }

    public void ActivateState(State state) {
        if(currentState)currentState.enabled = false;
        currentState = state;
        currentState.enabled = true;
    }
}



Estados:

Ahora como se hacen los estados. En este caso hice 2 estados exactamente igual, que hereden de la clase State, lo importante es que en el estado tengan referencia a la máquina de estado y referencia de los estados a los que vayan a cambiar al cumplir las condiciones. en este caso la única condición que puse fue que pasara 5 segundos para cambiar al siguiente estado.


using System.Collections;
using UnityEngine;

public class EstadoX : State
{
    public StateMachine stateMachine;
    public State nextState;

    private void OnEnable()
    {
        StartCoroutine(PreState());
    }

    IEnumerator PreState() {
        yield return new WaitForSeconds(5);
        stateMachine.ActivateState(nextState);
    }
    private void OnDisable()
    {
        StopCoroutine(PreState());
    }
}

Importante tener en cuenta poner las condiciones en cualquier parte, pero que no sea ni en el método Awake o Start, porque estas solo se ejecutan una sola vez desde que arranca el escenario.


La clase State.

La clase state, de momento no tiene nada, porque los estados que hice no tienen aplicación ninguna, pero en el caso que lo use para un proyecto concreto en esta clase podrá guardar variables y métodos en común entre estados, que facilitará aun mas el manejo entre ellas, tambien evita que la máquina de estados asigne otros tipos de objetos como cualquier monobehaviour que podría crear algún daño inesperado. Lo de poner la clase abstracta es porque evita que la misma clase State aunque es un monobehaviour, se pueda arrastrar a un GameObject, además que me permite poder usar métodos vacíos que los mismos estados puedan aplicar como quieran.

using UnityEngine;

public abstract class State : MonoBehaviour
{
   
}

Conclusión.

Una máquina de estados es un patrón de diseño simple, que si se quiere se puede complicar tanto como quiera, con el tiempo descubres que lo simple y básico resulta mejor que lo complejo y versátil, porque al cambio de las versiones se rompe menos, hay que corregir menos y siempre está listo para cualquier proyecto que quieras usarlo.


nos vemos en el siguiente blog.

13 visualizaciones0 comentarios

Commentaires


bottom of page