Tecnología

Cómo implementamos Asset Studio

AAnonymous
10 min de lectura

Introducción

En la publicación anterior, How We Make Games, expliqué qué tipos de juegos estamos construyendo y en qué orden. En ese texto, Asset Studio apenas apareció. Se mencionó, más o menos, como una herramienta para gestionar juntos los assets de personajes y fondos de Story Quiz y Mystery.

En esta publicación quiero desarrollar esa frase a partir de la implementación real. Asset Studio no es solo una pantalla para generar imágenes. Hoy, en Omnilude, es el lugar donde reunimos assets, ajustamos prompts, ejecutamos trabajos de generación, revisamos los resultados y luego los registramos en la biblioteca. El backoffice del frontend funciona como mesa de trabajo, game-service se encarga del dominio de assets y ai-service se encarga de la ejecución de la generación.

Por qué construimos Asset Studio por separado

Cuando uno empieza a hacer juegos de verdad, los assets se dispersan más rápido de lo que parece. Story Quiz tiene personajes, fondos e imágenes para blocks. Mystery tiene personajes de escenario, ubicaciones e imágenes para notas. Incluso si una sola persona está construyendo un mismo mundo, resulta difícil mantener el control si prompts, presets de estilo, resultados generados, estados de aprobación y uso real se gestionan en pantallas distintas.

Por eso Asset Studio se diseñó desde el principio más como un taller que como una galería. Lo importante es que esta herramienta no pertenece a un solo género. Maneja de la misma forma tanto los assets ya creados en Story Quiz como los assets traídos desde borradores de Mystery, conserva los resultados generados como una biblioteca revisable y después rastrea si esos assets se están usando en otros dominios. Sin esa perspectiva, la generación con IA puede sentirse cómoda, pero no termina convirtiéndose en un asset operativo.

Cómo está organizada la pantalla de backoffice

El backoffice del frontend tiene rutas dedicadas para Asset Studio. Fondos y personajes se abren desde entradas diferentes, pero por dentro comparten los mismos componentes de trabajo. La pantalla de listado soporta vista de árbol y vista plana, y también muestra los elementos editados recientemente. Es decir, se puede trabajar desde la estructura de directorios o volver a entrar desde la actividad reciente.

Cuando se entra a la pantalla de detalle, la estructura se vuelve mucho más clara. En la práctica, esta pantalla es una consola completa de producción de assets. Arriba se gestionan el nombre y el estado de aprobación. En el cuerpo se trabajan, por pestañas, las fuentes de importación, los prompts, la generación, los medios y el historial. En la implementación se puede ver que el estado de búsqueda de las fuentes importadas y el estado de acciones de los medios generados se mantienen a nivel de página. La idea es no perder contexto al cambiar de pestaña.

Aquí hay una separación importante. Las APIs de gestión de assets, como listar elementos, modificar directorios o registrar resultados generados, van todas a game-service a través de /api/asset-studio/*. En cambio, la petición de generación real no pasa por la API de Asset Studio. Va a ai-service a través de /api/media-generation/generate. Esa separación es intencional. La gestión de assets y la ejecución de la generación viven en la misma pantalla, pero no comparten la misma responsabilidad.

Cómo fluye realmente la generación

La experiencia de uso es bastante simple. Se abre un asset, se trae un asset existente o una fuente de Story Quiz o Mystery, se ajusta el prompt y se pulsa generar. Pero por dentro el flujo está dividido así.

  1. La pantalla de detalle del backoffice lee el assetItem.generationConfig actual.
  2. La petición de generación se envía a /api/media-generation/generate y el frontend recibe un jobId de inmediato.
  3. El frontend se suscribe al stream SSE de backbone con ese jobId y recibe eventos de progreso.
  4. ai-service crea el resultado real mediante el provider y lo sube a storage.
  5. El frontend recibe outputUrl y sourceUrl por SSE y muestra una vista previa.
  6. Si el resultado parece válido, el usuario lo registra entonces a través de /api/asset-studio/asset-items/{id}/media.

Ese último paso importa mucho. Asset Studio no guarda el resultado en la biblioteca apenas termina la generación. El resultado generado se trata primero como algo que debe revisarse. Solo se convierte en un asset operativo cuando el usuario pulsa la acción de registro. Eso también queda claro en la implementación. El hook de generación construye un estado de vista previa a partir de los eventos SSE, y una acción separada llama a createGeneratedMediaApi para mover el resultado a la biblioteca.

Gracias a esa estructura, Asset Studio no es una caja de prompts. Es una línea de producción con revisión incorporada. Los resultados fallidos, dudosos o producidos por un pipeline de posprocesamiento pueden quedarse en estado temporal hasta que alguien decida registrarlos.

Qué le corresponde a game-service

El Asset Studio de game-service no es solo un paquete de CRUD. En la práctica está mucho más cerca de ser el dueño de la biblioteca de assets. Incluso a nivel de controller, directorios, ítems, importación, export/import portability, generated media, selección de winner, reorder, bulk move y consulta de import sources están agrupados en un mismo dominio.

Hay tres razones por las que este servicio importa especialmente.

1. Toma fuentes desde dominios de juego ya existentes

AssetStudioImportService lee DraftLocation y DraftCharacter de Mystery, y StoryQuizBackgroundAsset y StoryCharacter de Story Quiz, y los convierte en tarjetas de fuentes importables. No trae solo nombres. También reúne prompt, provider, stylePreset y metadata. Eso hace que Asset Studio no sea un lugar donde cada asset empieza desde cero, sino un lugar donde la producción de assets continúa a partir de datos de juego que ya existen.

2. Convierte los resultados generados en assets operativos

AssetGeneratedMediaService guarda los resultados generados como registros de biblioteca y permite marcar uno de ellos como winner. Cuando cambia el winner en un tipo de imagen, también se actualiza el thumbnail del ítem. En cambio, si un asset ya está en uso, no permite borrar tan fácilmente su medio representativo. En otras palabras, separa el acto de acumular resultados generados del acto de adoptar un asset operativo.

3. Rastrea si un asset está realmente en uso

AssetReferenceSyncService comprueba si una URL se está usando realmente dentro de Story Quiz y Mystery y sincroniza isReferenced en consecuencia. Del lado de Story Quiz revisa covers, cards, imágenes base de personajes, imágenes de emoción, fondos y medios de block. Del lado de Mystery revisa thumbnails de escenario, avatares de personajes, notas e imágenes de objetos. Por eso Asset Studio no es solo un repositorio bonito de assets, sino una herramienta de gestión conectada con contenido en uso real.

Qué le corresponde a ai-service

La responsabilidad de ai-service es todavía más clara. Este servicio no conoce la biblioteca de assets. Se encarga de ejecutar la generación. Interpreta el generationConfig enviado por el backoffice, decide qué provider y qué workflow usar, ejecuta el trabajo de manera asíncrona y emite el progreso por SSE.

En la implementación, MediaGenerationService primero revisa los slots de concurrencia por cuenta. Después GenerationConfigParser analiza la configuración. La precedencia aquí importa bastante. generationConfig.media[mediaType].providers[providerCode] tiene la prioridad más alta, luego viene default y finalmente el nivel raíz actúa como fallback. Esa estructura permite que un mismo asset mantenga configuraciones distintas por tipo de medio y por provider.

Cuando el análisis termina, ai-service no genera de inmediato. Coloca el trabajo en una cola de DTE jobs. El jobId que recibe el frontend se mantiene después como la misma clave usada para la suscripción SSE. Es decir, el backoffice no queda bloqueado esperando una petición larga. Sigue el proceso de generación asíncrona alrededor de un job ID.

Por qué importa la integración con ComfyUI

En la implementación actual, una de las partes más interesantes de Asset Studio es la integración con ComfyUI. ComfyUIProvider no tiene los workflows codificados dentro de la aplicación. La configuración de runtime se lee desde la configuración del provider y los workflows ejecutables se cargan desde registros de workflow en la base de datos. Además, soporta no solo prompt-to-image, sino también pipelines media-to-media.

Esto importa porque la configuración de generación de Asset Studio no es simplemente una cadena de prompt. Los usuarios pueden enviar juntos overrides de runtime como workflowId, workflowPipeline, seed, width, height y una URL de imagen de entrada. En la implementación real, elegir un pipeline workflow permite usar generated media ya existente como entrada para posprocesamiento o transformación.

El resultado es que la pantalla de generación con IA deja de ser una caja genérica de prompts y se convierte en un ejecutor de workflows guiado por datos. El mismo personaje puede volver a generarse con otro provider, otro workflow u otra imagen de entrada, y el resultado puede revisarse y acumularse después en la biblioteca.

Más adelante tendría sentido escribir una publicación aparte para mirar ComfyUI con más detalle.

Por qué separamos game-service y ai-service

Esto no es solo una preferencia por MSA. El dominio de assets y el runtime de generación fallan de formas distintas, tienen ciclos de vida distintos y se observan desde perspectivas operativas diferentes.

game-service trata información que necesita permanecer durante mucho tiempo, como la estructura de directorios, el estado de aprobación, el uso real, la portability y el history. ai-service, en cambio, trata qué providers siguen vivos, qué workflow debe elegirse, cuántos trabajos pueden ejecutarse ahora mismo y hasta dónde ha avanzado la generación. Si esas dos preocupaciones se mezclan, la implementación puede sentirse cómoda durante un tiempo, pero el límite se vuelve borroso muy rápido.

En la estructura actual, Asset Studio conecta ambas capas. El backoffice trabaja desde una sola pantalla, pero maneja por separado el almacenamiento y la ejecución. Esa separación permite que la biblioteca de assets y la infraestructura de generación evolucionen de manera independiente a medida que se sumen más géneros, además de Story Quiz y Mystery.

Cierre

En la publicación anterior, Asset Studio fue apenas una frase de paso. Pero si se sigue la implementación real, no significa una sola función para generar imágenes. En Omnilude, Asset Studio es un sistema de producción que reúne assets de juego, toma fuentes desde dominios existentes, ejecuta trabajos de generación con IA, revisa los resultados y rastrea si esos assets están realmente en uso.

Por eso no veo Asset Studio como una pantalla secundaria dentro del desarrollo de juegos. Se parece mucho más a un punto de encuentro donde converge toda la línea de producción, incluyendo Story Quiz y Mystery. En la próxima publicación quiero acotar el foco y explicar cómo este sistema de assets entra en un pipeline de publicación real o en el flujo de producción de un género concreto.

Incluso para mí, releyéndolo como revisor, esta publicación terminó sintiéndose densa y un poco como un cifrado.

Pienso actualizar el blog para soportar imágenes adjuntas y luego revisar también las publicaciones anteriores, de modo que la legibilidad general mejore.

Aun así, como este texto costó bastante trabajo, voy a publicarlo de todos modos.