tutoriales.com

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.

Intermedio15 min de lectura17 views
Reportar error

🎮 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

  1. Abre Unity Hub y crea un nuevo proyecto 2D. Nómbralo, por ejemplo, TopDownCombatTutorial.
  2. Una vez abierto el proyecto, organiza tu carpeta Assets. Es una buena práctica tener carpetas como Sprites, 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.
💡 Consejo: Si no tienes sprites para 8 direcciones, puedes empezar con 4 (arriba, abajo, izquierda, derecha) y usar el espejo (flip) para las direcciones diagonales o para las laterales opuestas.

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

  1. Arrastra uno de tus sprites Idle (por ejemplo, Idle_Down) a la jerarquía para crear un nuevo GameObject. Renómbralo a Player.
  2. Añade un componente Rigidbody2D a Player. Configura Body Type a Kinematic si vas a manejar el movimiento manualmente a través de transformaciones o Dynamic si prefieres fuerzas, pero con Gravity Scale a 0. Para este tutorial, usaremos Kinematic y lo moveremos directamente.
  3. Añade un CapsuleCollider2D o BoxCollider2D al Player y ajústalo para que se adapte al tamaño de tu sprite. Marca Is Trigger si 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.

  1. Crea una nueva carpeta Animations dentro de Assets.
  2. Selecciona el GameObject Player. En la ventana Animation (si no la tienes, ve a Window > Animation > Animation), haz clic en Create. Guarda el primer clip como Player_Idle_Down dentro de tu carpeta Animations.
  3. Arrastra los sprites de tu animación Idle_Down al panel de Animation.
  4. 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).
  5. Unity habrá creado automáticamente un Animator Controller para tu Player. Haz doble clic en él para abrir la ventana Animator.
  6. 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.
Idle Blend Tree 2D (8 Dir) H, V Parameters Walk Blend Tree 2D (8 Dir) H, V Parameters Attack Speed > 0.01 Speed < 0.01 IsAttacking == true IsAttacking == true On Finish
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.

  1. Haz doble clic en el Blend Tree. En el Inspector, configura Blend Type a 2D Freeform Directional.
  2. Configura Parameters a Horizontal y Vertical.
  3. Añade Motion Fields para cada una de tus animaciones de Idle y Walk:
    • 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_UpLeft en (-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:

  1. Crea un nuevo estado para cada animación de ataque (e.g., Attack_Down).
  2. Crea un parámetro Trigger llamado Attack.
  3. Crea transiciones desde Blend_Idle_Walk a Attack_Down (y las otras direcciones de ataque) cuando el trigger Attack se activa. Desmarca Has Exit Time para una respuesta rápida.
  4. Desde cada estado de ataque, crea una transición de vuelta a Blend_Idle_Walk. Marca Has Exit Time para que la animación de ataque se complete antes de volver a Idle/Walk.
🔥 Importante: Para que las animaciones de ataque dirijan correctamente, puedes usar el mismo `Horizontal` y `Vertical` para decidir a qué animación de ataque transicionar. En el Blend Tree de ataque, pon las animaciones de ataque según su dirección.

2. Creando el Hitbox de Ataque 💥

El hitbox es el área que detectará si nuestro ataque golpea algo.

  1. Crea un nuevo Empty GameObject como hijo del Player. Nómbralo AttackHitbox.
  2. Añade un BoxCollider2D a AttackHitbox. Marca Is Trigger.
  3. Ajusta el tamaño y la posición del BoxCollider2D para que represente el área de ataque de tu personaje. Desactiva el GameObject AttackHitbox por ahora.
  4. Crea otro Empty GameObject como hijo del Player. Nómbralo AttackPoint. 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:

  1. attackPoint: Arrastra el GameObject AttackPoint que creaste en el paso anterior a esta ranura en el Inspector del script PlayerCombat.
  2. attackHitboxPrefab: En lugar de un prefab, simplificaremos y moveremos el GameObject AttackHitbox directamente. Elimina la línea public GameObject attackHitboxPrefab; y la lógica relacionada. En su lugar, obtén una referencia a AttackHitbox en Start() y actívalo/desactívalo durante la animación de ataque.
    • Modifica PlayerCombat.cs para obtener una referencia a AttackHitbox:
// ... 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.

📌 Nota: La lógica de `AttackPoint` para determinar la dirección es básica. Para un sistema robusto, podrías tener varios `AttackPoint` (uno para cada dirección) y activarlos según `Horizontal` y `Vertical`, o rotar el `AttackHitbox` alrededor del jugador.

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.

  1. Selecciona una de tus animaciones de ataque (ej. Player_Attack_Down) en la ventana Project. Haz doble clic para abrirla en la ventana Animation.
  2. En la línea de tiempo de la animación, navega al punto donde el arma/puño realmente golpearía al enemigo.
  3. Haz clic en el botón Add Event (el pequeño símbolo de diamante) en la línea de tiempo.
  4. En la ventana de eventos, haz clic en el botón Function y selecciona AddAnimationEvent.
  5. Abre tu script PlayerCombat.cs y añade dos funciones públicas:
// ... dentro de PlayerCombat.cs
public void EnableAttackHitbox()
{
attackHitbox.SetActive(true);
}

public void DisableAttackHitbox()
{
attackHitbox.SetActive(false);
}
// ...
  1. Ahora, vuelve a la ventana Animation para tu Player_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().
  2. Repite este proceso para todas tus animaciones de ataque.
⚠️ Advertencia: Asegúrate de que las funciones de eventos (`EnableAttackHitbox`, `DisableAttackHitbox`) estén en un script que esté **en el mismo GameObject** que el Animator Controller, o en un GameObject hijo con el que el Animator Controller sepa interactuar. En este caso, `PlayerCombat` está en `Player`, que es donde está el `Animator`, así que funciona.

🔄 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 JugadorDescripciónAcciones Posibles
---------
IdlePersonaje quietoMover, Atacar
WalkingPersonaje moviéndoseMover, Atacar
---------
AttackingPersonaje atacandoBloquea movimiento, no puede atacar de nuevo hasta terminar
HurtRecibiendo dañoNo 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.
💡 Consejo: Usa un `AudioSource` en el `Player` y llama a `audioSource.PlayOneShot(clip)` desde tus scripts para sonidos de un solo disparo como ataques.

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.

Inicio ¿Click de Ataque? No ¿Puede atacar? No Activar Animación de Ataque Evento 'Golpe': Activar Hitbox Detectar enemigos Aplicar daño Evento 'Fin': Desactivar Hitbox Esperar cooldown Fin
100% Completo

✅ 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

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!