Introducción

Las Máquinas de Estados Finite (FSM, por sus siglas en inglés) son una herramienta fundamental en la programación de comportamientos de IA en videojuegos. Permiten modelar el comportamiento de un personaje o sistema en términos de estados y transiciones, facilitando la toma de decisiones y la gestión de acciones complejas.

Conceptos Clave

  1. Estado: Representa una condición o situación específica en la que se encuentra el sistema o personaje.
  2. Transición: Es el cambio de un estado a otro, generalmente desencadenado por un evento o condición.
  3. Evento: Una acción o condición que provoca una transición entre estados.
  4. Acción: Comportamiento o actividad que se realiza en un estado específico o durante una transición.

Ejemplo de FSM

Consideremos un NPC (Non-Playable Character) en un juego de aventuras que puede estar en uno de los siguientes estados:

  • Patrullando
  • Persiguiendo
  • Atacando
  • Huyendo

Diagrama de Estados

[Patrullando] --(ve al jugador)--> [Persiguiendo]
[Persiguiendo] --(alcanza al jugador)--> [Atacando]
[Atacando] --(jugador huye)--> [Persiguiendo]
[Persiguiendo] --(jugador se escapa)--> [Patrullando]
[Atacando] --(vida baja)--> [Huyendo]
[Huyendo] --(seguro)--> [Patrullando]

Tabla de Transiciones

Estado Actual Evento Estado Siguiente Acción
Patrullando Ve al jugador Persiguiendo Comenzar persecución
Persiguiendo Alcanza al jugador Atacando Iniciar ataque
Atacando Jugador huye Persiguiendo Perseguir jugador
Persiguiendo Jugador se escapa Patrullando Reanudar patrulla
Atacando Vida baja Huyendo Buscar refugio
Huyendo Seguro Patrullando Reanudar patrulla

Implementación en Código

A continuación, se presenta un ejemplo de implementación de una FSM en Python para el NPC descrito anteriormente.

class State:
    def __init__(self, name):
        self.name = name

    def on_event(self, event):
        pass

class Patrolling(State):
    def __init__(self):
        super().__init__("Patrullando")

    def on_event(self, event):
        if event == "ve_al_jugador":
            return Chasing()
        return self

class Chasing(State):
    def __init__(self):
        super().__init__("Persiguiendo")

    def on_event(self, event):
        if event == "alcanza_al_jugador":
            return Attacking()
        elif event == "jugador_se_escapa":
            return Patrolling()
        return self

class Attacking(State):
    def __init__(self):
        super().__init__("Atacando")

    def on_event(self, event):
        if event == "jugador_huye":
            return Chasing()
        elif event == "vida_baja":
            return Fleeing()
        return self

class Fleeing(State):
    def __init__(self):
        super().__init__("Huyendo")

    def on_event(self, event):
        if event == "seguro":
            return Patrolling()
        return self

class NPC:
    def __init__(self):
        self.state = Patrolling()

    def on_event(self, event):
        self.state = self.state.on_event(event)

    def __str__(self):
        return self.state.name

# Ejemplo de uso
npc = NPC()
print(npc)  # Patrullando

npc.on_event("ve_al_jugador")
print(npc)  # Persiguiendo

npc.on_event("alcanza_al_jugador")
print(npc)  # Atacando

npc.on_event("vida_baja")
print(npc)  # Huyendo

npc.on_event("seguro")
print(npc)  # Patrullando

Explicación del Código

  1. Clase State: Clase base para todos los estados. Cada estado hereda de esta clase y redefine el método on_event para manejar las transiciones específicas.
  2. Clases de Estados: Patrolling, Chasing, Attacking, y Fleeing son clases que representan los diferentes estados del NPC. Cada una redefine el método on_event para manejar las transiciones.
  3. Clase NPC: Representa al NPC y mantiene su estado actual. El método on_event permite cambiar el estado del NPC basado en eventos.
  4. Ejemplo de Uso: Se crea una instancia de NPC y se simulan eventos para mostrar cómo cambia el estado del NPC.

Ejercicio Práctico

Ejercicio 1: Añadir un Nuevo Estado

Añade un nuevo estado llamado "Escondido" que se activa cuando el NPC está huyendo y encuentra un escondite. El NPC debe permanecer en el estado "Escondido" hasta que el jugador se aleje.

Solución

  1. Define la nueva clase Hiding que hereda de State.
  2. Modifica la clase Fleeing para que pueda transicionar al estado Hiding.
  3. Modifica la clase Hiding para que pueda transicionar de vuelta a Patrolling cuando el jugador se aleje.
class Hiding(State):
    def __init__(self):
        super().__init__("Escondido")

    def on_event(self, event):
        if event == "jugador_se_aleja":
            return Patrolling()
        return self

class Fleeing(State):
    def __init__(self):
        super().__init__("Huyendo")

    def on_event(self, event):
        if event == "seguro":
            return Patrolling()
        elif event == "encuentra_escondite":
            return Hiding()
        return self

# Ejemplo de uso
npc = NPC()
print(npc)  # Patrullando

npc.on_event("ve_al_jugador")
print(npc)  # Persiguiendo

npc.on_event("alcanza_al_jugador")
print(npc)  # Atacando

npc.on_event("vida_baja")
print(npc)  # Huyendo

npc.on_event("encuentra_escondite")
print(npc)  # Escondido

npc.on_event("jugador_se_aleja")
print(npc)  # Patrullando

Conclusión

Las Máquinas de Estados Finite son una herramienta poderosa y flexible para modelar comportamientos en videojuegos. Permiten gestionar de manera clara y estructurada los diferentes estados y transiciones de un personaje o sistema, facilitando la implementación y el mantenimiento del código. En el siguiente tema, exploraremos los Árboles de Decisión, otra técnica útil para la toma de decisiones en IA para videojuegos.

© Copyright 2024. Todos los derechos reservados