Introducción a los Behavior Trees

Los Behavior Trees (árboles de comportamiento) son una técnica popular en la inteligencia artificial para videojuegos, especialmente útil para la toma de decisiones y la gestión de comportamientos complejos en NPCs (Non-Player Characters). A diferencia de las máquinas de estados finitos (FSM), los Behavior Trees ofrecen una estructura más modular y escalable, facilitando la creación y mantenimiento de comportamientos complejos.

Conceptos Clave

  1. Nodo: La unidad básica de un Behavior Tree. Puede ser una acción, una condición o un controlador de flujo.
  2. Raíz: El nodo inicial del árbol desde donde comienza la evaluación.
  3. Hojas: Los nodos terminales que realizan acciones o verifican condiciones.
  4. Controladores de Flujo: Nodos que controlan la ejecución de otros nodos, como secuencias, selectores y paralelos.

Estructura de un Behavior Tree

Un Behavior Tree se compone de varios tipos de nodos:

  • Acciones: Realizan tareas específicas, como mover a un NPC o atacar a un enemigo.
  • Condiciones: Verifican si se cumplen ciertos criterios, como si un enemigo está cerca.
  • Secuencias: Ejecutan sus hijos en orden hasta que uno falla.
  • Selectores: Ejecutan sus hijos en orden hasta que uno tiene éxito.
  • Paralelos: Ejecutan todos sus hijos simultáneamente.

Ejemplo de un Behavior Tree

Imaginemos un NPC guardia que patrulla una zona y ataca a los enemigos si los detecta. El Behavior Tree podría estructurarse de la siguiente manera:

Root
├── Selector
│   ├── Sequence
│   │   ├── Condition: EnemyDetected
│   │   └── Action: AttackEnemy
│   └── Sequence
│       ├── Action: Patrol
│       └── Action: Idle

En este ejemplo:

  • El Selector intenta ejecutar sus hijos en orden.
  • La primera Sequence verifica si hay un enemigo detectado y, si es así, ejecuta la acción de atacar.
  • Si no se detecta ningún enemigo, el Selector pasa a la siguiente Sequence, que hace que el guardia patrulle y luego se quede en estado de espera.

Implementación de Behavior Trees

Paso 1: Definir los Nodos

Primero, definimos las clases base para los nodos:

class Node:
    def run(self):
        raise NotImplementedError("This method should be overridden.")

class Action(Node):
    def __init__(self, action):
        self.action = action

    def run(self):
        return self.action()

class Condition(Node):
    def __init__(self, condition):
        self.condition = condition

    def run(self):
        return self.condition()

Paso 2: Definir los Controladores de Flujo

Luego, definimos los controladores de flujo como secuencias y selectores:

class Sequence(Node):
    def __init__(self, children):
        self.children = children

    def run(self):
        for child in self.children:
            if not child.run():
                return False
        return True

class Selector(Node):
    def __init__(self, children):
        self.children = children

    def run(self):
        for child in self.children:
            if child.run():
                return True
        return False

Paso 3: Crear el Behavior Tree

Finalmente, creamos el Behavior Tree para nuestro guardia:

def enemy_detected():
    # Lógica para detectar enemigos
    return True  # Simulación de detección de enemigo

def attack_enemy():
    print("Attacking enemy!")
    return True

def patrol():
    print("Patrolling area.")
    return True

def idle():
    print("Idling.")
    return True

# Crear nodos de acción y condición
enemy_detected_node = Condition(enemy_detected)
attack_enemy_node = Action(attack_enemy)
patrol_node = Action(patrol)
idle_node = Action(idle)

# Crear secuencias y selector
attack_sequence = Sequence([enemy_detected_node, attack_enemy_node])
patrol_sequence = Sequence([patrol_node, idle_node])
root = Selector([attack_sequence, patrol_sequence])

# Ejecutar el Behavior Tree
root.run()

Ejercicio Práctico

Ejercicio: Implementa un Behavior Tree para un NPC que recoge objetos y los lleva a una base. Si no hay objetos cerca, el NPC patrulla la zona.

Solución:

def object_nearby():
    # Lógica para detectar objetos cercanos
    return True  # Simulación de objeto cercano

def collect_object():
    print("Collecting object!")
    return True

def move_to_base():
    print("Moving to base.")
    return True

# Crear nodos de acción y condición
object_nearby_node = Condition(object_nearby)
collect_object_node = Action(collect_object)
move_to_base_node = Action(move_to_base)

# Crear secuencias y selector
collect_sequence = Sequence([object_nearby_node, collect_object_node, move_to_base_node])
patrol_sequence = Sequence([patrol_node, idle_node])
root = Selector([collect_sequence, patrol_sequence])

# Ejecutar el Behavior Tree
root.run()

Conclusión

Los Behavior Trees son una herramienta poderosa para gestionar comportamientos complejos en videojuegos. Su estructura modular y escalable permite crear NPCs con comportamientos realistas y adaptativos. En este módulo, hemos cubierto los conceptos básicos, la estructura y la implementación de Behavior Trees, proporcionando una base sólida para su aplicación en proyectos de videojuegos.

© Copyright 2024. Todos los derechos reservados