De una Mentira a una Bestia Viva: Forjando un Sistema de Trenes Realista
¡Hola a todos!
Hoy quiero hablarles de un sistema que ha sido tanto una pesadilla como una de las glorias más recientes del desarrollo: el sistema de trenes.
Cuando empecé, el tren en mi juego era una mentira. Un simple objeto moviéndose sobre una línea, sin alma, sin física, sin inteligencia. Se trababa en las curvas, los vagones parecían flotar desconectados y, francamente, rompía toda la inmersión. Sabía que tenía que hacer algo más que un "apaño". Mi objetivo era claro: el tren no debía ser un adorno, debía sentirse como una entidad viva y pesada, consciente de su entorno.
Y así comenzó un viaje de desarrollo lleno de iteraciones, frustraciones y, finalmente, soluciones de las que estoy increíblemente orgulloso.
El Primer Desafío: El Movimiento Base
El primer paso era lograr que el tren siguiera la vía de forma suave. Un simple transform.Translate no iba a funcionar, especialmente en las curvas. La solución fue un sistema de Waypoints y Curvas Bezier. Cada tramo de la vía se define por un waypoint de inicio y uno de fin, con un "punto de control" opcional para crear curvas suaves.
El TrainController (el cerebro de la locomotora) no se teletransporta, sino que calcula su progreso (t, un valor de 0 a 1) a lo largo de cada segmento. Esto nos dio un movimiento base perfecto.
El problema fue que los vagones, al intentar seguir a la locomotora con su propia lógica de suavizado, generaban un "stuttering" horrible, como si estuvieran luchando contra un resorte invisible. La solución fue centralizar: el
Nace la "Física Cinemática"
Un tren no es solo un objeto que se mueve a velocidad constante. Es una bestia de metal de miles de toneladas. Para simular esto sin usar el motor de físicas de Unity (que puede ser caótico con objetos tan largos y complejos), desarrollé un sistema de "física cinemática".
En cada FixedUpdate, el script ApplyForcesAndCalculateSpeed calcula una fuerza neta. Esta no es una fuerza real de Rigidbody, sino un valor que determina la aceleración del tren. Considera:
- Potencia del Motor: Basada en el <span class="inline-code ng-star-inserted" <throttlenotch<="" span="">>span class="ng-star-inserted"> (la palanca de potencia).</span>
- Masa Total: Suma la masa de la locomotora y todos sus vagones. Más vagones = aceleración más lenta.
- Resistencia: Se simula la resistencia del aire (que aumenta con el cuadrado de la velocidad) y la fricción extra en las curvas.
- Gravedad: El tren ahora reacciona a las pendientes. Se acelera en las bajadas y le cuesta horrores subir una pendiente pronunciada, tal como en la vida real.
El resultado es un tren que tiene inercia. Se siente pesado, le cuesta arrancar y no se detiene al instante. ¡Empezaba a tener alma!
El Gran Salto: El Sistema Previsor
Aquí es donde todo cambió. El mayor problema del realismo era el frenado. Un tren no clava los frenos justo al llegar a la estación. Un maquinista anticipa la parada desde kilómetros antes. ¿Cómo replicar eso?
Mi primer intento (un desastre) fue usar triggers simples. El tren entraba en una zona y empezaba a frenar. El resultado era un frenado brusco y poco natural.
La solución fue darle "ojos" al tren. Nació el método LookAheadAndPlan().
En cada ciclo de física, el TrainController ahora escanea la vía hacia adelante (unos 500 metros virtuales). Busca el próximo RailWaypoint marcado como isStoppingPoint (un punto de parada).
Cuando encuentra uno, no frena inmediatamente. En su lugar, empieza a calcular en tiempo real:
- Distancia Restante: Mide los metros exactos que le quedan para llegar a ese punto de parada.
- Distancia de Frenado Necesaria: Calcula cuántos metros necesitaría para detenerse por completo desde su velocidad actual, usando la fórmula d = v² / (2a).
- La Decisión: En el momento exacto en que la distancia restante es menor o igual a la distancia de frenado necesaria (más un pequeño margen de seguridad), el tren cambia su estado a "Braking" y comienza a aplicar los frenos de forma progresiva y suave.
El resultado es mágico. Un tren que viene a toda velocidad por una recta larga empezará a frenar mucho antes que uno que se acerca lentamente a la misma estación. Es orgánico, es realista, es previsor.
Dando Vida al Entorno: Estaciones y Semáforos Inteligentes
Con el sistema previsor funcionando, el resto de la lógica se simplificó enormemente. Creé un único script, Signal.cs, que puede actuar como una estación o un semáforo.
- Si es una Estación, tiene un waitTime configurable.
- Si es un Semáforo, tiene un estado Red/Green.
El TrainController, cuando escanea la vía y encuentra un StoppingPoint, simplemente le pregunta al Signal asociado: "¿Tengo que parar?". El Signal responde basándose en sus reglas (si es una estación, si no hay que saltársela, o si es un semáforo y no está en verde).
Además, para darle más vida, el Signal ahora tiene estilos visuales: un semáforo de luces moderno (rojo, amarillo, verde) o una señal de brazo mecánica de estilo inglés, que rota suavemente a su posición. Y lo mejor de todo, al TrainController no le importa cómo se ve; solo le interesa la lógica.
El Tren Reacciona: Detección de Obstáculos
Finalmente, para completar la IA, le dimos al tren la capacidad de reaccionar a imprevistos.
- El TrainController ahora lanza un SphereCast (un "rayo gordo") hacia adelante.
- Si detecta un objeto en una capa configurable ("Obstacle", por ejemplo, donde pueden estar el jugador o vehículos) a una distancia lejana (ej. 150m), activa un modo de alerta: toca la bocina intermitentemente y hace un juego de luces.
- Si el obstáculo sigue ahí y entra en una distancia crítica (ej. 40m), el tren activa el freno de emergencia. Esto aplica la máxima fuerza de frenado y dispara efectos visuales, como un sistema de partículas de chispas en las ruedas, y un sonido de frenado mucho más violento.
Lo que Viene
Nada de esto existía hace unas semanas. Pasamos de un cubo sobre raíles a una bestia de acero semi-inteligente que respira inercia. Pero el viaje no ha terminado. Los próximos pasos en nuestra lista son igual de emocionantes:
- Arreglar el bug del Un pequeño demonio que se nos ha quedado pendiente.
- Generador de Vías Procedural: Crear una herramienta para que las vías y los durmientes se generen automáticamente, siguiendo las curvas a la perfección.
- Interacción con el Jugador: ¡El gran objetivo! Implementar un sistema para que el jugador pueda subir y bajar del tren (integrándolo con Invector), caminar por los vagones e incluso sujetarse del estribo mientras está en movimiento.
Ha sido un camino largo y lleno de errores (créanme, muchos errores), pero ver al tren frenar suavemente ante un semáforo en rojo que él mismo ha previsto, para luego arrancar cuando cambia a verde... es una de esas satisfacciones que hacen que todo el esfuerzo valga la pena.
¡Gracias por leer y seguir el desarrollo
Get Bienvenidos a la Matanza - San Justo City
Bienvenidos a la Matanza - San Justo City
Se parte del desarrollo y conoce los avances!
Status | In development |
Author | Esteban |
Genre | Action |
Tags | argentina, Crime, Driving, police, revenge, Sandbox, solo-dev, Story Rich, Third-Person Shooter, Urban |
Leave a comment
Log in with itch.io to leave a comment.