En este tema aprenderás cómo dotar a los enemigos de tu juego con comportamientos inteligentes y dinámicos utilizando Phaser. Cubriremos desde patrones simples de movimiento hasta la implementación de lógica básica de IA (Inteligencia Artificial) para que los enemigos reaccionen al jugador y al entorno.


  1. ¿Qué es la IA en Juegos?

La Inteligencia Artificial en juegos se refiere a la lógica que permite a los personajes controlados por el ordenador (NPCs o enemigos) tomar decisiones y actuar de manera autónoma. Los comportamientos pueden ser simples (patrullar, seguir al jugador) o complejos (planificar rutas, cooperar entre enemigos).

Conceptos clave:

  • Patrullaje: Movimiento repetitivo entre puntos.
  • Persecución: Seguir al jugador cuando está cerca.
  • Evasión: Alejarse del jugador o de peligros.
  • Ataque: Realizar acciones ofensivas bajo ciertas condiciones.
  • Estados: Cambiar de comportamiento según la situación (por ejemplo, patrullar → perseguir → atacar).

  1. Estructura Básica de un Enemigo con IA

En Phaser, los enemigos suelen ser Sprites con física habilitada y lógica personalizada en el bucle de actualización (update). La IA se implementa como una serie de condiciones y acciones dentro de este método.

Estructura típica:

class Enemy extends Phaser.Physics.Arcade.Sprite {
  constructor(scene, x, y, texture) {
    super(scene, x, y, texture);
    scene.add.existing(this);
    scene.physics.add.existing(this);

    // Estado inicial
    this.state = 'patrolling';
    this.speed = 100;
    this.patrolPoints = [x, x + 200];
    this.currentPatrolIndex = 0;
  }

  update(player) {
    switch (this.state) {
      case 'patrolling':
        this.patrol();
        if (this.canSeePlayer(player)) {
          this.state = 'chasing';
        }
        break;
      case 'chasing':
        this.chase(player);
        if (!this.canSeePlayer(player)) {
          this.state = 'patrolling';
        }
        break;
    }
  }

  patrol() {
    // Lógica de patrullaje
  }

  chase(player) {
    // Lógica de persecución
  }

  canSeePlayer(player) {
    // Lógica para detectar al jugador
  }
}

  1. Ejemplo Práctico: Patrullaje y Persecución

3.1. Patrullaje

El enemigo se mueve entre dos puntos. Cuando llega a un extremo, se da la vuelta.

patrol() {
  const targetX = this.patrolPoints[this.currentPatrolIndex];
  if (Math.abs(this.x - targetX) < 5) {
    // Cambia de dirección
    this.currentPatrolIndex = 1 - this.currentPatrolIndex;
  }
  this.setVelocityX(targetX > this.x ? this.speed : -this.speed);
}

Explicación:

  • patrolPoints define los extremos del patrullaje.
  • El enemigo se mueve hacia el punto objetivo.
  • Al llegar, invierte la dirección.

3.2. Persecución

El enemigo sigue al jugador si está dentro de un rango de visión.

chase(player) {
  if (player.x > this.x) {
    this.setVelocityX(this.speed);
  } else {
    this.setVelocityX(-this.speed);
  }
}

Explicación:

  • Compara la posición del jugador con la del enemigo.
  • Ajusta la velocidad para moverse hacia el jugador.

3.3. Detección del Jugador

canSeePlayer(player) {
  const distance = Phaser.Math.Distance.Between(this.x, this.y, player.x, player.y);
  return distance < 150; // Rango de visión
}

Explicación:

  • Calcula la distancia entre el enemigo y el jugador.
  • Si está dentro del rango, retorna true.

  1. Tabla Comparativa: Patrones de Comportamiento

Patrón Descripción Dificultad de Implementación
Patrullaje Movimiento entre puntos fijos Baja
Persecución Seguir al jugador si está cerca Media
Evasión Alejarse del jugador o de peligros Media
Ataque Realizar acciones ofensivas Media/Alta
Estados Cambiar entre varios comportamientos Media/Alta

  1. Ejercicio Práctico

Enunciado

Crea un enemigo que patrulle entre dos puntos. Si el jugador se acerca a menos de 100 píxeles, el enemigo lo perseguirá. Si el jugador se aleja, el enemigo volverá a patrullar.

Código Base

class Enemy extends Phaser.Physics.Arcade.Sprite {
  constructor(scene, x, y, texture) {
    super(scene, x, y, texture);
    scene.add.existing(this);
    scene.physics.add.existing(this);

    this.state = 'patrolling';
    this.speed = 80;
    this.patrolPoints = [x, x + 150];
    this.currentPatrolIndex = 0;
  }

  update(player) {
    switch (this.state) {
      case 'patrolling':
        this.patrol();
        if (this.canSeePlayer(player)) {
          this.state = 'chasing';
        }
        break;
      case 'chasing':
        this.chase(player);
        if (!this.canSeePlayer(player)) {
          this.state = 'patrolling';
        }
        break;
    }
  }

  patrol() {
    const targetX = this.patrolPoints[this.currentPatrolIndex];
    if (Math.abs(this.x - targetX) < 5) {
      this.currentPatrolIndex = 1 - this.currentPatrolIndex;
    }
    this.setVelocityX(targetX > this.x ? this.speed : -this.speed);
  }

  chase(player) {
    this.setVelocityX(player.x > this.x ? this.speed : -this.speed);
  }

  canSeePlayer(player) {
    return Phaser.Math.Distance.Between(this.x, this.y, player.x, player.y) < 100;
  }
}

Solución y Explicación

  • El enemigo alterna entre patrullar y perseguir según la distancia al jugador.
  • El método update se llama en cada frame y decide el comportamiento.
  • patrol y chase controlan el movimiento.
  • canSeePlayer determina si el jugador está lo suficientemente cerca.

Errores Comunes y Consejos

  • Olvidar llamar a update del enemigo en la escena: Asegúrate de llamar a enemy.update(player) en el método update de tu escena.
  • No habilitar la física: Usa scene.physics.add.existing(this) en el constructor.
  • No ajustar la velocidad a cero cuando el enemigo está quieto: Si el enemigo debe detenerse, usa this.setVelocityX(0).

  1. Ampliando la IA: Estados y Transiciones

Puedes agregar más estados, como "atacando" o "huyendo", y transiciones más complejas. Por ejemplo:

update(player) {
  switch (this.state) {
    case 'patrolling':
      this.patrol();
      if (this.canSeePlayer(player)) {
        this.state = 'chasing';
      }
      break;
    case 'chasing':
      this.chase(player);
      if (this.isCloseToPlayer(player)) {
        this.state = 'attacking';
      } else if (!this.canSeePlayer(player)) {
        this.state = 'patrolling';
      }
      break;
    case 'attacking':
      this.attack(player);
      if (!this.isCloseToPlayer(player)) {
        this.state = 'chasing';
      }
      break;
  }
}

  1. Resumen

  • La IA en Phaser se basa en lógica condicional dentro del método update de los enemigos.
  • Los patrones básicos incluyen patrullaje, persecución y ataque.
  • Utiliza estados para organizar y escalar la complejidad de los comportamientos.
  • Practica implementando y combinando estos patrones para crear enemigos más interesantes y desafiantes.

¡Ahora estás listo para dotar a tus enemigos de comportamientos inteligentes y dinámicos! En el siguiente tema aprenderás a trabajar con temporizadores y eventos retardados para enriquecer aún más la jugabilidad.

Phaser - Desarrollo de Juegos con JavaScript

Módulo 1: Introducción al Desarrollo de Juegos y Phaser

Módulo 2: Fundamentos de Phaser

Módulo 3: Sprites y Animación

Módulo 4: Física e Interactividad en el Juego

Módulo 5: Mundo del Juego y Cámara

Módulo 6: Audio e Interfaz de Usuario

Módulo 7: Arquitectura del Juego y Gestión de Estados

Módulo 8: Características Avanzadas de Jugabilidad

Módulo 9: Despliegue y Optimización

Módulo 10: Proyecto Final

© Copyright 2024. Todos los derechos reservados