Modelado

Desarrollo completo de un juego backrooms en Unity

Los juegos del tipo backrooms se basan en escenarios laberínticos y atmósferas tensas donde el jugador es perseguido constantemente.

Vamos a hacer un desarrollo completo de un Juego Backrooms en Unity e Implementar esta experiencia inmersiva requiere sistemas robustos de generación procedural, navegación con IA, y controles de jugador refinados. Este artículo abarca en detalle cada componente crítico, apuntando a una comprensión profunda de la arquitectura del juego, basado en Unity y C#.


1. Generación Procedural de Mapas con CreadorEscenarios

La generación procedural de mapas es el núcleo para crear espacios dinámicos e infinitos. La clase CreadorEscenarios se encarga de crear nuevos escenarios alrededor del actual en las direcciones arriba, abajo, izquierda y derecha, empleando tamaños que detecta automáticamente mediante el MeshRenderer, o usa valores predeterminados si no se encuentra.

Lógica de Generación y Posicionamiento

Mediante un enum Posiciones, el sistema enumera las direcciones disponibles:

public enum Posiciones { Arriba, Abajo, Izquierda, Derecha }

Luego, con BuclePosiciones(), se iteran las direcciones para crear los nuevos mapas:

public void BuclePosiciones()
{
foreach (Posiciones posicion in Enum.GetValues(typeof(Posiciones)))
{
GenerarEscenario(posicion);
}
}

Cada escenario se instancia en un offset de la posición actual en la dirección correspondiente usando las dimensiones calculadas para mantener continuidad.

Manejo de Trigger para Generación Dinámica

Al entrar el jugador en un mapa, el trigger gestiona actualizar el índice de mapa que tocará generarse, limpiar mapas anteriores para optimización, y crear nuevos mapas alrededor:

private void OnTriggerEnter(Collider other)
{
if (!other.CompareTag("Player")) return;

borrarEsteMapa = false;

var gm = GameManager.instancia;
gm.CambiaIndiceMapa(siguienteMapa);
gm.BorrarMapasAlrededor();

BuclePosiciones();
}

Este enfoque permite un mundo en expansión que se siente vivo y sin límites definidos.


2. Inteligencia Artificial del Enemigo (EnemyAI)

El enemigo que persigue al jugador ofrece la tensión principal del juego. Su IA combina navegación usando NavMeshAgent, detección visual, animación, y audio ambiental.

Componentes Clave

  • NavMeshAgent: Permite movimiento inteligente y navegación en el escenario generado, manejando rutas y obstáculos.
  • Animador: Controla animaciones fluidas de caminar y correr con interpolaciones suaves (Blend Tree).
  • AudioSource: Reproduce sonidos de detección, proximidad y pasos sincronizados con la velocidad.

Patrullaje y Persecución

El sistema alterna entre patrullar puntos específicos y perseguir al jugador si este entra en su rango de visión definido por un ángulo y distancia:

public float visionRange = 50f;
public float visionAngle = 90f;
public LayerMask obstacleMask;

La función CanSeePlayer() usa raycasting para confirmar línea de vista directa:

bool CanSeePlayer()
{
Vector3 dir = (player.position - transform.position).normalized;
if (Vector3.Angle(transform.forward, dir) > visionAngle * 0.5f) return false;

float distance = Vector3.Distance(transform.position, player.position);
if (distance > visionRange) return false;

if (Physics.Raycast(transform.position, dir, out var hit, distance, obstacleMask))
{
if (!hit.collider.CompareTag("Player")) return false;
}
return true;
}

Transición de Estados

Cuando detecta al jugador, inicia persecución:

void StartChase()
{
if (isChasing) return;

alertMessageTimer = alertMessageDuration;

if (audioSource != null)
{
AudioClip clip = GetDetectionClip();
audioSource?.PlayOneShot(clip);
}

isPatrolling = false;
isChasing = true;

agent.ResetPath();
agent.speed = chaseSpeed;
agent.acceleration = chaseAcceleration;
agent.stoppingDistance = 0.5f;
agent.isStopped = false;

currentAnimationSpeed = Mathf.InverseLerp(0f, chaseSpeed, agent.speed);
animator.SetFloat("Speed", currentAnimationSpeed);
}

Al perder de vista al jugador, vuelve a patrullar:

void StopChase()
{
if (!isChasing) return;

isChasing = false;
agent.ResetPath();
agent.acceleration = 8f;

if (patrolPoints != null && patrolPoints.Count > 0)
{
isPatrolling = true;
currentPatrolTimer = 0f;
SetNewPatrolDestination();
}
else
{
isPatrolling = false;
agent.speed = idleSpeed;
agent.isStopped = true;
}

currentAnimationSpeed = Mathf.InverseLerp(0f, chaseSpeed, agent.speed);
animator.SetFloat("Speed", currentAnimationSpeed);
}

Audio Ambiental y Feedback Visual

El enemigo reproduce sonidos de terror aleatorios cuando está cerca, así como sonidos específicos al detectar al jugador, aumentando la inmersión:

private void TryPlayProximitySound()
{
if (player == null || audioSource == null || proximitySounds == null || proximitySounds.Count == 0) return;
if (proximitySoundTimer > 0f) return;

float dist = Vector3.Distance(transform.position, player.position);
if (dist > proximitySoundRange) return;

AudioClip clip = proximitySounds[Random.Range(0, proximitySounds.Count)];
audioSource.PlayOneShot(clip);
proximitySoundTimer = proximitySoundCooldown;
}

También sincroniza sonidos de pasos con la velocidad real del enemigo.

Colisiones y Fin del Juego

Cuando colisiona con el jugador se activa el fin del juego, pausando la IA y mostrando mensaje visual.


3. Gestión Dinámica del Enemigo con EnemyTeleportManager

Cada vez que se genera un nuevo mapa, el enemigo se teletransporta a un punto de spawn valido o a uno aleatorio de los waypoints del mapa, asegurando que esté siempre cerca para mantener la presión sobre el jugador.

public void HandleMapaCreado(GameObject nuevoMapa)
{
if (enemy == null || nuevoMapa == null) return;

List<Transform> waypoints = ObtenerWaypoints(nuevoMapa);
Vector3 destino = CalcularDestino(nuevoMapa, waypoints);

var navAgent = enemy.GetComponent<NavMeshAgent>();
if (navAgent != null)
{
navAgent.Warp(destino + Vector3.up * warpYOffset);
}
else
{
enemy.transform.position = destino;
}

if (waypoints.Count > 0)
{
enemy.SetPatrolRoute(waypoints);
}
}

4. Control del Jugador con PlayerMovement

El jugador se mueve usando un CharacterController que facilita el manejo de colisiones y gravedad. La cámara principal influye en la dirección del movimiento para una experiencia natural e intuitiva.

Movimiento y Rotación

Las entradas horizontales y verticales se traducen a un vector de movimiento relativo a la orientación de la cámara, lo que permite que el jugador se mueva en la dirección hacia la que mira.

Vector3 forward = cam.forward;
Vector3 right = cam.right;
forward.y = 0f; right.y = 0f;
forward.Normalize(); right.Normalize();

Vector3 move = forward * z + right * x;
controller.Move(move * speed * Time.deltaTime);

La rotación suave del modelo 3D del jugador se ajusta para que mire en la dirección que camina.

Animaciones y Sonido de Pasos

Un Animator con blend trees ajusta animaciones para que la marcha y la carrera se vean fluidas. Se emiten sonidos de pasos aleatorios en intervalos sincronizados con la velocidad y estado de movimiento.

Salto y Gravedad

El salto se calcula con una fórmula física básica para altura y la gravedad se aplica manualmente, actualizando la velocidad vertical cada frame.

Derrota

Cuando el jugador entra en un trigger de derrota, se muestra un mensaje en pantalla y se reproduce un sonido para aumentar la inmersión en la experiencia.


5. Cámara con SimpleCameraFollow 

La cámara sigue al jugador desde atrás con un offset configurable y permite rotación alrededor del jugador usando movimiento del ratón.

float mouseX = Input.GetAxis("Mouse X");
rotationY += mouseX * mouseSensitivity * Time.deltaTime;
Quaternion rotation = Quaternion.Euler(0, rotationY, 0);

Vector3 desiredPos = target.position - rotation * Vector3.forward * distance + Vector3.up * height;
transform.position = desiredPos;
transform.LookAt(target.position + Vector3.up * (height * 0.5f));

Esta configuración garantiza que la cámara ofrezca buena visibilidad con una sensación natural de control.


6. Gestión de Mapas y Ciclo de Juego con GameManager

GameManager actúa como coordinador maestro para creación y destrucción de mapas, manteniendo un número controlado para no saturar los recursos.

Almacenamiento y Selección de Mapas

Mantiene una lista de mapas alrededor del jugador, y elimina aquellos que no se están usando para liberar memoria.

Selección de Prefabs y Generación

Decide si generar un mapa final especial o un mapa aleatorio o forzado según condiciones como cantidad ya generada.

Comunicación entre Componentes

Notifica al teletransportador para adaptar rutas del enemigo al nuevo mapa generado.


7. Menú Principal para Control de Escena

Un menú sencillo permite iniciar el juego o salir, con validación para evitar errores como no definir la escena destino correctamente.

8. Modelado y Creación del Personaje Principal

El desarrollo visual y la fidelidad del personaje principal es fundamental para crear una experiencia inmersiva en juegos como este tipo backrooms. Para este proyecto, el modelado y diseño del personaje reutiliza y adapta recursos del segundo corte anteriores, integrando creativamente diversas herramientas y librerías especializadas en modelado 3D, texturizado facial, y animación.

Reutilización y Adaptación del Escenario de Blender

Se partió del escenario base desarrollado en Blender durante el segundo corte del proyecto, adaptando algunos de sus componentes para optimizar tanto el performance como la coherencia visual. Este proceso permitió aprovechar activos ya generados sin necesidad de recrear desde cero, lo que agilizó el flujo productivo.

Blender es popular en la industria debido a su potencia para modelado poligonal, esculpido y creación de texturas, todos aspectos esenciales para crear entornos y personajes detallados. La integración eficiente entre Blender y Unity facilita importar estos modelos 3D de alta calidad.

Obtención de Modelos en Sketchfab

Los modelos de personajes, incluyendo el protagonista y posibles NPCs, provienen de Sketchfab, una plataforma que ofrece tanto modelos gratuitos como de pago listos para uso en tiempo real. La colección usada se seleccionó priorizando características como rigging y compatibilidad con motores de juego.

Sketchfab ofrece descargas en formatos amigables para Unity (.fbx, .obj), lo que facilita la integración directa. Además, muchos modelos están optimizados para animaciones y soporte de morph targets (deformaciones faciales).

Texturizado Facial con FaceBuilder

Para añadir realismo a las expresiones faciales, se utilizó FaceBuilder, una herramienta especializada en reconstrucción facial 3D a partir de fotos. Esto permitió crear rostros con proporciones naturales y alineadas con el concepto artístico, mejorando la conexión emocional del jugador con el personaje.

FaceBuilder es compatible con Blender y exporta modelos con mapas UV listos para rigging, complementando el workflow de personajes.

Animaciones con Mixamo

Finalmente, para dotar de vida al personaje, las animaciones fueron creadas y adaptadas en Mixamo, una plataforma de Adobe que proporciona rigging automático y una gran cantidad de animaciones predefinidas para personajes humanos.

Mixamo permite aplicar animaciones como caminar, correr, saltar y varios ciclos de movimiento con transiciones suaves, exportando datos que Unity puede importar y usar con su sistema de animaciones (Animator Controller y Blend Trees).

Esta combinación de modelos estáticos (de Sketchfab y Blender), ajustes faciales con FaceBuilder, y animaciones en Mixamo constituyen un pipeline eficiente y ampliamente utilizado en la industria para acelerar desarrollo manteniendo alta calidad visual.


Reflexiones Finales

Este sistema representa un desarrollo completo para un juego de laberinto con atmósfera de supervivencia y tensión, incorporando generación procedural de escenarios, inteligencia artificial avanzada, audio ambiental, animaciones y controles finos de jugador y cámara.

Los sistemas están diseñados para ser modulares y extensibles, permitiendo añadir funcionalidades como más tipos de enemigos, eventos ambientales o efectos sonoros sin comprometer la estabilidad del juego.

Este proyecto sirve como base sólida para crear experiencias inmersivas con tecnologías Unity modernas.

Video

Créditos

Autor: Juan Felipe Ramirez SánchezSergio Numael Linares Ducuara

Editor: Mg. Carlos Iván Pinzón Romero

Código: CG-20252

Universidad: Universidad Central

Fuentes:


Adobe. (s. f.). Mixamo: 3D character animation. Adobe Inc. https://www.mixamo.com​

Autodesk. (2022). Unity vs Unreal Engine vs otros motores: Guía comparativa para desarrolladores de videojuegos. Autodesk, Inc. https://www.autodesk.com​

Brackeys. (2020, 10 de mayo). NavMesh in Unity [Video]. YouTube. https://www.youtube.com​

Epic Games. (2023). Introducción a la inteligencia artificial en videojuegos. En Unreal Engine Documentation. Epic Games. https://docs.unrealengine.com​

Gamedev Academy. (2023, 31 de octubre). Complete guide to Unity procedural generation. GameDev Academy. https://gamedevacademy.org/complete-guide-to-procedural-level-generation-in-unity-part-1​

Gamedev Academy. (2023, 2 de noviembre). Noise generator: Procedural 2D maps Unity tutorial. GameDev Academy. https://gamedevacademy.org/procedural-2d-maps-unity-tutorial