🌟 Crear un videojuego de plataformas con Unity 🎮 4 – Programando el movimiento del personaje

Después de las últimas entradas sobre diseño de videojuegos, ya toca ponerse un poquito en materia de programación, vamos a crear el movimiento de nuestro personaje en un videojuego de plataformas con Unity.

Antes de comenzar con el código, vamos a definir brevemente que puede hacer nuestro personaje y crearemos un script para más adelante añadirlo como componente.



Para nuestro curso de un videojuego de plataformas, queremos que nuestro personaje pueda moverse en horizontal, saltar y agacharse. Para que nuestro personaje se mueva y realice estas acciones vamos a usar las físicas y el sistema de colisiones de Unity, por lo que el primer paso será añadir nuestro personaje a la escena de juego y añadirle los siguientes componentes: BoxCollider2D y Rigidbody2D.

BoxCollider2D

BoxCollider2D es un componente en Unity que se utiliza para detectar colisiones entre objetos en un entorno 2D. Se utiliza en combinación con Rigidbody2D para proporcionar física básica y detección de colisiones en juegos y aplicaciones en 2D.

El BoxCollider2D se puede adjuntar a un objeto en la escena y define un área rectangular que representa la forma y el tamaño del objeto en términos de colisiones. Cuando dos objetos con BoxCollider2D se superponen en el juego, se generará una colisión y se activará un evento de colisión que puedes utilizar para realizar acciones específicas.

El componente BoxCollider2D tiene varias propiedades ajustables, como el tamaño, el offset y la rotación, que te permiten personalizar la forma del colisionador. Puedes ajustar estas propiedades para que coincidan con la apariencia visual de tu objeto en el juego y garantizar que las colisiones se detecten correctamente.

Rigidbody2D

Rigidbody2D es un componente en Unity que se utiliza para simular la física de objetos en un entorno 2D. Proporciona un conjunto de propiedades y métodos que permiten aplicar fuerzas, simular gravedad, detectar colisiones y controlar el movimiento de los objetos de manera realista.

Al adjuntar el componente Rigidbody2D a un objeto en la escena, ese objeto se convierte en un objeto dinámico que responde a las leyes de la física, como la gravedad y las fuerzas aplicadas. Algunas de las funcionalidades clave de Rigidbody2D incluyen:

  1. Movimiento y fuerzas: Puedes aplicar fuerzas o impulso a un objeto Rigidbody2D para que se mueva en respuesta a esas fuerzas. Por ejemplo, puedes empujar un objeto con una fuerza constante o aplicar una fuerza en una dirección específica.
  2. Gravedad: Rigidbody2D permite que los objetos se vean afectados por la gravedad, lo que significa que caerán hacia abajo a menos que se les apliquen otras fuerzas que los contrarresten.
  3. Colisiones: Los objetos con Rigidbody2D interactúan con otros objetos con colisionadores (como BoxCollider2D) y generan colisiones realistas. Puedes detectar colisiones y responder a ellas mediante eventos y scripts.
  4. Restricciones de movimiento: Rigidbody2D te permite restringir el movimiento de un objeto en diferentes direcciones. Puedes controlar si el objeto puede moverse en el eje X, el eje Y o ambos, lo que es útil para simular objetos que se desplazan en una sola dirección o que están restringidos a un área específica.

A modo de resumen, Rigidbody2D es esencial para simular el movimiento y la física de objetos en un entorno 2D en Unity. Proporciona una manera conveniente de aplicar fuerzas, simular gravedad y detectar colisiones para crear una experiencia de juego más realista y dinámica.

¡A programar!

Ahora que tenemos claro los componentes que necesita nuestro player para realizar el movimiento, vamos a crear un Script que en mi caso llamaré PlayerController que gestionará el movimiento y las colisiones del jugador:

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float jumpForce = 5f;

    private Rigidbody2D rb;
    private bool isJumping;
    private bool isCrouching;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        float moveInput = Input.GetAxis("Horizontal");

        // Move the player horizontally
        rb.velocity = new Vector2(moveInput * moveSpeed, rb.velocity.y);

        if (Input.GetButtonDown("Jump") && !isJumping)
        {
            // Make the player jump
            rb.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
            isJumping = true;
        }

        if (Input.GetKeyDown(KeyCode.LeftControl))
        {
            // Crouch if the player presses the crouch key
            isCrouching = true;
            // Adjust the player's collider size and position to crouch
            // Use a BoxCollider2D component and adjust its size and offset accordingly
            GetComponent<BoxCollider2D>().size = new Vector2(GetComponent<BoxCollider2D>().size.x, GetComponent<BoxCollider2D>().size.y / 2f);
            
        }
        else if (Input.GetKeyUp(KeyCode.LeftControl))
        {
            // Stand up if the player releases the crouch key
            isCrouching = false;
            // Restore the player's collider size and position
            GetComponent<BoxCollider2D>().size = new Vector2(GetComponent<BoxCollider2D>().size.x, GetComponent<BoxCollider2D>().size.y * 2f);
            
        }
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        // Check if the player is grounded
        if (collision.gameObject.CompareTag("Ground"))
        {
            isJumping = false;
        }
    }
}

Analicemos el código

Variables

En primer lugar hemos declarado las variables que vamos a utilizar y lo hacemos dentro de nuestra clase PlayerController:

public float moveSpeed = 5f;
public float jumpForce = 5f;

private Rigidbody2D rb;
private bool isJumping;
private bool isCrouching;

En programación, una variable es un espacio de memoria designado para almacenar un valor. Las variables se utilizan para almacenar y manipular datos en un programa.

En C#, una variable se declara especificando su tipo de datos y un nombre. Por ejemplo, int edad; declara una variable llamada edad de tipo entero. Posteriormente, se puede asignar un valor a la variable utilizando el operador de asignación =. Por ejemplo, edad = 25; asigna el valor 25 a la variable edad.

Las variables pueden tener diferentes alcances o visibilidades, lo que determina desde dónde se pueden acceder y modificar. En C#, las palabras clave private y public se utilizan para especificar el alcance de una variable en una clase.

  • private: Una variable declarada como private solo es accesible desde dentro de la clase en la que se declara. No se puede acceder directamente a la variable desde fuera de la clase. Es útil para encapsular y ocultar datos internos de una clase, y para asegurar que solo los métodos de la clase tengan acceso a la variable.
  • public: Una variable declarada como public es accesible desde cualquier parte del programa, incluyendo otras clases. Se puede acceder a la variable directamente desde fuera de la clase en la que se declara. Es útil cuando se necesita que la variable sea visible y accesible desde múltiples partes del programa.

En resumen, una variable es un espacio de memoria para almacenar datos en un programa. En C#, private y public son modificadores de acceso que especifican si una variable es accesible solo desde dentro de la clase (private) o desde cualquier parte del programa (public). La elección entre private y public depende de los requisitos de visibilidad y encapsulación de los datos en una clase.

Ahora que tenemos claro que son las variables y como se declaran, podemos sacar en claro que las variables public moveSpeed y jumpForce pueden ser modificadas desde fuera del script, en este caso las he declarado así para que puedan visualizarse y modificarse desde nuestro editor en Unity.



Para terminar, tenemos las variables private rb, isJumping e isCrouching. La variable rb la utilizaremos para controlar el rigidbody2d de nuestro jugador, la variable isJumping para saber si el jugador está saltando o no y por último isCrouching para saber si el jugador puede o no agacharse.

Métodos y funciones en Unity

En Unity, Awake(), Start(), Update(), y OnCollisionEnter2D() son métodos predefinidos que se utilizan en los scripts de los componentes de Unity para controlar el comportamiento de los objetos en el juego. Estos métodos se llaman automáticamente por Unity en momentos específicos durante el ciclo de vida del objeto.

  1. Awake(): Este método se llama una vez cuando el objeto es creado en la escena. Se utiliza para inicializar variables y configuraciones antes de que comience la ejecución del juego. Es útil para realizar configuraciones iniciales, como asignar referencias a otros componentes o preparar datos necesarios para el funcionamiento del objeto.
  2. Start(): El método Start() se llama una vez en el primer frame después de que el objeto haya sido creado y todas las configuraciones en Awake() hayan sido realizadas. Es útil para iniciar acciones o comportamientos específicos que deben ocurrir al inicio del juego. Por ejemplo, iniciar animaciones, reproducir sonidos o establecer estados iniciales.
  3. Update(): Este método se llama en cada frame del juego, generalmente 60 veces por segundo. Se utiliza para realizar actualizaciones continuas y responder a eventos en tiempo real. Aquí puedes escribir lógica para controlar el movimiento, la interacción con el usuario, la detección de colisiones, la actualización de la lógica del juego, etc. El código dentro de Update() se ejecuta de forma iterativa durante la ejecución del juego.
  4. OnCollisionEnter2D(Collision2D collision): Este método se llama cuando un objeto con un colisionador (como BoxCollider2D) colisiona con otro objeto en el juego. Permite detectar colisiones y realizar acciones específicas en respuesta a ellas. El parámetro collision proporciona información sobre la colisión, como los objetos involucrados y los puntos de contacto. Puedes utilizar este método para realizar acciones como cambiar el estado del objeto, reproducir efectos de sonido, activar animaciones, etc.

Estos métodos son fundamentales para el desarrollo de juegos en Unity, ya que permiten controlar el comportamiento de los objetos y responder a eventos en el juego de manera personalizada y dinámica.

Y ahora que lo tenemos claro, analicemos nuestro método Awake, en el asignamos a nuestra variable rb el componente Rigidbody2D mediante la función GetComponent de Unity.

private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
    }

La función GetComponent<>() es un método utilizado en Unity para acceder a un componente adjunto a un objeto en la escena. Permite obtener una referencia al componente deseado para poder acceder a sus propiedades, métodos y funcionalidades desde un script.

La función GetComponent<>() se utiliza de la siguiente manera:

GetComponent<ComponentType>();

Donde ComponentType es el tipo de componente al que se desea acceder. Por ejemplo, si se desea acceder al componente Rigidbody2D adjunto a un objeto, se utilizaría GetComponent<Rigidbody2D>().

Cuando se llama a GetComponent<>(), Unity busca el componente especificado en el objeto actual y devuelve una referencia a él si se encuentra. Si el componente no está presente en el objeto, o si hay múltiples componentes del mismo tipo, la función devuelve null.

Una vez que se obtiene una referencia al componente utilizando GetComponent<>(), se puede acceder a sus propiedades y métodos utilizando la referencia devuelta. Por ejemplo, si se obtiene una referencia a un componente Rigidbody2D llamado rb, se pueden utilizar las propiedades y métodos del componente de la siguiente manera:

rb.velocity = new Vector2(5f, 0f); // Establecer la velocidad del Rigidbody2D
rb.AddForce(new Vector2(0f, 10f)); // Aplicar una fuerza al Rigidbody2D

Ahora que tenemos analizado nuestro método Awake, pasemos a Update:

private void Update()
    {
        float moveInput = Input.GetAxis("Horizontal");

        // Move the player horizontally
        rb.velocity = new Vector2(moveInput * moveSpeed, rb.velocity.y);

        if (Input.GetButtonDown("Jump") && !isJumping)
        {
            // Make the player jump
            rb.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
            isJumping = true;
        }

        if (Input.GetKeyDown(KeyCode.LeftControl))
        {
            // Crouch if the player presses the crouch key
            isCrouching = true;
            // Adjust the player's collider size and position to crouch
            // Use a BoxCollider2D component and adjust its size and offset accordingly
            GetComponent<BoxCollider2D>().size = new Vector2(GetComponent<BoxCollider2D>().size.x, GetComponent<BoxCollider2D>().size.y / 2f);
            
        }
        else if (Input.GetKeyUp(KeyCode.LeftControl))
        {
            // Stand up if the player releases the crouch key
            isCrouching = false;
            // Restore the player's collider size and position
            GetComponent<BoxCollider2D>().size = new Vector2(GetComponent<BoxCollider2D>().size.x, GetComponent<BoxCollider2D>().size.y * 2f);
            
        }
    }

Como Update se ejecuta una vez cada frame, comprobamos que botones pulsa el jugador y si se pueden realizar las acciones deseadas. En "Edit" -> "Project Settings" -> "Input Manager" podemos definir los botones de nuestro juego, esto es muy interesante por si a futuro se quiere realizar un keybinding para cambiar los botones que se utilizan en nuestro juego.



Nosotros estamos utilizando "Horizontal" y "Jump", pero esta no es la única forma de detectar los botones que pulsa el jugador, también podemos usar "KeyCode." para usar una tecla concreta como hacemos para agacharse.

En Unity, GetKeyDown() y GetKeyUp() son funciones utilizadas para detectar el estado de una tecla en un momento específico durante la ejecución del juego. Ambas funciones pertenecen a la clase Input de Unity y se utilizan para capturar la entrada del teclado.

  1. GetKeyDown():
    • Sintaxis: Input.GetKeyDown(KeyCode tecla)
    • Descripción: Esta función se utiliza para detectar si una tecla específica se ha presionado en el último frame. Devuelve true si la tecla se ha presionado y false en caso contrario.
    • Ejemplo: Input.GetKeyDown(KeyCode.Space) detectará si la tecla Espacio se ha presionado.
  2. GetKeyUp():
    • Sintaxis: Input.GetKeyUp(KeyCode tecla)
    • Descripción: Esta función se utiliza para detectar si una tecla específica se ha soltado en el último frame. Devuelve true si la tecla se ha soltado y false en caso contrario.
    • Ejemplo: Input.GetKeyUp(KeyCode.Space) detectará si la tecla Espacio se ha soltado.

Ambas funciones utilizan un parámetro KeyCode para especificar la tecla que se desea verificar. KeyCode es una enumeración que contiene los códigos para las teclas comunes del teclado, como Espacio, Enter, A, S, D, etc.

Estas funciones son útiles para controlar la interacción del jugador con el juego. Por ejemplo, puedes utilizar GetKeyDown(KeyCode.Space) para detectar cuando el jugador presiona la tecla Espacio y realizar una acción, como hacer que el personaje salte. Por otro lado, puedes utilizar GetKeyUp(KeyCode.Space) para detectar cuando el jugador suelta la tecla Espacio y realizar otra acción, como detener el salto del personaje.

Creo que no hace falta que lo diga pero, si tienes cualquier duda y no me he explicado bien, puedes escribir a mi email desde la página de contacto, a traves de cualquier red social o aquí mismo en comentarios.

Continuemos con el último método OnCollisionEnter2D. Como hemos explicado anteriormente, este método nos permite realizar acciones al detectar una colisión.

private void OnCollisionEnter2D(Collision2D collision)
    {
        // Check if the player is grounded
        if (collision.gameObject.CompareTag("Ground"))
        {
            isJumping = false;
        }
    }

En nuestro ejemplo, si nuestro jugador choca con una colisión que tenga como tag "Ground", sabremos que ha tocado el suelo y le permitiremos volver a saltar.

Configurar la escena de Unity

Vamos a crear un suelo para que nuestro jugador no caiga infinitamente al vacio por el efecto de gravedad del componente Rigidbody2D y le vamos a añadir el tag "Ground" para que nuestro script sepa que es el suelo.



Tags en Unity

Los tags (etiquetas) en Unity son una forma de asignar identificadores a los objetos en tu escena. Permiten clasificar y categorizar los objetos para facilitar su identificación y manipulación en el código.

Los tags son útiles en varios aspectos:

  1. Identificación de objetos: Los tags te permiten identificar y referenciar objetos específicos en tu código de manera más fácil y eficiente. En lugar de buscar objetos por su nombre o mediante referencias directas, puedes utilizar los tags para acceder a ellos.
  2. Colisiones y triggers: Los tags son utilizados en la detección de colisiones y triggers. Puedes asignar tags a objetos y luego utilizarlos en funciones como OnCollisionEnter o OnTriggerEnter para determinar qué tipo de objetos están colisionando o interactuando.
  3. Gestión de grupos o categorías: Los tags también se utilizan para agrupar objetos relacionados o categorizarlos. Puedes asignar el mismo tag a varios objetos para identificar que pertenecen a la misma categoría o grupo. Por ejemplo, puedes tener objetos enemigos con el tag "Enemy" o objetos recolectables con el tag "Collectible".
  4. Filtrado y búsqueda: Los tags pueden ser utilizados para filtrar objetos en funciones como FindGameObjectWithTag o FindGameObjectsWithTag. Estas funciones permiten buscar objetos en la escena que tengan un tag específico, lo que facilita la búsqueda y manipulación de objetos.

Los tags son configurables en el editor de Unity. Puedes crear tags personalizados en la ventana de "Tags and Layers" y asignarlos a los objetos en la pestaña "Inspector" de cada objeto.



En resumen, los tags en Unity permiten clasificar y categorizar los objetos en la escena. Proporcionan una forma más eficiente de identificar y acceder a los objetos en el código, y se utilizan en la detección de colisiones, agrupación de objetos y filtrado en las funciones de búsqueda de Unity.

Nuestro personaje ya se mueve

Ya tenemos a nuestro personaje programado, quedan cositas más avanzadas como controlar las animaciones, añadirle sonido... pero en esencia este es el núcleo que da vida a nuestro personaje ya que son sus mecánicas.

Espero que este post te haya sido útil y te haya brindado una base sólida para continuar tu aventura en el desarrollo de videojuegos. ¡No dudes en compartir tus proyectos y experiencias en los comentarios! Hasta la próxima entrada y ¡feliz desarrollo de juegos!

Comentarios

Entradas populares de este blog

Metal Gear Solid V Analisis sin spoilers 2: Gráficos Antigua Generación VS Nueva Generación

IA: Seguir objetivo y esconderse en Unity3D

🌟 Crear un videojuego de plataformas con Unity 🎮 6 – Crear efecto parallax