Arquitectura - JRuizGil/MeltTheDeep GitHub Wiki
Arquitectura
TDD · Documento técnico
Plataforma y objetivo
| Aspecto | Objetivo |
|---|---|
| Plataforma | PC con Windows 10/11 de 64 bits |
| API gráfica | DirectX 11/12 |
| Resolución | 1920×1080 |
| Rendimiento | 60 FPS estables (16,6 ms por fotograma) en el PC de clase |
El PC de clase es el punto de referencia para todo: si una medida sale bien en mi ordenador y mal en el del aula, vale la del aula.
Versión web (propuesta, pendiente de confirmar con los profesores)
Si los profesores dan el visto bueno, saco también una build para navegador con la plataforma Web de Unity 6 (WebGL 2, y WebGPU si está lo bastante estable), publicada en itch.io o en GitHub Pages. Tiene limitaciones que afectan justo a los sistemas más pesados del juego:
| Tema | Qué cambia en la web | Qué haría |
|---|---|---|
| Hilos | El código C# no se reparte entre varios hilos como en PC, así que los jobs de las cuevas y del remallado van más despacio | Medir la generación de cuevas en la build web desde el principio, porque es mi objetivo de optimización, y bajar la resolución o generar por partes si no llega |
| Memoria | El navegador da menos memoria que un PC | Cargar un módulo cada vez y vigilar el tamaño del campo de densidad |
| Física | Cientos de bolitas cuestan más | Bajar el tope de bolitas activas solo en la versión web |
| Sonido | FMOD tiene soporte para web, pero el navegador no deja sonar nada hasta que el jugador hace clic | Pantalla de inicio con un botón de jugar |
| Descarga | La build tiene que bajarse entera antes de jugar | Texturas comprimidas y bancos de FMOD ligeros |
| Guardado | No hay disco: se guarda en el almacenamiento del navegador | Mismo formato de guardado, con un tope de tamaño |
La versión de PC no se toca por esto: la web sale del mismo proyecto con ajustes de calidad propios.
Herramientas
| Herramienta | Para qué la uso |
|---|---|
| Unity 6 LTS (6000.3.23f1) | Motor del juego |
| URP y Shader Graph | Render y shaders del hielo, el agua, las grietas y las siluetas |
| Input System | Controles de la pistola, el movimiento y el taller |
| Job System, Burst, Collections y Mathematics | Campo de densidad, mallado y generación de cuevas |
| Splines | Carril por el que se mueve el tanque |
| TextMeshPro y UGUI | Interfaz |
| Unity Test Framework | Pruebas EditMode y PlayMode |
| FMOD Studio y su integración para Unity | Todo el sonido |
| Autodesk 3ds Max | Modelado, exportado en FBX |
| Visual Studio | Código C# |
| Git y Git LFS en GitHub | Control de versiones |
| Hoja de cálculo | Balance de costes, tanques y minerales |
| Esta wiki | Documentación |
Código propio
Todo el código lo escribo yo desde cero. Las referencias que he mirado se citan en la página del sistema al que pertenecen y sirven como idea, nunca como código. El único dato que copio tal cual es la tabla de triangulación de marching cubes de Paul Bourke, que también está citada en Sistema de hielo.
Estructura del proyecto
Assets/
_Project/
Scripts/
Core/ Ice/ Caves/ Water/ Gun/ World/
Economy/ Loot/ Fossil/ Audio/ UI/ Save/
Shaders/
ScriptableObjects/
Prefabs/
Materials/
Art/
Scenes/
Tests/
EditMode/
PlayMode/
FMOD/ proyecto de FMOD Studio, en la raíz del repositorio
Cada carpeta de Scripts tiene su propia Assembly Definition y su espacio de nombres: MeltTheDeep.Ice, MeltTheDeep.Caves, MeltTheDeep.Gun y así con el resto. Las dependencias entre ensamblados son explícitas, de modo que si Ice intentara usar algo de Economy el compilador se queja, y los tiempos de compilación bajan porque Unity solo recompila el ensamblado que ha cambiado.
Escenas
| Escena | Para qué sirve |
|---|---|
Boot |
Arranque, carga de guardado y paso a Game |
Game |
El juego completo |
Sandbox_Ice |
Probar el campo de densidad, el fundido y el mallado sin nada más |
Sandbox_Caves |
Generar cuevas con cualquier semilla, ver cortes horizontales y estadísticas |
Sandbox_Water |
Probar bolitas, fusión y rendimiento con cientos de ellas |
Sandbox_Fossil |
Probar coger, girar, voltear y encajar huesos |
Cada sistema difícil tiene su escena de pruebas. Así puedo trabajar en uno sin arrastrar el resto y medir su coste por separado.
Principios
- Componentes pequeños con una sola responsabilidad. Una clase que funde no mezcla el sonido ni la interfaz.
- Los datos viven en ScriptableObjects: minerales, huesos, módulos, perfil de dureza, tabla de mejoras.
- Los sistemas se avisan con canales de eventos hechos con ScriptableObjects (
GameEvent,FloatEvent). La interfaz, el audio y el guardado escuchan sin que quien lanza el evento sepa que existen. PlayerStatsse calcula a partir de los niveles comprados. Nadie lee las mejoras directamente: el chorro pregunta por su potencia aPlayerStatsy no sabe qué mejora la subió.- La lógica pura, como el generador de cuevas o la validación del encaje, no depende de la escena para poder probarse con tests.
Mapa de sistemas
flowchart LR
Input
subgraph arma["Pistola"]
WaterGun["WaterGun (FSM)"]
JetMode
VacuumMode
end
subgraph hielo["Hielo y cuevas"]
CaveGenerator
MineralPlacer
MineralVein
MeltSystem
IceVolume
ChunkMesher
end
subgraph agua["Agua"]
WaterBallPool
WaterBall
AccumulationGrid
end
subgraph botin["Botín"]
LootItem
SpeciesCatalog
FossilInventory
end
subgraph taller["Fósil"]
FossilAssembly
GrabController
end
subgraph economia["Economía"]
Tanks
WaterMarket
UpgradeShop
PlayerStats
end
Eventos["Canales de eventos"]
AudioService
Input --> WaterGun
WaterGun --> JetMode
WaterGun --> VacuumMode
JetMode --> MeltSystem
MeltSystem --> IceVolume
CaveGenerator --> IceVolume
CaveGenerator --> MineralPlacer
MineralPlacer --> MineralVein
JetMode --> MineralVein
IceVolume --> ChunkMesher
IceVolume --> WaterBallPool
WaterBallPool --> WaterBall
WaterBall --> AccumulationGrid
AccumulationGrid --> MeltSystem
VacuumMode --> WaterBall
VacuumMode --> LootItem
VacuumMode --> Tanks
LootItem --> SpeciesCatalog
LootItem --> FossilInventory
FossilInventory --> FossilAssembly
GrabController --> FossilAssembly
Tanks --> WaterMarket
WaterMarket --> UpgradeShop
UpgradeShop --> PlayerStats
PlayerStats --> WaterGun
Eventos -.-> AudioService
El bucle del agua y el del dinero se cierran solos: el hielo fundido da bolitas, las bolitas frenan el fundido de la zona donde se acumulan, el tanque se vende, la venta paga mejoras y las mejoras cambian lo que hace la pistola. Los canales de eventos no aparecen conectados uno a uno para que el diagrama se pueda leer, pero casi todos los sistemas lanzan algún evento y AudioService escucha los suyos.
Componentes
| Componente | Tipo | Responsabilidad |
|---|---|---|
WaterGun |
MonoBehaviour | Máquina de estados de la pistola: Inactivo, Chorro, Aspirar |
JetMode |
Clase de estado | Chorro: temperatura, consumo, raycast y calor sobre vetas |
VacuumMode |
Clase de estado | Aspiradora: selección de objetivos, fuerza y entrada al tanque |
MeltSystem |
MonoBehaviour | Aplica el fundido al campo de densidad y convierte lo fundido en litros |
IceVolume |
Clase C# | Dueña del NativeArray de densidad, de los ids de región y del porcentaje restante |
ChunkMesher |
MonoBehaviour y Job | Marching cubes por chunk y colliders |
HardnessProfile |
ScriptableObject | Curva de dureza por profundidad |
CaveGenerator |
Clase estática | Genera las cuevas de un módulo a partir de una semilla |
MineralPlacer |
Clase estática | Reparte las vetas sobre las paredes de las cuevas |
MineralVein |
MonoBehaviour | Cristales, integridad y grietas de una veta |
MineralDefinition |
ScriptableObject | Tipo, resistencia, tamaño y volumen de un mineral |
WaterBallPool |
MonoBehaviour | Pool de bolitas y límite de activas |
WaterBall |
MonoBehaviour | Nivel, litros y fusión |
AccumulationGrid |
Clase C# | Litros por celda de 1 m para frenar el fundido |
LootItem |
MonoBehaviour | Pez, mineral o hueso recogible |
Tanks |
Clase C# | Tanque de agua caliente y tanque de la aspiradora |
WaterMarket |
Clase C# | Venta del agua y multiplicadores |
UpgradeShop |
Clase C# | Costes, niveles y compra de mejoras |
PlayerStats |
Clase C# | Valores finales calculados desde las mejoras y el fósil |
SpeciesCatalog |
ScriptableObject y datos de partida | Especies existentes y cuáles se han registrado |
BoneDefinition, SkeletonDefinition |
ScriptableObject | Datos de cada hueso y del esqueleto completo |
FossilInventory |
Clase C# | Huesos recogidos y huesos montados |
FossilAssembly |
MonoBehaviour | Armazón, sockets y montaje |
GrabController |
MonoBehaviour | Coger, mover, girar y voltear huesos |
SnapValidator |
Clase estática | Decide si un hueso está bien colocado |
ModuleDefinition, ModuleTracker |
ScriptableObject, MonoBehaviour | Datos de cada módulo y avance de su fundido |
Elevator |
MonoBehaviour | Subir y bajar entre superficie y módulos |
HoseCart |
MonoBehaviour | Tanque sobre el carril, manguera y límite de movimiento |
AudioService |
MonoBehaviour | Escucha eventos y lanza los sonidos de FMOD |
SaveSystem |
Clase C# | Guardar y cargar la partida |
Presupuesto de fotograma
| Sistema | Presupuesto |
|---|---|
| Render | 6,0 ms |
| Física | 4,0 ms |
| Hielo (fundido y remallado) | 3,0 ms |
| Gameplay, UI y audio | 1,5 ms |
| Margen | 2,1 ms |
| Total | 16,6 ms |
La generación de cuevas queda fuera de este presupuesto porque se ejecuta tras la pantalla de carga, no durante el juego. Su tiempo es el principal objetivo de optimización del proyecto, y lo mido con la misma semilla en cada iteración. Estos números son valores iniciales y los reviso con cada captura del Profiler; las medidas van a Planificación.