Creando un Sistema de Movimiento y Combate Básico con Animaciones en Unity para Juegos Top-Down ⚔️
Este tutorial te guiará paso a paso en la creación de un sistema de movimiento de personaje en 8 direcciones y un mecanismo de combate elemental para juegos con vista cenital en Unity. Cubriremos la configuración de animaciones, detección de colisiones de ataque y gestión de estados básicos. Ideal para principiantes que buscan construir la base de un RPG o juego de acción top-down.
🎮 Introducción al Movimiento y Combate Top-Down
Bienvenido a este tutorial donde construiremos los cimientos de un juego top-down en Unity. Nos centraremos en dos aspectos cruciales: el movimiento del personaje y un sistema de combate rudimentario pero funcional, ambos sincronizados con animaciones para dar vida a nuestros héroes (o villanos).
Los juegos top-down, como los clásicos Zelda o los modernos indies de acción, a menudo requieren una forma de moverse en 8 direcciones y una mecánica de ataque que se sienta responsiva y visualmente atractiva. Aquí, desglosaremos el proceso para que puedas implementarlo fácilmente en tus propios proyectos.
¿Qué aprenderás en este tutorial? 🎯
- Configurar un personaje y su controlador de movimiento.
- Implementar un sistema de detección de entrada para 8 direcciones.
- Gestionar estados de animación (Idle, Walking, Attacking).
- Crear un sistema de ataque básico con hitboxes.
- Sincronizar animaciones con la lógica de juego.
🛠️ Preparación del Proyecto y Recursos
Antes de sumergirnos en el código, necesitamos configurar nuestro entorno de Unity. Asegúrate de tener Unity Hub y una versión reciente de Unity instalada (preferiblemente 2021.3 LTS o superior).
1. Creación de un Nuevo Proyecto
- Abre Unity Hub y crea un nuevo proyecto 2D. Nómbralo, por ejemplo,
TopDownCombatTutorial. - Una vez abierto el proyecto, organiza tu carpeta
Assets. Es una buena práctica tener carpetas comoSprites,Animations,Scripts,Prefabs, etc.
2. Importación de Recursos Gráficos 🖼️
Para este tutorial, necesitaremos algunos sprites para nuestro personaje y sus animaciones. Puedes usar los tuyos propios o buscar sets gratuitos en itch.io o la Asset Store de Unity. Necesitarás al menos:
- Sprites para
Idle(quieto) en 8 direcciones (o al menos 4 y flipear). - Sprites para
Walking(caminando) en 8 direcciones. - Sprites para
Attacking(atacando) en 8 direcciones.
Una vez importados, selecciona tus sprites en la ventana del Project y configura el Sprite Mode a Multiple en el Inspector. Luego, haz clic en Sprite Editor, córtalos (slice) si es necesario (Grid by Cell Size o Automatic), y aplica los cambios. Asegúrate de que el Pixels Per Unit sea consistente (por ejemplo, 16 o 32) y que el Filter Mode sea Point (no filter) para un estilo pixel art, y Compression esté en None.
🚶♂️ Configuración del Personaje y Movimiento
Ahora vamos a crear nuestro personaje y a darle la capacidad de moverse por el escenario.
1. Creación del GameObject del Jugador
- Arrastra uno de tus sprites
Idle(por ejemplo,Idle_Down) a la jerarquía para crear un nuevoGameObject. Renómbralo aPlayer. - Añade un componente
Rigidbody2DaPlayer. ConfiguraBody TypeaKinematicsi vas a manejar el movimiento manualmente a través de transformaciones oDynamicsi prefieres fuerzas, pero conGravity Scalea0. Para este tutorial, usaremosKinematicy lo moveremos directamente. - Añade un
CapsuleCollider2DoBoxCollider2DalPlayery ajústalo para que se adapte al tamaño de tu sprite. MarcaIs Triggersi no quieres que el collider físico impida el movimiento sino que solo detecte colisiones.
2. El Controlador de Animaciones (Animator Controller) ✨
Las animaciones son clave para que nuestro personaje se sienta vivo.
- Crea una nueva carpeta
Animationsdentro deAssets. - Selecciona el
GameObject Player. En la ventanaAnimation(si no la tienes, ve aWindow > Animation > Animation), haz clic enCreate. Guarda el primer clip comoPlayer_Idle_Downdentro de tu carpetaAnimations. - Arrastra los sprites de tu animación
Idle_Downal panel deAnimation. - Repite el proceso para
Player_Walk_Down,Player_Idle_Up,Player_Walk_Up, etc. Crea animaciones para todas las direcciones (al menos 4: Up, Down, Left, Right) y estados (Idle, Walk). - Unity habrá creado automáticamente un
Animator Controllerpara tuPlayer. Haz doble clic en él para abrir la ventanaAnimator. - En el
Animator, crea parámetros para controlar los estados. Necesitaremos:Speed(Float): Para saber si el personaje se está moviendo.Horizontal(Float): La dirección horizontal del movimiento.Vertical(Float): La dirección vertical del movimiento.IsAttacking(Bool): Para activar la animación de ataque.
Configuración del Blend Tree para 8 Direcciones
Dentro del Animator Controller, haz clic derecho y selecciona Create State > From New Blend Tree. Renómbralo a Blend_Idle_Walk.
- Haz doble clic en el
Blend Tree. En elInspector, configuraBlend Typea2D Freeform Directional. - Configura
ParametersaHorizontalyVertical. - Añade
Motion Fieldspara cada una de tus animaciones deIdleyWalk:Player_Idle_Up: Posición (0, 1)Player_Idle_Down: Posición (0, -1)Player_Idle_Left: Posición (-1, 0)Player_Idle_Right: Posición (1, 0)Player_Walk_Up: Posición (0, 1)Player_Walk_Down: Posición (0, -1)Player_Walk_Left: Posición (-1, 0)Player_Walk_Right: Posición (1, 0)- (Opcional) Puedes añadir las diagonales también si tienes sprites específicos, por ejemplo,
Player_Walk_UpLeften (-0.7, 0.7).
Esto permitirá que Unity mezcle las animaciones según las entradas Horizontal y Vertical.
3. Script PlayerMovement.cs 📝
Crea una nueva carpeta Scripts y dentro un script C# llamado PlayerMovement. Este script manejará la entrada del jugador y el movimiento del Rigidbody2D.
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float moveSpeed = 5f;
private Rigidbody2D rb;
private Vector2 movement;
private Animator animator;
void Start()
{
rb = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
}
void Update()
{
// Input del movimiento
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
movement.Normalize(); // Normaliza para evitar movimiento más rápido en diagonales
// Actualiza parámetros del Animator
animator.SetFloat("Horizontal", movement.x);
animator.SetFloat("Vertical", movement.y);
animator.SetFloat("Speed", movement.sqrMagnitude); // sqrMagnitude es más eficiente que magnitude
// Detección de ataque
if (Input.GetMouseButtonDown(0))
{
animator.SetTrigger("Attack"); // Usaremos un Trigger para el ataque
}
}
void FixedUpdate()
{
// Movimiento del Rigidbody2D
rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
}
}
Arrastra este script al GameObject Player en la jerarquía. Ajusta Move Speed a tu gusto.
⚔️ Implementación del Sistema de Combate Básico
Ahora que nuestro personaje se mueve, vamos a añadir una mecánica de ataque simple.
1. Animaciones de Ataque y Eventos 🗡️
Crea animaciones de ataque para tu personaje. Al igual que con Idle y Walk, necesitarás animaciones de Attack para al menos 4 (o 8) direcciones. Nómbralas, por ejemplo, Player_Attack_Down.
En el Animator:
- Crea un nuevo estado para cada animación de ataque (e.g.,
Attack_Down). - Crea un parámetro
TriggerllamadoAttack. - Crea transiciones desde
Blend_Idle_WalkaAttack_Down(y las otras direcciones de ataque) cuando el triggerAttackse activa. DesmarcaHas Exit Timepara una respuesta rápida. - Desde cada estado de ataque, crea una transición de vuelta a
Blend_Idle_Walk. MarcaHas Exit Timepara que la animación de ataque se complete antes de volver aIdle/Walk.
2. Creando el Hitbox de Ataque 💥
El hitbox es el área que detectará si nuestro ataque golpea algo.
- Crea un nuevo
Empty GameObjectcomo hijo delPlayer. NómbraloAttackHitbox. - Añade un
BoxCollider2DaAttackHitbox. MarcaIs Trigger. - Ajusta el tamaño y la posición del
BoxCollider2Dpara que represente el área de ataque de tu personaje. Desactiva elGameObject AttackHitboxpor ahora. - Crea otro
Empty GameObjectcomo hijo delPlayer. NómbraloAttackPoint. Este será el punto de origen de tu hitbox, y lo moveremos para que la hitbox aparezca en la dirección correcta.
3. Script PlayerCombat.cs 🔪
Crea un nuevo script C# llamado PlayerCombat.
using UnityEngine;
public class PlayerCombat : MonoBehaviour
{
public Transform attackPoint;
public GameObject attackHitboxPrefab; // Un prefab del hitbox que creamos
public float attackRange = 0.5f;
public LayerMask enemyLayers; // Capas que consideramos 'enemigos'
public float attackRate = 2f; // Ataques por segundo
private float nextAttackTime = 0f;
private Animator animator;
private PlayerMovement playerMovement; // Para obtener la dirección de ataque
void Start()
{
animator = GetComponent<Animator>();
playerMovement = GetComponent<PlayerMovement>();
}
void Update()
{
if (Time.time >= nextAttackTime)
{
if (Input.GetMouseButtonDown(0))
{
Attack();
nextAttackTime = Time.time + 1f / attackRate;
}
}
}
void Attack()
{
// Activa la animación de ataque
animator.SetTrigger("Attack");
// Determinar la dirección de ataque y ajustar AttackPoint
Vector2 currentMovement = new Vector2(animator.GetFloat("Horizontal"), animator.GetFloat("Vertical"));
if (currentMovement.sqrMagnitude == 0) // Si está quieto, usa la última dirección conocida o por defecto
{
// Podrías guardar la última dirección de movimiento válida en PlayerMovement
// Por ahora, asumimos que 'Down' es la dirección por defecto si está quieto.
attackPoint.localPosition = new Vector3(0f, -0.5f, 0f); // Ejemplo para abajo
} else {
// Esto es solo un ejemplo, lo ideal es tener un AttackPoint para cada dirección
// o rotar dinámicamente el AttackHitbox
if (Mathf.Abs(currentMovement.x) > Mathf.Abs(currentMovement.y)) {
// Horizontal
if (currentMovement.x > 0) attackPoint.localPosition = new Vector3(0.5f, 0f, 0f);
else attackPoint.localPosition = new Vector3(-0.5f, 0f, 0f);
} else {
// Vertical
if (currentMovement.y > 0) attackPoint.localPosition = new Vector3(0f, 0.5f, 0f);
else attackPoint.localPosition = new Vector3(0f, -0.5f, 0f);
}
}
// Detección de enemigos en el rango de ataque
Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(attackPoint.position, attackRange, enemyLayers);
foreach (Collider2D enemy in hitEnemies)
{
// Lógica de daño o interacción con el enemigo
Debug.Log("¡Golpeamos a " + enemy.name + "!");
// Ejemplo: enemy.GetComponent<EnemyHealth>().TakeDamage(10);
}
}
// Función auxiliar para dibujar el rango de ataque en el editor
void OnDrawGizmosSelected()
{
if (attackPoint == null)
return;
Gizmos.DrawWireSphere(attackPoint.position, attackRange);
}
}
Explicación y Configuración:
attackPoint: Arrastra elGameObject AttackPointque creaste en el paso anterior a esta ranura en el Inspector del scriptPlayerCombat.attackHitboxPrefab: En lugar de un prefab, simplificaremos y moveremos elGameObject AttackHitboxdirectamente. Elimina la líneapublic GameObject attackHitboxPrefab;y la lógica relacionada. En su lugar, obtén una referencia aAttackHitboxenStart()y actívalo/desactívalo durante la animación de ataque.- Modifica
PlayerCombat.cspara obtener una referencia aAttackHitbox:
- Modifica
// ... dentro de PlayerCombat.cs
public GameObject attackHitbox;
// ...
void Start()
{
animator = GetComponent<Animator>();
playerMovement = GetComponent<PlayerMovement>();
attackHitbox.SetActive(false); // Asegúrate de que esté desactivado al inicio
}
// ...
* Arrastra el `GameObject AttackHitbox` al slot `Attack Hitbox` en el Inspector de `PlayerCombat`.
3. attackRange: Ajusta este valor para controlar cuán lejos llega tu ataque. Se usa con OverlapCircleAll.
4. enemyLayers: Crea una nueva Layer en Unity (por ejemplo, Enemy). Asigna esta capa a tus GameObjects enemigos. Luego, en el Inspector de PlayerCombat, selecciona la capa Enemy para que tu ataque solo detecte objetos en esa capa.
5. attackRate: Controla la velocidad a la que el jugador puede atacar.
4. Sincronizando Hitbox con Animación (Animation Events) ⏰
Queremos que el hitbox solo esté activo durante una parte específica de la animación de ataque.
- Selecciona una de tus animaciones de ataque (ej.
Player_Attack_Down) en la ventanaProject. Haz doble clic para abrirla en la ventanaAnimation. - En la línea de tiempo de la animación, navega al punto donde el arma/puño realmente golpearía al enemigo.
- Haz clic en el botón
Add Event(el pequeño símbolo de diamante) en la línea de tiempo. - En la ventana de eventos, haz clic en el botón
Functiony seleccionaAddAnimationEvent. - Abre tu script
PlayerCombat.csy añade dos funciones públicas:
// ... dentro de PlayerCombat.cs
public void EnableAttackHitbox()
{
attackHitbox.SetActive(true);
}
public void DisableAttackHitbox()
{
attackHitbox.SetActive(false);
}
// ...
- Ahora, vuelve a la ventana
Animationpara tuPlayer_Attack_Down.- En el evento que creaste al inicio del golpe, selecciona
EnableAttackHitbox(). - Crea otro evento más adelante en la animación (justo después del golpe) y selecciona
DisableAttackHitbox().
- En el evento que creaste al inicio del golpe, selecciona
- Repite este proceso para todas tus animaciones de ataque.
🔄 Refinamientos y Consideraciones Adicionales
Este es un sistema básico, pero hay mucho que puedes mejorar y expandir.
1. Estados de Juego y Combate
Considera un sistema de estados (State Machine) para tu jugador. Por ejemplo:
| Estado de Jugador | Descripción | Acciones Posibles |
|---|---|---|
| --- | --- | --- |
| Idle | Personaje quieto | Mover, Atacar |
| Walking | Personaje moviéndose | Mover, Atacar |
| --- | --- | --- |
| Attacking | Personaje atacando | Bloquea movimiento, no puede atacar de nuevo hasta terminar |
| Hurt | Recibiendo daño | No puede moverse/atacar, tiene invulnerabilidad temporal |
Esto te permitirá controlar mejor qué acciones son posibles en cada momento. Por ejemplo, no quieres que el jugador pueda atacar mientras ya está en medio de un ataque, o moverse si está recibiendo daño.
Ejemplo de Lógica de Bloqueo de Ataque
Podrías añadir una bandera isAttacking en PlayerCombat:
// ... dentro de PlayerCombat.cs
private bool isAttacking = false;
void Attack()
{
if (isAttacking) return; // No permitir múltiples ataques simultáneos
isAttacking = true;
animator.SetTrigger("Attack");
// ... lógica de ataque ...
// Después de que la animación termine (usando Animation Events)
// Añade un evento al final de la animación de ataque:
// public void OnAttackAnimationEnd() { isAttacking = false; }
}
2. Feedback Visual y de Audio
- Efectos visuales: Partículas al golpear un enemigo, rastro del arma, etc.
- Sonido: Reproduce clips de audio para pasos, ataque, golpe, etc.
3. Detección de Colisiones Más Avanzada
Para ataques con áreas complejas o duración, OverlapAreaAll o CapsuleOverlapAll pueden ser útiles. También puedes usar raycasts en la dirección del ataque para mayor precisión.
4. Pooling de Objetos para Hitboxes
Si tu juego tiene muchos ataques o enemigos, crear y destruir GameObjects constantemente (como un AttackHitbox si lo instanciaras) puede afectar el rendimiento. Considera usar Object Pooling para reutilizar los hitboxes.
✅ Conclusión
¡Felicidades! Has construido la base de un sistema de movimiento en 8 direcciones y combate básico para tus juegos top-down en Unity. Has aprendido a integrar el input del jugador con un Rigidbody2D, a controlar animaciones con Animator Controller y Blend Trees, y a implementar un mecanismo de ataque simple con OverlapCircleAll y Animation Events.
Este es solo el comienzo. Desde aquí, puedes expandir este sistema añadiendo más complejidad, como diferentes tipos de ataques, combos, habilidades especiales, un sistema de inventario y más. La clave es construir sobre estos fundamentos, iterar y probar constantemente. ¡Ahora sal y crea tu propia aventura épica!
Tutoriales relacionados
- Creando un Sistema de Diálogos Ramificados en Unity 🗣️ RPGs y Aventuras Narrativasintermediate25 min
- Creando un Sistema de Portales Dimensiones con Render Textures en Unity 🌌intermediate18 min
- Creando un Sistema de Inventario Modular en Unity para RPGs y Juegos de Aventuraintermediate25 min
- Creando un Sistema de Guardado y Carga Universal en Unity: Persistencia de Datos para tus Juegos 💾intermediate25 min
- Controlador de Personaje en Primera Persona: Construyendo desde Cero en Unity 🚶♂️ FPS Essentialsintermediate18 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!