CP

InicioCaso de estudio

Enigma

Aventura gráfica en tercera persona, con puzzles de entorno y exploración narrativa.

Introducción

Enigma es mi proyecto individual de la tecnicatura. Tiene cuatro niveles y cinco salas, y cada objeto se puede usar solo si el paso anterior está hecho. Quería que el lugar le respondiera siempre al jugador: si se equivoca, le dice qué falta; si acierta, la sala cambia. Acá cuento cómo lo diseñé, cómo lo programé y qué problemas tuve que resolver.

Problemas que resolví

  • La palanca quedaba torcida. El éxito llegaba mientras todavía rebotaba el intento anterior, y StopCoroutine corta la corrutina sin ejecutar el finally. Ahora el éxito espera a que termine el rebote.
  • Los pasos sonaban desfasados porque los disparaba un temporizador, sin relación con la animación. Los pasé a que suenen cuando el talón toca el piso. Para elegir el umbral, medí antes la curva del tobillo.
  • Había paredes invisibles junto a los muebles: los colliders de los bloques de prueba eran más grandes que los modelos. Ajusté cada collider a lo que se ve.
  • La batería aparecía de la nada, porque se activaba sin que se abriera nada. La puse en un archivero, con un cajón que se abre. Desde entonces, todo ítem sale de algo que se vea abrirse.

Código

Interacción por SphereCast

Busca el objeto más cercano en la dirección de la cámara, aunque sea chico. Solo lo toma si en ese momento se puede intentar.

Ray ray = new Ray(rayCamera.transform.position, rayCamera.transform.forward);
int count = Physics.SphereCastNonAlloc(ray, aimRadius, _hits, range, _layerMask, QueryTriggerInteraction.Collide);
if (count > 1)
    System.Array.Sort(_hits, 0, count, HitDistanceComparer.Instance);
for (int i = 0; i < count; i++)
{
    var interactable = _hits[i].collider.GetComponentInParent<IInteractable>();
    if (interactable == null || !interactable.CanAttemptInteract(BuildContext()))
        continue;
    // ...
}

Estados por nivel con eventos

Cuando un estado cambia, puertas, luces, cajones y tomas de cámara reaccionan sin conocerse entre sí. Ningún paso apaga un estado: por diseño, solo se prenden.

public event Action<string, bool> OnFlagChanged;
// La interfaz, las luces y las puertas reaccionan cuando cambia un flag.
public void Set(string flagId, bool value)
{
    if (string.IsNullOrEmpty(flagId))
        return;
    _flags[flagId] = value;
    OnFlagChanged?.Invoke(flagId, value);
    // Notifica a quien escuche, por ejemplo la luz de la puerta cuando power_active.
}

Cada nivel guarda sus estados, y esos estados solo se prenden. Puertas, luces, cajones y cámaras se suscriben al evento, así que ningún objeto necesita conocer a los demás.

IA de la cámara de seguridad

Es una máquina de estados, con cono de visión y línea de vista. Sigue al jugador y pasa a alerta si lo ve sin obstáculos.

bool inCone = InCone();
bool seen = inCone && HasLineOfSight();
// ...
if (_state == State.Track)
{
    FacePlayer();
    if (!inCone)
        _state = State.Idle;
    else if (seen && _cool <= 0f)
        EnterAlert();
    return;
}

Qué aprendí

  • Verifico en la escena, con capturas desde la cámara del juego, y no me quedo solo en el código.
  • Que un objeto exista no alcanza para saber si se ve.
  • No corto con StopCoroutine algo que limpia en un finally.
  • El mensaje de error también se diseña. Si te equivocás, el juego tiene que orientarte, no castigarte.

Bugs, pendientes y mejoras

Bugs conocidos

  • En el N4, el collider del cassette es más alto que el modelo, así que se puede apuntar por encima de la cinta. Voy a ajustarlo a lo que se ve, como hice con el resto de los objetos.
  • Los textos que están sobre objetos del mundo no reciben luz. En los lugares oscuros, la tinta se ve más clara que el papel. Voy a pasarlos a un material que reaccione a la iluminación de la sala.

Incompleto

  • Todavía no hay guardado. Los estados del nivel no sobreviven a una recarga: si cerrás el juego, empezás de cero. Voy a guardar al menos el nivel alcanzado.
  • Tampoco hay menú de opciones. El mixer ya expone Master, Música, SFX, Ambiente y Voz, así que los volúmenes se pueden conectar directo. Faltan la sensibilidad y el tamaño de los subtítulos.
  • En el entrepiso, la radio no tiene asignado el clip de estática, así que se prende sin ese sonido.
  • Los finales múltiples no están implementados. El concepto original planteaba tres finales, como diseño a futuro.
  • No hay una pista opcional para quien se traba. Quiero sumar una después de varios fallos seguidos.

A corregir o mejorar

  • Luces sin fuente visible: en varios niveles hay luces que iluminan sin una lámpara, baliza o fuego que las justifique. Voy a sumar las fuentes que faltan o sacar las luces que sobren.
  • En el N2 quedan cuatro luces viejas con intensidad 0. No hacen nada, así que las voy a borrar.
  • Los textos del menú y del N1 se ven chicos porque la referencia es 1920×1080. Voy a escalar la interfaz según la pantalla y subir los tamaños.
  • El candado de flechas muestra < y > porque las fuentes no tienen los glifos de flecha. Puedo sumar una fuente que sí los tenga.
  • El inspector de ítems crea su texto de ayuda en ejecución, en lugar de tenerlo armado en la escena. Lo voy a dejar armado ahí, como el resto de la interfaz.

Próximos pasos

  1. Corregir los bugs que ya conozco: el collider del cassette y los textos del mundo que no reciben luz.
  2. Darles una fuente visible a todas las luces y borrar las que no aportan.
  3. Subir una build web nueva y medir cómo rinde y cuánto tarda en cargar en el navegador.
  4. Sumar un menú de opciones, con volumen por grupo, sensibilidad y tamaño de subtítulos.
  5. Guardar el nivel alcanzado, y agregar una pista opcional después de varios fallos seguidos.
  6. Probar el juego con personas que no lo conozcan.

Presentación del diseño

Presentación del diseño

Diapositiva 1 de 11: Enigma

01 · Título

Enigma

Es una aventura gráfica en tercera persona, de puzzles y exploración narrativa. Alguien sin memoria recorre una instalación subterránea en ruinas. Solo avanza si observa, usa lo que encuentra y reconstruye su pasado.

Lo hice sola. Unity 6, C#, y sigue en desarrollo.

1 / 11

Making of

  • Video próximamente

    La experiencia

    Explorar, investigar y descubrir, con una tensión que se mantiene baja.

  • Video próximamente

    El loop principal

    Explorás, apuntás a un objeto (aparece [E]) y lo intentás.

  • Video próximamente

    Mecánicas y puzzles

    Interacción condicional, un inventario de 8 lugares con inspección 3D y un journal de memorias.

  • Narrativa y decisiones

    Video próximamente

    Narrativa y decisiones

    M. despierta sin memoria.

  • Sistemas en Unity/C#

    Video próximamente

    Sistemas en Unity/C#

    SphereCast, estados por nivel con eventos, y una cámara orbital que choca con el escenario. Las tomas van con Cinemachine.

  • Problemas que resolví

    Video próximamente

    Problemas que resolví

    La palanca quedaba torcida porque StopCoroutine no ejecuta el finally. El éxito ahora espera a que termine el rebote.

  • Qué aprendí

    Video próximamente

    Qué aprendí

    Verifico con la cámara del juego, no solo leyendo el código.