Curso Avanzado de Creación de RPGs con Unity

Programación de Movimiento Aleatorio para Enemigos en Juegos de Rol

Curso Avanzado de Creación de RPGs con Unity

Contenido del curso

Personaje principal: Movimiento y Animaciones

Enemigos Avanzados

Personaje principal avanzado

Programación de Movimiento Aleatorio para Enemigos en Juegos de Rol

Resumen

La mejor forma de dar vida a un enemigo en Unity 2D es con una IA simple de movimiento aleatorio. Aquí verás cómo estructurar un EnemyController en C#, conectar Rigidbody2D y Animator, y controlar tiempos de paso con precisión. Todo está preparado para reutilizarlo en distintos tipos de enemigos sin cambiar el script, solo los gráficos.

¿Cómo crear e integrar el enemy controller?

Para empezar, se crea un script de C# llamado EnemyController y se arrastra al objeto enemigo en la jerarquía. Aunque se llame controller, no lo controlará el jugador: gobierna el movimiento autónomo del enemigo.

¿Qué requisitos debe tener el enemigo?

  • Tag "enemy" aplicada al objeto enemigo.
  • Sistema de animaciones con blend tree y parámetros "horizontal" y "vertical".
  • Collider configurado.
  • Rigidbody2D agregado.

¿Cómo resolver errores del editor?

  • Si Visual Studio marca todo en rojo sin motivo: guardar, cerrar y reabrir el script desde Unity.

¿Qué variables controlan la IA y el movimiento aleatorio?

La lógica se basa en dos temporizadores: tiempo entre pasos y tiempo de cada paso. Además, se registra si el enemigo está en movimiento y en qué dirección debe desplazarse. Se comunican los ejes al animator mediante constantes.

using UnityEngine; public class EnemyController : MonoBehaviour { public float enemySpeed = 1f; // Velocidad del enemigo. private Rigidbody2D enemyRigidBody; // Referencia al Rigidbody2D. private bool isMoving; // Estado de movimiento. public float timeBetweenSteps = 2f; // Tiempo en reposo entre pasos. private float timeBetweenStepsCounter; // Contador interno de reposo. public float timeToMakeStep = 1.5f; // Duración de cada paso. private float timeToMakeStepCounter; // Contador interno de paso. public Vector2 directionToMakeStep; // Dirección del movimiento. private Animator enemyAnimator; // Referencia al Animator. private const string Horizontal = "horizontal"; // Parámetro del blend tree. private const string Vertical = "vertical"; // Parámetro del blend tree. void Start() { enemyRigidBody = GetComponent<Rigidbody2D>(); enemyAnimator = GetComponent<Animator>(); // Inicialización de contadores con los valores públicos. timeBetweenStepsCounter = timeBetweenSteps; timeToMakeStepCounter = timeToMakeStep; }
  • enemySpeed: controla la rapidez del desplazamiento.
  • isMoving: indica si se está moviendo o está quieto.
  • timeBetweenSteps/timeBetweenStepsCounter: reposo y su contador interno.
  • timeToMakeStep/timeToMakeStepCounter: duración del paso y su contador interno.
  • directionToMakeStep: vector de dirección aleatoria.
  • enemyRigidBody y enemyAnimator: componentes para movimiento y animación.
  • "horizontal" y "vertical": nombres exactos de los parámetros del animator.

¿Cómo se programa la lógica en update para moverse y parar?

La mecánica alterna entre reposo y movimiento. En movimiento, se consume el contador del paso y se aplica velocidad en la dirección elegida. En reposo, se espera hasta agotar el contador y se decide una nueva dirección aleatoria.

void Update() { if (isMoving) { // Mientras se mueve. timeToMakeStepCounter -= Time.deltaTime; enemyRigidBody.velocity = directionToMakeStep * enemySpeed; if (timeToMakeStepCounter < 0f) { // Fin del paso: parar y preparar reposo. isMoving = false; timeBetweenStepsCounter = timeBetweenSteps; // Ojo con asignar el correcto. enemyRigidBody.velocity = Vector2.zero; } } else { // Mientras está quieto. timeBetweenStepsCounter -= Time.deltaTime; if (timeBetweenStepsCounter < 0f) { // Toca iniciar un nuevo paso. isMoving = true; timeToMakeStepCounter = timeToMakeStep; // Dirección aleatoria en X e Y entre -1 y 1. directionToMakeStep = new Vector2( Random.Range(-1f, 1f), Random.Range(-1f, 1f) ); } } // Notificar la dirección al Animator para el blend tree. enemyAnimator.SetFloat(Horizontal, directionToMakeStep.x); enemyAnimator.SetFloat(Vertical, directionToMakeStep.y); } }

¿Qué ocurre mientras se mueve?

  • Se decrementa el contador del paso con Time.deltaTime.
  • Se aplica velocity al rigidbody según la dirección y la velocidad.
  • Al agotar el tiempo del paso: se detiene, se pone Vector2.zero y se arma el reposo.

¿Qué ocurre mientras está quieto?

  • Se decrementa el contador de reposo con Time.deltaTime.
  • Al agotarse: se activa el movimiento, se reinicia el contador del paso y se elige una dirección aleatoria con Random.Range(-1, 1) en X e Y (puede salir diagonal).

¿Cómo sincronizar animaciones con el movimiento?

  • Se actualizan los parámetros "horizontal" y "vertical" mediante SetFloat.

  • El blend tree interpreta estos valores para girar la animación según la dirección.

  • Valores de ejemplo comprobados: enemySpeed = 1. Tiempo entre pasos = 2 s. Tiempo de paso = 1.5 s.

  • Consejo práctico: hay mucho código dentro de Update; avanza con cuidado para no mezclar contadores.

  • Próximo paso: crea un prefab con varios enemigos en pantalla y valida el comportamiento.

¿Tienes dudas o ideas para ampliar esta IA? Comparte tu pregunta y cuéntame cómo la adaptarías a tus enemigos.