Programación de Movimiento Aleatorio para Enemigos en Juegos de Rol
Clase 21 de 60 • Curso Avanzado de Creación de RPGs con Unity
Contenido del curso
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.