video juegos

Desarrollo del videojuego “The Four Trials” en Unity: paso a paso

Introducción

El desarrollo de videojuegos se ha convertido en una de las áreas más importantes del entretenimiento digital, permitiendo crear experiencias interactivas mediante herramientas como Unity. En este artículo se presenta el proceso de creación del videojuego “The Four Trials”, un proyecto desarrollado en tercera persona que incluye diferentes escenarios, selección de personajes, temporizador y recolección de objetos.

Para el desarrollo del proyecto se utilizaron recursos y herramientas como Sketchfab para algunos modelos 3D, MetaPerson Creator para la creación de personajes, Mixamo para las animaciones y Blender para la organización de texturas y modelos.

A continuación, se mostrará paso a paso el proceso de creación e implementación de los diferentes elementos del videojuego dentro de Unity.

Paso A Paso

Para el desarrollo del videojuego “The Four Trials” se utilizó Unity en su versión 6.4, empleando una plantilla de proyecto 3D con el fin de trabajar escenarios, personajes y mecánicas en tercera persona.

Una vez instalado Unity Hub y la versión correspondiente del motor, se creó un nuevo proyecto seleccionando la opción 3D Core, asignándole el nombre The Four Trials. Esta configuración permitió disponer de las herramientas necesarias para la construcción del entorno, la iluminación y la integración de modelos 3D dentro del videojuego.

Importación del paquete Starter Assets – Third Person Controller

Después de crear el proyecto, se importó el paquete Starter Assets – Third Person Controller, el cual proporciona un controlador básico en tercera persona junto con el sistema de cámara y movimiento del personaje. Este paquete facilita la configuración inicial del videojuego, permitiendo implementar desplazamiento, salto y seguimiento de cámara sin necesidad de programar estas funciones desde cero.

Para instalarlo, se accedió a la sección Window > Package Manager, donde posteriormente se descargó e importó el paquete dentro del proyecto de Unity. Una vez finalizada la importación, se agregaron los prefabs correspondientes del controlador en tercera persona dentro de la escena principal para comenzar con la configuración del entorno y los personajes.

Configuración del escenario principal

El escenario principal del videojuego se creó utilizando los recursos incluidos en el paquete Starter Assets – Third Person Controller. Estos elementos permitieron construir el entorno inicial y configurar el sistema de movimiento en tercera persona.

Además, se importaron modelos y escenarios complementarios desde Sketchfab. Estos recursos ayudaron a mejorar el apartado visual y la ambientación de las diferentes escenas del proyecto.

Los modelos descargados en formato FBX se organizaron en carpetas dentro de la sección Assets de Unity. También se almacenaron sus respectivas texturas para facilitar la administración de los archivos.

Finalmente, los modelos se arrastraron desde la carpeta Assets hacia la jerarquía de la escena. Después, se ajustaron la posición, escala y rotación de cada objeto para adaptarlos correctamente al entorno del videojuego.

Creación e importación de personajes

Para crear los personajes del videojuego se utilizó MetaPerson Creator. Esta plataforma permite generar modelos humanos en 3D a partir de una fotografía. Después de generar el personaje, se descargó el modelo en formato FBX.

Pesonaje en MetaPerson Creato

Luego, el archivo se abrió en Blender para organizar correctamente las texturas. En la sección UV Editing se visualizaron las imágenes asociadas al modelo y posteriormente se exportaron a una carpeta junto con el archivo FBX.

UV Editing

Después, el personaje se cargó en Mixamo para añadir las animaciones necesarias del videojuego. Una vez configuradas las animaciones, el modelo se descargó nuevamente en pose T.

Pesonaje en mixamo
Descargar en pose T

Finalmente, se importó el personaje en Unity arrastrando la carpeta completa hacia Assets. El modelo se agregó a la escena principal y se configuró como tipo de animación Humanoid para hacerlo compatible con el sistema Third Person Controller.

Se añadió el personaje Third Person Controller del paquete Starter Assets, ubicado en la carpeta Prefabs. Este controlador ya incluye la configuración necesaria para el movimiento del personaje y el seguimiento de la cámara en tercera persona.

Después, se ajustaron la posición y la escala del controlador dentro de la escena para adaptarlo correctamente al entorno del videojuego.

Posteriormente, se arrastró el Avatar del modelo del robot hacia el personaje personalizado. De esta manera, el personaje conservó las animaciones y el sistema de movimiento configurado previamente.

Finalmente, se desactivó el robot ubicado en la carpeta Geometry para evitar elementos duplicados dentro de la escena.

Robot prefab
Personaje con el prefab configurado

Solución del problema de cámara en Unity

Durante el desarrollo del proyecto en Unity se presentó un inconveniente con el sistema de cámara. La cámara dejó de seguir correctamente al personaje dentro de la escena.

Al revisar la consola de Unity, aparecieron errores relacionados con scripts faltantes en los objetos cm y PlayerFollowCamera. Después de analizar el problema, se identificó que el paquete Cinemachine no estaba instalado en el proyecto. Este paquete es necesario para el funcionamiento de la cámara en tercera persona del paquete Starter Assets.

Para solucionar el error, se abrió el Package Manager de Unity y se instaló el paquete Cinemachine desde la sección Unity Registry.

Una vez completada la instalación, Unity restauró automáticamente los componentes faltantes de la cámara. Finalmente, el sistema volvió a funcionar correctamente y la cámara siguió nuevamente al personaje en tiempo real.

Creación de los otros personajes

Para añadir más personajes al videojuego, se buscaron diferentes modelos en Mixamo. Posteriormente, los modelos se descargaron junto con sus respectivas animaciones.

Otros personajes para el multiplayer

Después de la descarga, se creó una carpeta para cada personaje con el fin de organizar correctamente los archivos del modelo y sus texturas antes de importarlos en Unity.

Finalmente, todas las carpetas de los personajes se arrastraron hacia la carpeta Assets del proyecto. Esto permitió mantener una estructura organizada y facilitar la implementación de los modelos dentro de las diferentes escenas del videojuego.

Carpeta Personajes

Creación de interfaces y menú principal

Canvas de inicio

Las escenas del proyecto se encuentran almacenadas dentro de la carpeta Scenes ubicada en Assets. En esta ubicación se creó una nueva escena llamada Intro, la cual se utilizó como pantalla principal del videojuego.

Creando escena

Después de crear la escena, se añadió un objeto Canvas para diseñar la interfaz gráfica del menú principal. Posteriormente, el Canvas se ajustó al tamaño de la pantalla para adaptar correctamente todos los elementos de la interfaz.

Creación de canvas

Se escala al tamaño de la pantalla

escalar canvas

Luego, se agregó un Panel dentro del Canvas y se creó una carpeta destinada a almacenar las imágenes utilizadas en el proyecto.

Panel de canvas

A este panel se le va a agregar una imagen de fondo por lo que también se debe tener una carpeta para las imágenes

Para colocar la imagen principal del menú, se añadió un objeto Raw Image. Después, en el panel Inspector, se arrastró la imagen correspondiente al apartado Texture. Finalmente, se presionó la tecla T para ajustar la imagen al tamaño completo del Canvas.

Creación de escenas con imágenes

Con el fin de evitar repetir el mismo procedimiento, se duplicó la escena principal para crear nuevas pantallas, como la escena de controles y la escena de introducción.

Después de copiar la escena, únicamente se reemplazó la imagen utilizada en el objeto Raw Image, permitiendo reutilizar la misma estructura del Canvas en las demás pantallas del videojuego.

Creación de botones

Una vez diseñado el menú principal, se añadieron botones desde la opción UI > Button TextMeshPro dentro del Canvas.

Se crearon tres botones principales:

  • Inicio
  • Controles
  • Salir

Posteriormente, se modificaron los colores y textos de cada botón para adaptarlos al diseño visual del videojuego.

Creación de botones

Programación de botones y navegación entre escenas

Para programar las funciones de los botones, se creó una carpeta llamada Scripts dentro de Assets. Luego, se generó un script llamado MenuPrincipal, encargado de controlar la navegación entre escenas y la opción para cerrar el videojuego.

El script se asignó al objeto Canvas arrastrándolo directamente desde la carpeta Scripts hacia la escena.

Después, desde el apartado OnClick() de cada botón, se arrastró el Canvas para acceder a los métodos creados en el script.

El método Siguiente() permitió avanzar a la siguiente escena del videojuego. Por otro lado, el método Atras() permitió regresar a la escena anterior. Finalmente, el método QuitarJuego() cerró la aplicación al presionar el botón de salir.

Para habilitar correctamente el cambio entre escenas, se añadieron todas las escenas del proyecto en la opción File > Build Profiles de Unity. Allí, cada escena recibió un número de identificación según el orden establecido dentro del proyecto.

Método para el botón salir del juego

Los métodos Siguiente() y Atras() se reutilizaron en los diferentes botones del videojuego, facilitando la navegación entre todas las escenas creadas.

Escenas con sus botones

Selector de personajes

Para implementar el selector de personajes, se creó una nueva escena dentro del proyecto en Unity. En esta escena se añadieron los tres personajes previamente importados al proyecto.

Posteriormente, los modelos se organizaron dentro del escenario con el fin de visualizar correctamente cada personaje durante la selección. Además, se ajustaron elementos como posición, rotación y escala para mejorar la presentación visual dentro de la escena.

Después de agregar los personajes a la escena, se creó un script llamado SimpleCharacterSelector. Este script permitió cambiar entre los diferentes personajes disponibles y seleccionar uno antes de iniciar el juego.

El código se encargó de mostrar un personaje a la vez mediante la activación y desactivación de objetos. Además, incluyó funciones para avanzar al siguiente personaje, regresar al anterior y cargar la escena del primer nivel al presionar el botón de jugar.

Posteriormente, se creó un objeto vacío dentro de la jerarquía de la escena y se le asignó el script arrastrándolo desde la carpeta Scripts. Después, en el panel Inspector, se configuró la cantidad de personajes y se arrastraron los modelos correspondientes al arreglo del script.

Para mejorar la visualización del selector, se desactivaron todos los personajes y únicamente se dejó activo el primero al iniciar la escena.

Escenario del selector de personajes

Luego, se construyó un escenario sencillo utilizando planos para crear el entorno donde se mostrarían los personajes seleccionables.

Posteriormente, se añadieron botones para navegar entre los personajes y confirmar la selección. Cada botón recibió el método correspondiente desde el apartado OnClick(), permitiendo ejecutar las funciones del script para avanzar, retroceder o iniciar el juego.

Una vez completada la funcionalidad, se agregó una imagen al plano para utilizarla como material del escenario. Finalmente, se ajustó la posición de los botones y los elementos de la interfaz para mejorar la presentación visual del selector de personajes.

Organización de modelos por niveles

Con el fin de mantener una estructura organizada dentro del proyecto en Unity, se creó una carpeta destinada a almacenar los modelos utilizados en cada nivel del videojuego.

Dentro de esta carpeta se organizaron los escenarios, objetos y recursos 3D según el nivel correspondiente. Esta organización facilitó la administración de archivos y permitió localizar rápidamente los modelos necesarios durante el desarrollo de cada escena.

Nivel 1

Para la creación del primer nivel del videojuego se descargó un modelo de laberinto desde Sketchfab. El modelo se importó en Unity junto con sus respectivas texturas para integrarlo dentro de la escena.

Después de importar el escenario, se configuraron las colisiones del mapa. Para evitar que el personaje atravesara las paredes y el piso, se seleccionaron los objetos principales del laberinto y se les añadió el componente Box Collider desde el panel Inspector.

laberinto

Lógica para el spawn del personaje seleccionado

Con el fin de cargar el personaje elegido previamente en el selector, se creó un objeto vacío dentro de la escena para utilizarlo como punto de aparición del jugador.

Posteriormente, el objeto SpawnPoint se posicionó correctamente dentro del mapa para definir el lugar donde aparecería el personaje al iniciar el nivel.

Después, se creó un script llamado SpawnPlayer. Este script se encargó de obtener el personaje seleccionado mediante PlayerPrefs y generar automáticamente el modelo correspondiente en la posición del SpawnPoint.

Finalmente, el script se asignó al objeto GameManager, donde también se configuraron los personajes disponibles y el punto de aparición desde el panel Inspector.

Implementación de la moneda coleccionable

La moneda utilizada dentro del nivel fue obtenida desde la plataforma Sketchfab, utilizando un modelo 3D descargado para integrarlo como objeto coleccionable dentro del laberinto.

Posteriormente, al modelo de la moneda se le agregó un componente Collider, con el fin de permitir la detección de contacto entre el jugador y el objeto dentro de la escena.

Después, se activó la opción Is Trigger en el Collider. Esta configuración permite que el jugador pueda atravesar la moneda mientras el sistema detecta automáticamente la colisión sin generar bloqueos físicos en el movimiento.

Finalmente, se creó un script encargado de detectar cuándo el jugador recoge la moneda. Al producirse la interacción, el sistema ejecuta la acción correspondiente para indicar que el nivel fue completado correctamente y permitir el avance dentro del videojuego.

Implementación del temporizador del nivel

Con el fin de agregar un mayor nivel de dificultad y control dentro de la partida, se implementó un sistema de temporizador para medir el tiempo disponible o transcurrido durante el nivel.

Inicialmente, se creó un objeto Canvas dentro de la escena de Unity, el cual se utiliza para mostrar elementos de interfaz gráfica en pantalla. Dentro del Canvas, se agregó un objeto de tipo Text para visualizar el tiempo en tiempo real durante la partida.

Posteriormente, se ajustó la posición del texto dentro de la interfaz para que el temporizador fuera visible para el jugador mientras recorre el laberinto.

Después, se desarrolló un script encargado de controlar el tiempo del juego. Este script actualiza constantemente el valor mostrado en pantalla y permite gestionar acciones relacionadas con el temporizador, como finalizar la partida o mostrar el tiempo restante.

Finalmente, se creó un objeto GameObject dentro de la escena y se le agregó el script del temporizador anteriormente creado. Desde el panel Inspector, se puede modificar fácilmente el tiempo asignado para cada nivel según la dificultad deseada del videojuego.

Desarrollo del nivel 2: Recoger tesoros con enemigo persiguiendo

Para el desarrollo del segundo nivel se diseñó un escenario enfocado en la exploración y supervivencia, donde el jugador debe recoger diferentes tesoros mientras es perseguido por un enemigo dentro del mapa.

Inicialmente, el escenario fue construido utilizando diferentes elementos básicos como planos, cajas, escaleras, puentes y otros objetos del entorno. Además, se utilizaron componentes incluidos en el paquete Starter Assets de Unity para complementar la estructura y jugabilidad del nivel.

Posteriormente, a cada objeto del escenario se le agregó el componente Mesh Collider, permitiendo que el personaje interactúe correctamente con el entorno y evitando que atraviese paredes, plataformas, escaleras u otros obstáculos del mapa.

Después, se configuró el sistema NavMesh sobre las superficies principales del escenario, incluyendo el suelo, puentes y escaleras. Esto permitió generar rutas de navegación válidas para el enemigo y mejorar el desplazamiento dentro del nivel.

Gracias a esta configuración, el enemigo puede recorrer el escenario de manera automática persiguiendo al jugador mientras este intenta recoger los tesoros necesarios para completar el nivel.

Movimiento del enemigo

Para permitir que el enemigo persiguiera constantemente al jugador dentro del nivel 2, se implementó un script utilizando el sistema NavMeshAgent de Unity. Este script se encargó de localizar automáticamente al jugador mediante la etiqueta Player y actualizar continuamente la posición de destino del enemigo para seguirlo dentro del escenario. Además, se configuró una velocidad de movimiento para aumentar la dificultad del nivel y generar mayor presión durante la recolección de los objetos.

using UnityEngine;
using UnityEngine.AI;

public class Enemy: MonoBehaviour
{
private Transform player;
private NavMeshAgent agent;

void Start()
{
// Obtiene el NavMeshAgent
agent = GetComponent<NavMeshAgent>();

// Velocidad del enemigo
agent.speed = 0.5f;

// Busca automáticamente el jugador
player = GameObject.FindGameObjectWithTag("Player").transform;
}

void Update()
{
// Persigue al jugador
if (player != null)
{
agent.SetDestination(player.position);
}
}
}

Implementación de Animaciones para un Enemigo en Unity usando Mixamo

Para agregar animaciones a un enemigo en Unity se utilizó un modelo descargado desde Mixamo con una animación de caminata incluida. El objetivo fue lograr que el personaje enemigo reprodujera automáticamente la animación al iniciar el juego.

Importación del modelo animado

Primero se importó el archivo .fbx descargado desde Mixamo al proyecto de Unity. Después se configuró correctamente el rig del personaje:

  • En el Inspector se abrió la pestaña Rig.
  • Se seleccionó:
    • Animation Type: Humanoid
    • Avatar Definition: Create From This Model
  • Finalmente se presionó Apply.

Esto permitió que Unity reconociera correctamente el esqueleto y las animaciones del personaje.

Configuración de la animación y Animator Controller

En la pestaña Animation del modelo se verificó que la animación estuviera correctamente importada y se activó la opción Loop Time para que la caminata se reprodujera continuamente.Después se creó un Animator Controller desde Create > Animator Controller, nombrado como EnemyAnimator. Finalmente, la animación importada desde Mixamo se arrastró a la ventana Animator para convertirla en el estado principal del enemigo.

Asignación del Animator al enemigo

El siguiente paso fue seleccionar el objeto enemigo en la jerarquía y agregar el componente:

Animator

En el campo Controller se asignó el archivo EnemyAnimator.

Con esto, el enemigo comenzó a reproducir automáticamente la animación al iniciar el juego.

Texturización del escenario del nivel 2

Con el fin de mejorar la apariencia visual del segundo nivel, se creó una carpeta específica destinada al almacenamiento y organización de las texturas utilizadas dentro del escenario.

En esta carpeta se importaron diferentes materiales y texturas para aplicarlas sobre los objetos del mapa, como paredes, pisos, puentes, escaleras y demás elementos del entorno.

Posteriormente, las texturas fueron asignadas a los distintos objetos del escenario mediante materiales dentro de Unity, permitiendo darle una apariencia más detallada y realista al nivel.

Gracias a este proceso de texturización, el escenario del nivel 2 obtuvo una mejor ambientación visual, mejorando la experiencia del jugador durante la exploración y la persecución del enemigo.

Implementación del mar y zona de derrota

Con el fin de evitar que el jugador pudiera salir del escenario y además mejorar visualmente el entorno del nivel 2, se decidió agregar una zona de mar alrededor del mapa.

Inicialmente, se creó un objeto tipo Plane dentro de la escena de Unity y posteriormente se posicionó alrededor del escenario para rodear completamente el mapa.

Después, se añadieron partículas para generar un mejor efecto visual y dar mayor sensación de movimiento en el agua.

Posteriormente, se implementó un Shader personalizado encargado de crear el efecto visual del mar con movimiento dinámico, transparencia y simulación de olas. Este shader controla diferentes propiedades visuales como color, espuma, transparencia y desplazamiento del agua para lograr una apariencia más realista.

El archivo del shader fue descargado en formato .shader y posteriormente agregado dentro de la carpeta Assets del proyecto en Unity.

Particulas

Código shader

Shader “Custom/WaterShader”
{
Properties
{
_DeepColor(“Deep Water Color”, Color) = (0,0.25,0.8,1)
_ShallowColor(“Shallow Water Color”, Color) = (0.4,0.8,1,1)
_FresnelColor(“Fresnel Color”, Color) = (1,1,1,1)
_FresnelPower(“Fresnel Power”, Range(0.1,5.0)) = 2.0
_Transparency(“Transparency”, Range(0,1)) = 0.8

    _Amplitude1("Amplitude1", Range(0,1)) = 0.1
    _Frequency1("Frequency1", Range(0,10)) = 3
    _Speed1("Speed1", Range(0,10)) = 1
    _Direction1("Direction1 (X,Z)", Vector) = (1,0,0,0)

    _Amplitude2("Amplitude2", Range(0,1)) = 0.06
    _Frequency2("Frequency2", Range(0,10)) = 2
    _Speed2("Speed2", Range(0,10)) = 1.3
    _Direction2("Direction2 (X,Z)", Vector) = (0,1,0,0)

    _Amplitude3("Amplitude3", Range(0,1)) = 0.08
    _Frequency3("Frequency3", Range(0,10)) = 4
    _Speed3("Speed3", Range(0,10)) = 1.8
    _Direction3("Direction3 (X,Z)", Vector) = (0.7,0.7,0,0)

    _FoamColor("Foam Color", Color) = (1,1,1,1)
    _FoamTexture("Foam Texture (R)", 2D) = "white" {}
    _FoamScale("Foam Texture Scale", Range(0,10)) = 3.0
    _FoamIntensity("Foam Intensity", Range(0,2)) = 1.0

    _SeaLevel("Base Sea Level", Float) = 0.0
}

SubShader
{
    Tags { "Queue"="Transparent" "RenderType"="Transparent" }
    Blend SrcAlpha OneMinusSrcAlpha
    ZWrite Off

    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

        struct appdata
        {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float2 uv : TEXCOORD0;
        };

        struct v2f
        {
            float4 clipPos : SV_POSITION;
            float3 worldPos : TEXCOORD0;
            float3 normal : TEXCOORD1;
            float2 uv : TEXCOORD2;
            float waveHeight : TEXCOORD3;
        };

        float4 _DeepColor;
        float4 _ShallowColor;
        float4 _FresnelColor;
        float _FresnelPower;
        float _Transparency;

        float _Amplitude1, _Frequency1, _Speed1; float4 _Direction1;
        float _Amplitude2, _Frequency2, _Speed2; float4 _Direction2;
        float _Amplitude3, _Frequency3, _Speed3; float4 _Direction3;

        float4 _FoamColor;
        sampler2D _FoamTexture;
        float _FoamScale;
        float _FoamIntensity;

        float _SeaLevel;

        float3 GerstnerWave(float3 pos, float amplitude, float frequency, float speed, float2 direction)
        {
            float theta = dot(direction, pos.xz) * frequency + (_Time.y * speed);
            float sinT = sin(theta);
            float cosT = cos(theta);

            float3 offset;
            offset.x = amplitude * cosT * direction.x;
            offset.z = amplitude * cosT * direction.y;
            offset.y = amplitude * sinT;

            return offset;
        }

        v2f vert(appdata v)
        {
            v2f o;

            float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
            float3 worldNormal = normalize(mul((float3x3)unity_ObjectToWorld, v.normal));

            float2 dir1 = normalize(_Direction1.xy);
            float2 dir2 = normalize(_Direction2.xy);
            float2 dir3 = normalize(_Direction3.xy);

            float3 waveOff1 = GerstnerWave(worldPos, _Amplitude1, _Frequency1, _Speed1, dir1);
            float3 waveOff2 = GerstnerWave(worldPos, _Amplitude2, _Frequency2, _Speed2, dir2);
            float3 waveOff3 = GerstnerWave(worldPos, _Amplitude3, _Frequency3, _Speed3, dir3);

            float3 totalOffset = waveOff1 + waveOff2 + waveOff3;

            worldPos += totalOffset;

            o.waveHeight = worldPos.y - _SeaLevel;
            o.clipPos = mul(UNITY_MATRIX_VP, float4(worldPos, 1.0));
            o.worldPos = worldPos;
            o.normal = worldNormal;
            o.uv = v.uv;

            return o;
        }

        fixed4 frag(v2f i) : SV_Target
        {
            float3 N = normalize(i.normal);
            float3 V = normalize(_WorldSpaceCameraPos - i.worldPos);

            float fresnelFactor = pow(1.0 - saturate(dot(N, V)), _FresnelPower);

            float shallowFactor = saturate(i.worldPos.y * 0.5 + 0.5);
            float3 waterColor = lerp(_DeepColor.rgb, _ShallowColor.rgb, shallowFactor);

            float3 finalColor = lerp(waterColor, _FresnelColor.rgb, fresnelFactor);

            float foamEdge0 = 0.02;
            float foamEdge1 = 0.10;

            float foamFactorHeight = smoothstep(foamEdge0, foamEdge1, i.waveHeight);

            float slopeFactor = 1.0 - saturate(dot(N, float3(0,1,0)));
            slopeFactor *= 0.5;

            float2 foamUV = i.uv * _FoamScale;
            float foamTex = tex2D(_FoamTexture, foamUV).r;

            float foamFactor = foamFactorHeight + slopeFactor;
            foamFactor *= foamTex;
            foamFactor = saturate(foamFactor * _FoamIntensity);

            finalColor = lerp(finalColor, _FoamColor.rgb, foamFactor);

            return fixed4(finalColor, _Transparency);
        }

        ENDCG
    }
}

FallBack Off

}

Después, se realizó clic derecho sobre el shader para crear un nuevo Material, el cual permitió aplicar todos los efectos visuales configurados anteriormente.

Posteriormente, el material creado fue arrastrado directamente al plano del agua para aplicar el shader sobre la superficie del mar.

Para mejorar aún más la apariencia visual, se ajustaron diferentes valores desde el panel Inspector, modificando propiedades relacionadas con el movimiento de las olas, intensidad, transparencia y color del agua.

Finalmente, el mar quedó configurado con movimiento dinámico y un mejor aspecto visual dentro del escenario.

Sistema de derrota al caer al mar

Con el objetivo de aumentar la dificultad del nivel, se configuró el mar como una zona de derrota para el jugador.

Inicialmente, se activó la opción Is Trigger en el collider del plano del agua, permitiendo detectar cuándo el jugador entra en contacto con esta zona sin bloquear físicamente el movimiento.

Posteriormente, se creó un script llamado AguaMuerte, encargado de reiniciar automáticamente la escena cuando el jugador cae al mar.

using UnityEngine;
using UnityEngine.SceneManagement;

public class AguaMuerte : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
SceneManager.LoadScene(
SceneManager.GetActiveScene().buildIndex
);
}
}
}

Después, el script fue arrastrado al plano del agua para activar el sistema de detección dentro de la escena.

De esta manera, cada vez que el jugador cae al mar, el nivel se reinicia automáticamente.

Finalmente, para incrementar la dificultad del juego, el mismo script fue agregado también al enemigo. Además, se aumentó su velocidad de movimiento para hacer más complicada la recolección de tesoros y generar mayor presión sobre el jugador durante la partida.

Objetos recogibles

Para implementar los objetos recogibles del nivel 2, inicialmente se buscó e importó un modelo 3D desde la plataforma Sketchfab utilizando el recurso disponible en el enlace https://skfb.ly/6TFCB. Posteriormente, el modelo fue agregado dentro del escenario y configurado como objeto coleccionable para que el jugador pudiera recogerlo durante la partida mientras evade al enemigo que lo persigue constantemente.

Distribución de cofres

Finalmente, se distribuyeron un total de cinco cofres en diferentes zonas del mapa con el objetivo de incentivar la exploración del escenario y aumentar la dificultad del nivel mientras el jugador evade constantemente al enemigo que lo persigue.

Se crea un objeto padre para todos los cofres

Se les añade un collider

Por ultimo se activa la opción  Is Trigger

Script de objeto recogible

Posteriormente, se creó un script llamado ObjetoRecogible1, encargado de detectar cuándo el jugador entra en contacto con los cofres u objetos coleccionables del nivel. Mediante el uso de un Trigger, el sistema identifica al jugador utilizando la etiqueta Player y, al producirse la colisión, muestra un mensaje de confirmación y elimina automáticamente el objeto de la escena para simular que fue recogido correctamente.

using UnityEngine;

public class ObjetoRecogible1 : MonoBehaviour
{
public int valor = 1;

private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
Debug.Log("Objeto recogido");

Destroy(gameObject);
}
}
}

Contador de objetos recogidos

Para mostrar el progreso del jugador durante el nivel, inicialmente se creó un texto dentro del Canvas utilizando TextMeshPro, el cual se encarga de visualizar en pantalla la cantidad de objetos recogidos.

Posteriormente, se desarrolló un script llamado GameManager, encargado de administrar el contador de objetos recolectados dentro del nivel. Este script actualiza constantemente el texto mostrado en pantalla y verifica si el jugador ya recogió la cantidad necesaria de cofres para completar el nivel.

Además, cuando el jugador alcanza el número de objetos requeridos, el sistema carga automáticamente el siguiente nivel del videojuego.

using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;

public class GameManager : MonoBehaviour
{
public static GameManager instancia;

public int objetosRecogidos = 0;

public int objetosNecesarios = 5;

public TextMeshProUGUI textoObjetos;

public string siguienteNivel;

void Awake()
{
instancia = this;
}

void Update()
{
textoObjetos.text =
"Objetos: " +
objetosRecogidos +
"/" +
objetosNecesarios;

if(objetosRecogidos >= objetosNecesarios)
{
SceneManager.LoadScene(siguienteNivel);
}
}

public void SumarObjeto()
{
objetosRecogidos++;
}
}

Finalmente, se creó un objeto vacío (Empty GameObject) dentro de la escena llamado GameManager, el cual se utilizó para centralizar la lógica principal del nivel. Posteriormente, se le asignó el script GameManager anteriormente creado, permitiendo administrar el contador de objetos recogidos, actualizar el texto en pantalla y controlar automáticamente el cambio al siguiente nivel cuando el jugador completa el objetivo.

Nivel 3: No caer

El tercer nivel del videojuego fue diseñado con una temática de parkour, donde el principal objetivo del jugador consiste en avanzar a través de diferentes plataformas sin caer al vacío.

Inicialmente, se buscó un modelo 3D de parkour desde la plataforma Sketchfab utilizando el recurso disponible en el enlace https://skfb.ly/pDMnU.

Posteriormente, el modelo fue importado dentro de Unity y agregado a la escena del proyecto para comenzar la construcción del nivel.

Después, se realizaron diferentes ajustes de posición, escala y rotación sobre el escenario con el fin de adaptar correctamente el mapa a la jugabilidad deseada y definir el recorrido que debe completar el jugador.

Además, se organizaron las plataformas y obstáculos estratégicamente para aumentar la dificultad del nivel, obligando al jugador a calcular correctamente los saltos y movimientos para evitar caer.

Finalmente, el nivel fue configurado como una prueba de habilidad y precisión, ofreciendo una experiencia más desafiante y dinámica dentro del videojuego.

Configuración de colliders en el nivel

Posteriormente, se añadieron los componentes Collider a los diferentes objetos y plataformas del escenario con el fin de permitir una correcta interacción física dentro del nivel. Gracias a esta configuración, el jugador puede desplazarse sobre las estructuras del mapa sin atravesarlas, mejorando la jugabilidad y permitiendo realizar correctamente los saltos y movimientos del parkour.

Se crea un plano y se le añade un material, es este caso se busco una imagen del espacio

Sistema de derrota y pantalla final

Para detectar cuándo el jugador cae fuera de las plataformas, se reutilizó el mismo script implementado anteriormente en el nivel 2 para el agua. De esta manera, al entrar en contacto con la zona de caída, el sistema reinicia automáticamente la escena, obligando al jugador a comenzar nuevamente el recorrido del parkour.

Finalmente, después de completar el nivel 3, se creó un Canvas similar al utilizado en el menú inicial del videojuego. Este canvas se encargó de mostrar la pantalla final del juego, indicando que el jugador logró completar correctamente todos los niveles.

Música de los niveles

Con el fin de mejorar la ambientación y generar una experiencia más inmersiva dentro del videojuego, se buscaron diferentes efectos de sonido y música de fondo para acompañar cada uno de los niveles del juego. Posteriormente, los audios fueron importados a Unity y configurados dentro de la escena para reproducirse durante la partida según el nivel correspondiente.

https://pixabay.com/es/sound-effects/search/musica%20juego

Implementación de música en el videojuego

Con el fin de mejorar la ambientación y experiencia del jugador dentro del videojuego, inicialmente se creó una carpeta llamada Música dentro de la carpeta Assets del proyecto para organizar correctamente los archivos de audio utilizados en los diferentes niveles.

Posteriormente, los archivos de música fueron importados dentro de esta carpeta y luego se creó un objeto de audio en la escena utilizando un componente Audio Source, encargado de reproducir la música de fondo durante la partida.

Posteriormente, se activó la opción Loop dentro del componente de audio para que la música se reprodujera continuamente durante la partida, y este mismo procedimiento fue repetido en las demás escenas del videojuego para mantener ambientación sonora en todos los niveles.

File Build Profile

En este apartado se verifica que todas las escenas del proyecto estén correctamente listadas y organizadas en el orden adecuado dentro del Build Profile. Esto es importante para asegurar que el juego cargue de manera correcta durante su ejecución.

Posteriormente, en el escritorio se crea una nueva carpeta con el nombre “Exe”, la cual se utilizará como destino del archivo ejecutable del juego.

Finalmente, se selecciona la opción Build y, en la ventana de configuración, se elige la carpeta creada anteriormente como ubicación de salida. Al completar este proceso, se genera el ejecutable del juego dentro de dicha carpeta, permitiendo su ejecución de forma independiente.

Créditos:

Autor: Oscar Eduardo Pacheco Sanchez

Editor: Carlos Ivan Pinzon Romero

Código: UCMVG1-9

Universidad: Universidad Central

Referencias:

Adobe. (2024). Mixamo. Mixamo

Dev Rychz. (2025, 27 de marzo). Como hacer un enemigo que te persiga con RANGO en Unity || Enemigo te persigue || Tutorial 2025 [Video]. YouTube. YouTube

Pixabay. (s. f.). Música de juego: efectos de sonido y música libre de derechos. Pixabay. Pixabay

iRulosso. (2024, 28 de abril). ¡Cómo crear un personaje en Unity 3D! (Fácil y rápido) [Video]. YouTube. YouTube

Kostasdev. (2020, 20 de septiembre). Animaciones GRATIS para tu juego en UNITY! | MIXAMO [Video]. YouTube. YouTube

LuisCanary. (2025, 9 de junio). Como hacer un MENU PRINCIPAL en Unity /Main Menu/ Facil y Sencillo para 2D y 3D [Video]. YouTube. YouTube

Sketchfab. (2024). Sketchfab: 3D models platform. Sketchfab

Unity Technologies. (2024). Unity documentation: NavMesh Agent. Unity Documentation. Unity Documentation

Unity Technologies. (2024). Unity documentation: PlayerPrefs. Unity Documentation. Unity Documentation