Cómo dominar el Asset Stripping en Unreal Engine Dedicated Server (Paso a paso)
Inicias tu Unreal Engine dedicated server recién compilado, esperando un proceso ligero y headless. Realizas un memory profile y ahí están: miles de objetos UMaterial, UTexture y USoundWave ocupando la RAM de tu servidor.
La documentación oficial dice que un servidor headless no renderiza visuales. Entonces, ¿por qué tu servidor está acaparando megabytes de datos de texturas?
Cualquier desarrollador indie conoce el momento en que los costes de hosting empiezan a amenazar la viabilidad del proyecto. Cuando una máquina bare-metal solo puede alojar 10 instancias de tu juego en lugar de 50 debido al memory bloat, tu arquitectura de backend está comprometida.
En este análisis profundo, vamos a diseccionar cómo funciona realmente el unreal engine dedicated server asset stripping, por qué los ghost assets permanecen en memoria y cómo puedes diseñar tu C++ y Blueprints para erradicarlos por completo.
La anatomía de un "Ghost Asset" en un Dedicated Server
Para solucionar el problema, primero debes entender qué hace la Unreal Automation Tool (UAT) cuando haces el cook para el target Server.
Cuando un desarrollador ve UTexture o UMaterial en un perfil de memoria, asume que el engine falló al hacer el stripping. Esto es solo parcialmente cierto.
Unreal Engine separa los assets en dos partes:
- El UObject Wrapper: Metadatos, propiedades y datos de reflexión.
- El Bulk Data: La carga pesada real (datos de píxeles comprimidos en DXT para texturas, vertex buffers para meshes, datos PCM para audio).
Al cocinar un dedicated server, el cooker elimina con éxito el bulk data. Los datos de renderizado desaparecen. Sin embargo, el UObject wrapper permanece.
Si un Blueprint Class Default Object (CDO) tiene una hard reference a una UTexture2D, el servidor debe instanciar el UObject UTexture2D para satisfacer el sistema de reflexión y evitar crashes por punteros nulos. Aunque el bulk data se haya eliminado y la textura solo consuma 1KB en lugar de 10MB, el overhead de instanciar 50,000 de estos UObjects se traduce en un memory bloat significativo y carga extra para el Garbage Collection.
¿El Audio y las Partículas siguen la misma lógica?
Sí. Si tienes una hard reference a un USoundCue o un UNiagaraSystem, el servidor cargará el UObject. Los datos pesados de audio PCM se eliminan, pero el objeto existe.
Con los sistemas de partículas, esto es peligroso. Aunque se omita el renderizado visual, si un sistema de partículas contiene lógica ejecutada por CPU (como simulaciones complejas de Niagara o eventos de colisión), el servidor podría ejecutar ese tick de lógica si no se configura correctamente, consumiendo ciclos de CPU y memoria.
Paso 1: Profiling del Memory Bloat del servidor
Antes de empezar a desmantelar tu código, necesitas números concretos. No puedes optimizar lo que no puedes medir.
Lanza tu dedicated server empaquetado con los siguientes argumentos de línea de comandos:
-LLM -LLMCSV -memoryprofiler
Una vez que el servidor esté corriendo y haya empezado una partida, abre la consola del servidor y ejecuta:
memreport -full
Esto genera un archivo .memreport en tu directorio Saved/Profiling/MemReports. Ábrelo y busca Obj List. Verás algo como esto:
Class UTexture2D: 1452 Objects, 1.25 MB
Class UMaterial: 840 Objects, 0.85 MB
Class USoundWave: 620 Objects, 0.45 MB
Aunque 2.5MB no parezca catastrófico, es solo el tamaño del objeto base. Al sumar el memory alignment, los name pool strings y las dependencias en cascada, una pequeña cadena de referencias visuales puede inflar un servidor de 150MB a más de 600MB.
Para más detalles sobre gestión agresiva de recursos, consulta nuestro análisis sobre arquitectura de servidores zero-waste.
Paso 2: Cortar Hard References con Soft Pointers
La causa más común de ghost assets son las hard references. Si tu clase ACharacter tiene un hard pointer a un retrato de UI, el servidor cargará ese asset al spawnear al personaje.
La solución en C++: TSoftObjectPtr
Debes auditar tus headers y reemplazar las referencias visuales/audio por soft references. Estas solo guardan la ruta del asset; el UObject no se carga hasta que llames a LoadSynchronous() o uses el Streamable Manager.
MAL: Hard Reference (Carga en el servidor)
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
UTexture2D* HeroPortrait;
BIEN: Soft Reference (Servidor limpio)
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
TSoftObjectPtr<UTexture2D> HeroPortraitSoft;
En el cliente, verificas el net mode y cargas de forma asíncrona:
void AHeroCharacter::PlayUltimateVFX()
{
if (GetNetMode() == NM_DedicatedServer) return;
if (UltimateAbilityVFXSoft.IsPending())
{
UNiagaraSystem* LoadedVFX = UltimateAbilityVFXSoft.LoadSynchronous();
// ... spawn logic
}
}
La solución en Blueprints
Cambia tus variables de Object Reference a Soft Object Reference. Usa el nodo Async Load Asset antes de utilizar el asset en el cliente.
Paso 3: Forzar NeedsLoadForServer
Puedes indicar al sistema de empaquetado que ignore componentes puramente visuales sobrescribiendo la función NeedsLoadForServer.
bool UMyVisualCustomizationComponent::NeedsLoadForServer() const
{
return false; // Garantiza que el componente se elimine de la memoria del servidor
}
Paso 4: Stripping de Audio y Partículas vía Configuración
En tu DefaultEngine.ini, puedes excluir directorios enteros:
[/Script/UnrealEd.ProjectPackagingSettings]
+DirectoriesToNeverCook=(Path="UI/Widgets")
En tu YourGameServer.Target.cs, puedes desactivar el motor de audio:
bDisableAudio = true;
Buenas prácticas para la optimización de memoria
- Separar Meshes de Colisión de los Visuales: Usa un
UStaticMeshsimplificado e invisible para el servidor. - Auditar Construction Scripts: Usa
Switch Has Authoritypara evitar que el servidor instancie efectos visuales. - Aislar Data Assets: Separa los stats (servidor/cliente) de los visuales (solo cliente).
- Automatizar Profiling en CI/CD: No esperes al lanzamiento para comprobar la memoria.
- Módulos ServerOnly y ClientOnly: Separa físicamente el código para evitar referencias cruzadas.
Escalando tu Backend optimizado
Si logras bajar el footprint de tu servidor de 800MB a 180MB, es una victoria técnica masiva. Podrás alojar 4-5 veces más instancias, reduciendo drásticamente los costes de AWS o Google Cloud.
Pero orquestar esos servidores es complejo. Con horizOn, estos servicios vienen pre-configurados. Sube tu build de Linux optimizada y nosotros nos encargamos del auto-scaling y la orquestación global.
Conclusión
El asset stripping en Unreal Engine es potente pero depende de tu arquitectura. Al migrar a soft pointers y separar datos visuales de los de gameplay, obtendrás el rendimiento que los juegos multiplayer exigen.
¿Listo para escalar? Prueba horizOn gratis o consulta nuestra documentación de API.