video juegos

Buscador de Gemas en Tiempo Record

El presente proyecto, Buscador de Gemas en Tiempo Récord, consiste en el desarrollo de un videojuego de plataformas en tres dimensiones utilizando el motor de desarrollo Unity. En este juego, el jugador asume el rol de un explorador que debe recolectar gemas dispersas en diferentes escenarios dentro de un límite de tiempo, lo que añade un componente de urgencia y estrategia. Este tipo de juegos de 3D se caracteriza por centrarse en la exploración de entornos tridimensionales, la precisión en los saltos y la superación de obstáculos mediante mecánicas de movimiento como correr, saltar y escalar. El objetivo principal de este trabajo es diseñar y programar una experiencia interactiva que combine jugabilidad fluida, un entorno visual atractivo y una estructura de niveles que promueva el aprendizaje progresivo del jugador.

Inicio juego buscador de Gemas en tiempo Record

Figura 1. Inicio de jugabilidad

Jugabilidad Recolector Gemas

La jugabilidad del videojuego desarrollado se basa en mecánicas clásicas del género de plataformas en 3D, con un enfoque en la simplicidad y la precisión del control. El jugador asume el rol de un personaje principal que debe desplazarse a través de escenarios tridimensionales superando obstáculos, evitando caídas y alcanzando puntos clave del mapa.

El sistema de control está diseñado para ser intuitivo y accesible. El desplazamiento del personaje se realiza mediante el teclado, utilizando las siguientes teclas:

  • W: Avanzar hacia adelante
  • A: Desplazarse hacia la izquierda
  • S: Retroceder
  • D: Desplazarse hacia la derecha

Estas teclas permiten moverse libremente por el entorno, combinando desplazamientos horizontales con rotación de cámara, que puede ser controlada con el ratón (si se ha implementado esta función). Además, el jugador puede realizar un salto utilizando la tecla espacio, lo que le permite superar obstáculos, saltar entre plataformas flotantes o alcanzar zonas elevadas.

Modulos Scrips unity

Figura 2. Jugabilidad

Creación Escenario

La base del entorno de los juegos en 3D se crea de la siguiente manera: primero, se inicia un nuevo proyecto en Unity seleccionando la plantilla 3D y asignándole un nombre representativo.

Una vez dentro del entorno de desarrollo, se procede a crear el suelo que servirá como plataforma principal. Esto se hace haciendo clic derecho en el panel Hierarchy y seleccionando la opción 3D Object > Cube.

Al cubo generado se le asigna el nombre de “Ground” y se ajusta su escala en el panel Inspector para convertirlo en una superficie amplia y plana, por ejemplo, con valores de escala X=20, Y=1 y Z=20. Esta modificación transforma el cubo en una plataforma adecuada para el movimiento del personaje.

Opcionalmente, para mejorar la apariencia visual del escenario, se puede crear un nuevo material desde el panel Assets, asignarle un color personalizado y aplicarlo al objeto “Ground” arrastrándolo sobre él.

Con estos pasos, se establece el terreno base sobre el cual se construirá el resto del entorno y se desarrollará la jugabilidad del juego de plataformas en 3D.

Cargue de ecenarios

Figura 3. Creación escenario.

Importación de personaje

Se selecciona un modelo para juegos de 3D adecuado al estilo del videojuego. Posteriormente, se elige una animación básica y se descargó el archivo en formato FBX para Unity, con la opción with ski activada para incluir tanto el modelo como su esqueleto.

Una vez descargado el archivo, este fue importado al proyecto de Unity arrastrándolo directamente a la carpeta Assets dentro del panel del proyecto. Unity reconoce automáticamente la estructura del rig humanoide. A continuación, se seleccionó el archivo del modelo y, en el inspector, se accedió a la pestaña Rig. Allí, se cambió el tipo de animación de Generic y se hizo clic en Apply para asegurar la compatibilidad con el sistema de animaciones de Unity.

Luego, se creó un objeto en la escena arrastrando el modelo desde la carpeta de assets hasta el panel “Hierarchy”. Este objeto fue renombrado como Player. Para habilitar la interacción física con el entorno, se le agregó un componente Rigidbody, permitiéndole reaccionar a la gravedad y otras fuerzas físicas. También se añadió un Capsule Collider, adaptado a la forma y tamaño del personaje, para gestionar las colisiones con el entorno.

Paralelamente, se creó un Animator Controller, el cual fue asignado al componente Animator del modelo. Este controlador permite administrar las distintas animaciones del personaje (por ejemplo, Idle, Caminar, Saltar) mediante parámetros que luego son manipulados desde un script en C#. Finalmente, se desarrolló un script de movimiento básico que responde a las entradas del teclado (WASD para moverse y barra espaciadora para saltar) y que controla tanto el desplazamiento del personaje como el cambio de animaciones a través del Animator.

Cargue de personajes

Figura 4. Carga del personaje

Carga de lógica en la aplicación

Los juegos de 3D tienen una mecánica de interacción que permita al jugador recoger objetos (gemas), se desarrolló unos script , que permite detectar colisiones entre el personaje y los objetos recolectables, así como gestionar su desaparición del entorno y su conteo interno. Esta funcionalidad es fundamental para construir dinámicas de juego como la obtención de puntos, desbloqueo de niveles o recolección de elementos clave.

Se debe ubicar el archivo con extensión .cs y arrastrarlo directamente a la carpeta Assets del proyecto dentro del panel del explorador de Unity. Alternativamente, también puede copiarse y pegarse manualmente dentro de la carpeta del proyecto desde el sistema de archivos del equipo. Una vez importado, Unity reconocerá automáticamente el archivo como un componente de script y procederá a compilarlo.

Logica de la aplicacion

Figura 5. Importación Script

Timer en C#

public class Timer : MonoBehaviour
{
public float timer = 300f; // tiempo inicial en segundos (5 min)
public TextMeshProUGUI textTimer;
public GameObject gameOverPanel;
private bool isGameOver = false;

 void Update()
{
    if (isGameOver) return;

    timer -= Time.deltaTime;
    if (timer <= 0f)
    {
        timer = 0f;
        GameOver();
    }

    int minutes = Mathf.FloorToInt(timer / 60f);
    int seconds = Mathf.FloorToInt(timer % 60f);

    textTimer.text = string.Format("{0:00}:{1:00}", minutes, seconds);
}

void GameOver()
{
    isGameOver = true;
    Time.timeScale = 0f;
    gameOverPanel.SetActive(true);
}

public void Retry()
{
    Time.timeScale = 1f;
    SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}

Script para Caídas del Personaje

public class PlayerMotion : MonoBehaviour
{
public Transform cam;
public float speed;
public float speedRotation = 10;
public float groundDistanceUp, groundDistance;
public float jumpPower = 35;
public float gravity = 9.8f;
public float gravityMultiplayer = 1;
public bool onGround, isJump;
public bool stop;
public LayerMask groundLayer;
Rigidbody rb;
Animator anim;
Vector2 _move;
Vector3 move;
//variables Camara
public CinemachineFreeLook cinemachineFreeLook;
public GameObject targetCam;
public float rotationSpeedCamX, rotationSpeedCamY;
Vector2 m_look;

void Awake()
{
    rb = GetComponent<Rigidbody>();
    anim = GetComponentInChildren<Animator>();       
}

private void OnDrawGizmosSelected()
{
    Gizmos.color = Color.yellow;
    Gizmos.DrawSphere(transform.position + (Vector3.up * groundDistanceUp),groundDistance);
}


void FixedUpdate()
{
    onGround = Physics.CheckSphere(transform.position + (Vector3.up * groundDistanceUp),groundDistance, groundLayer);
    if (!onGround)
        rb.AddForce(-gravity * gravityMultiplayer * Vector3.up, ForceMode.Acceleration);
    if (isJump && onGround)
    {
        isJump = false;
        anim.SetBool("OnAir", false);
        rb.velocity = Vector3.zero;
    }
    else if (!isJump && !onGround)
    {
        anim.SetBool("OnAir", true);
        isJump = true;
        Stopping();
        anim.SetTrigger("Fall");
    }

    if (stop)
        return;

    if(_move.x != 0 || _move.y != 0)
    {
        move = cam.forward * _move.y;
        move += cam.right * _move.x;
        move.Normalize();
        move.y = 0;
        rb.velocity = move * speed;
        Vector3 dir = cam.forward * _move.y;
        dir += cam.right * _move.x;
        dir.Normalize();
        dir.y = 0;
        Quaternion targetR = Quaternion.LookRotation(dir);
        Quaternion playerR = Quaternion.Slerp(transform.rotation, targetR, speedRotation * Time.fixedDeltaTime);
        transform.rotation = playerR;

    }

}
public void OnMove(InputValue value)
{
    _move = value.Get<Vector2>();
    if (stop)
        return;
    anim.SetBool("Move", (_move.x == 0 && _move.y == 0) ? false : true);
    anim.SetFloat("Moving", (_move.x == 0 && _move.y == 0) ? 0 : 1);
    if (_move.x == 0 && _move.y == 0)
         rb.velocity = Vector3.zero; 
    anim.SetFloat("MoveX", _move.x);
    anim.SetFloat("MoveY", _move.y);
}

public void OnJump()
{
    Stopping();
    isJump = true;
    Vector2 moveDir = _move;
    anim.SetTrigger("Jumping");
    if(moveDir != Vector2.zero)
    {
        Vector3 dir = cam.forward * moveDir.y;
        dir += cam.right * moveDir.x;
        dir.Normalize();
        dir.y = 0;
        Quaternion targetR = Quaternion.LookRotation(dir);
        transform.rotation = targetR;
        rb.AddForce((transform.forward + Vector3.up) * jumpPower, ForceMode.Impulse);
    }
    else
    {
        rb.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
    }
    anim.SetBool("OnAir", true);
}

public void OnCam(InputValue value)
{
    m_look = value.Get<Vector2>();
    cinemachineFreeLook.m_XAxis.Value += m_look.x * rotationSpeedCamX;
    cinemachineFreeLook.m_YAxis.Value += m_look.y * rotationSpeedCamY * Time.fixedDeltaTime;
}


public void FallEnd()
{
    StopEnd();
}


void Stopping()
{
    if(onGround)
    rb.velocity = Vector3.zero;
    stop = true;
    anim.SetFloat("MoveX",0);
    anim.SetFloat("MoveY",0);
    anim.SetFloat("Moving",0);
    anim.SetBool("Move",false);
}

public void StopEnd()
{
    anim.SetBool("Move", (_move.x == 0 && _move.y == 0) ? false : true);
    anim.SetFloat("Moving", (_move.x == 0 && _move.y == 0) ? 0 : 1);
    anim.SetFloat("MoveX", _move.x);
    anim.SetFloat("MoveY", _move.y);
    rb.velocity = Vector3.zero;
    stop = false;
}
}

Logica de Recojer

public class Collection : MonoBehaviour
{
public int Quantity = 0;
public int totalObjects = 10;
public TextMeshProUGUI textCount;
public TextMeshProUGUI winText;

void Start()
{
    Quantity = 0;
    UpdateCounter();
}

public void UpdateCounter()
{
    if (textCount != null)
    {
        int remaining = totalObjects - Quantity;
        textCount.text = "Objetos restantes: " + remaining;

        if (remaining <= 0)
        {
            textCount.text = "¡Todos recolectados!";

        } 
    }
}
}

Video

Video. Autoría Propia

Créditos

Autor: Daniel Forigua Martinez

EditorMaster- Ingeniero Carlos Iván Pinzón

Código : UCMV-9

Universidad Universidad Central

Referencias

Accurate. (s.f.). Berserker Mutant [Modelo 3D]. Sketchfab. https://sketchfab.com/3d-models/berserker-mutant-accurate-271e55f3c5e9429f91d3a1ccc31d1f53

Ready Player Me. (s.f.). Avatar personalizado [Modelo 3D]. Ready Player Me. https://readyplayer.me/avatar

Sleepmesh. (s.f.). Modelos 3D de gemas Vol. 1 [Modelo 3D]. CLIP STUDIO ASSETS. https://assets.clip-studio.com/es-es/detail?id=2041053

Unity Technologies. (s.f.). Crear y usar scripts. Unity. https://docs.unity3d.com/es/530/Manual/CreatingAndUsingScripts.html

Unity Technologies. (s.f.). Visión general de los colliders. Unity. https://docs.unity3d.com/560/Documentation/Manual/CollidersOverview.html

Unity Technologies. (s.f.). Unity Essentials Pathway. Unity. https://learn.unity.com/pathway/unity-essentials