# Capability group: `gamedev` / `gamedev-2d` — assets 2D para Godot (generación + post-proceso + puente) Cluster de funciones para producir y mover assets 2D de juego entre **ComfyUI** (generación) y **Godot 4** (consumo). Tres capas: 1. **Builders de workflow 2D** (`gamedev-2d`, GPU): construyen el dict (API format) de los workflows ComfyUI para pixel-art, tiles seamless, isométrico, sprites de personaje y VFX en bucle. Son **puros** (no tocan GPU al construir); el coste GPU está al enviar con `comfyui_submit_workflow`. 2. **Post-proceso determinista** (`gamedev`, CPU): pixelizar, recortar a alpha. 3. **Puente de assets** (`gamedev`, CPU): coloca el resultado en un proyecto Godot con sus import settings. Tags: `gamedev` (post-proceso + puente) y `gamedev-2d` (builders de workflow). Filtro: `mcp__registry__fn_search query="" tag="gamedev-2d"`. Documento hermano del grupo `comfyui` (generación genérica de imágenes/video/3D). Diseño del puente: `docs/comfyui-godot-integration.md`. Planes origen: `reports/0135` (pixelart), `reports/0139` (entornos/tiles/iso), `reports/0137` (personajes/sprites), `reports/0140` (VFX), `reports/0143` (ronda 2b: builders), `reports/0147` (item icons), `reports/0149` (parallax background). ## Builders de workflow 2D (`gamedev-2d`, puros — generación) Construyen el dict API format listo para `comfyui_submit_workflow`. Cada uno compone funciones existentes del registry (`comfyui_build_txt2img_workflow`, `comfyui_inject_*`, `comfyui_build_ipadapter_workflow`) — no reinventan el grafo. class_types verificados contra `/object_info` del server (8GB lowvram). Probados e2e en GPU: pixelart, seamless, VFX (ver `reports/0143`). | ID | Firma corta | Qué hace | |---|---|---| | `comfyui_build_pixelart_workflow_py_ml` | `(positive, negative=…, *, ckpt_name="juggernaut_xl_v11…", pixel_lora="pixel-art-xl…", use_lcm=True, …) -> dict` | Fase 1 pixel-art: SDXL + LoRA pixel-art-xl (+ LCM 8 steps). El pixel-perfect es post (`comfyui_pixelize_image`). | | `comfyui_build_seamless_tile_workflow_py_ml` | `(positive, negative="", *, tiling="enable", copy_model="Make a copy", circular_vae=True, material_lora=None, …) -> dict` | Textura tileable: `SeamlessTile` (Conv2d circular) + `CircularVAEDecode`. Coste VRAM ≈0. | | `comfyui_build_isometric_workflow_py_ml` | `(positive, negative=…, *, iso_lora="isometric_game_assets_sd15…", grid_image=None, …) -> dict` | Asset iso 2:1: LoRA iso + ControlNet grid opcional. | | `comfyui_build_sprite_sheet_workflow_py_ml` | `(subject, *, ref_image=None, pose_skeleton=None, char_lora=None, transparent=True, …) -> dict` | UN sprite de personaje: IPAdapter-FaceID + LoRA + ControlNet OpenPose (Advanced, end<1) + Rembg. Varias poses → sheet. SD1.5. | | `comfyui_build_vfx_spritesheet_workflow_py_ml` | `(prompt, *, motion_model="mm_sd_v15_v2.ckpt", num_frames=16, closed_loop=True, lora=None, …) -> dict` | N frames AnimateDiff loop sobre negro (insumo de luma→alpha). 8GB: 16f@512² revienta, usar ≤8f@512² o bajar resolución. | | `comfyui_build_item_icon_workflow_py_ml` | `(item, *, style="game icon, clean, centered", checkpoint="dreamshaper_8…", size=512, transparent=True, lora=None, …) -> dict` | UN icono de item de inventario (espada/poción/anillo/libro/escudo): txt2img cuadrado + prompt scaffold de icono + LoRA estilo opcional + Rembg (alpha). Set coherente = mismo style/checkpoint/lora por item. SD1.5. | | `comfyui_build_portrait_avatar_workflow_py_ml` | `(character, *, style="character portrait", ref_face=None, checkpoint="dreamshaper_8…", size=512, facedetailer=True, lora=None, …) -> dict` | UN retrato/avatar de personaje (busto centrado, cara al espectador, fondo simple): txt2img + prompt scaffold de retrato + FaceDetailer (cara nítida) + LoRA estilo opcional; `ref_face` → IPAdapter-FaceID para rostro consistente entre retratos. Diálogo/perfil/selección. SD1.5. | | `comfyui_build_emote_workflow_py_ml` | `(character, expression, *, ref_face=None, style="character portrait", checkpoint="dreamshaper_8…", size=512, facedetailer=True, lora=None, …) -> dict` | UN emote/expresión facial del MISMO personaje (alegre/triste/enfadado/sorprendido/neutral…) para diálogo, retratos reactivos o emotes de chat: txt2img + prompt scaffold de emote (`portrait of {character}, {expression} expression, emote, clean background`) + FaceDetailer (conserva la expresión); `ref_face` → IPAdapter-FaceID para que varíe SOLO la expresión y el rostro sea el mismo. UNA expresión por llamada; set = mismas claves variando `expression` → `comfyui_build_grid`. Probado e2e en GPU (`reports/0151`). SD1.5. | | `comfyui_build_parallax_background_workflow_py_ml` | `(scene, *, style="game background, side-scroller…", layers=3, checkpoint="dreamshaper_8…", depth_node="DepthAnythingV2Preprocessor", width=1024, height=512, …) -> dict` | Fondo en capas para parallax 2.5D: genera el fondo apaisado (txt2img) + su depth map (`DepthAnythingV2Preprocessor` sobre el VAEDecode), dos SaveImage. El split en N bandas por profundidad es post (GAP: `split_parallax_layers`, aún no creada). Probado e2e en GPU (`reports/0149`). SD1.5. | | `comfyui_build_normal_map_workflow_py_ml` | `(image, *, method="normal", strength=1.0, resolution=512, bg_threshold=0.1, filename_prefix="normal_map") -> dict` | Normal/depth map de un sprite existente para iluminación dinámica 2.5D (Godot CanvasItem `normal_map`, Unity sprite normal). `LoadImage → preprocesador controlnet_aux → SaveImage`. `method`: `normal` (default, `BAE-NormalMapPreprocessor`, normal canónico **azul/violeta** usable directo en motor), `normal_midas` (MiDaS, único con `strength`→`a`, paleta no canónica), `normal_dsine` (DSINE), `depth` (`DepthAnythingV2`, height en gris). `image` debe estar en `input/` de ComfyUI. Coste VRAM ≈0. Probado e2e en GPU (`reports/0150`). | | `comfyui_build_ui_hud_workflow_py_ml` | `(element, *, ui_style="fantasy game UI", checkpoint="dreamshaper_8…", size=512, transparent=True, lora=None, …) -> dict` | UN elemento de interfaz/HUD de juego (botón, marco/panel, barra de vida/maná/XP, icono de UI, cursor, viñeta de menú): txt2img cuadrado + prompt scaffold de UI (`{element}, {ui_style}, game UI element, centered, clean, plain background…`) + LoRA estilo opcional + Rembg (alpha). HUD coherente = mismo `ui_style`/`checkpoint`/`lora` por pieza, varía solo `element`. El texto/label lo pone el motor (negativo empuja a `no text`). Probado e2e en GPU (`reports/0152`). SD1.5. | | `comfyui_build_card_art_workflow_py_ml` | `(subject, *, card_style="fantasy trading card art", checkpoint="juggernaut_xl_v11…", width=512, height=768, hires=True, seed=0, lora=None, …) -> dict` | LA ilustración central de UNA carta coleccionable (TCG): criatura/personaje/hechizo en formato **vertical** de carta (`width dict` | UN enemigo/criatura de juego (goblin, esqueleto, slime, dragón, boss, elemental): figura de **cuerpo entero** centrada, fondo limpio recortable a alpha (`{variant} {creature}, {style}, full body, centered, plain background, game asset…`) → txt2img cuadrado + LoRA estilo opcional + Rembg (alpha). `variant` (ice/fire/elite/corrupted…) se antepone a la criatura para generar la familia del MISMO enemigo (misma `creature`/`seed`/`style`, varía solo `variant`); bestiario coherente = mismo `style`/`checkpoint`/`lora`, varía solo `creature`. El negativo empuja a UNA criatura entera sin recorte. Probado e2e en GPU con SD1.5 (`reports/0154`). SD1.5. | | `comfyui_build_prop_object_workflow_py_ml` | `(prop, *, style="game prop, isometric or side view", checkpoint="dreamshaper_8…", size=512, transparent=True, seed=0, lora=None, …) -> dict` | UN prop/objeto de escenario (barril, cofre, antorcha, planta, mueble, roca, fuente, estatua): objeto inanimado aislado a **escala de escena y perspectiva de juego** (iso/lateral), centrado, fondo limpio recortable a alpha (`{prop}, {style}, game asset, single object, centered, plain background, scene prop, world object…`) → txt2img cuadrado + LoRA estilo opcional + Rembg (alpha). **Objeto de MUNDO**, no icono plano de inventario (≠ `item_icon`, que es para una casilla de UI); este puebla el nivel. Atrezzo coherente = mismo `style`/`checkpoint`/`lora`, varía solo `prop`. El negativo excluye personas/criaturas (objeto inanimado). Probado e2e en GPU con SD1.5 (`reports/0155`). SD1.5. | ## Funciones de post-proceso y puente (`gamedev`, CPU) | ID | Firma corta | Qué hace | |---|---|---| | `comfyui_pixelize_image_py_ml` | `(src, dst, *, downscale=8, colors=16, palette=None, dither=False, upscale_back=True) -> dict` | Pixel-perfect: downscale nearest + cuantización a N colores o paleta fija (game-boy/pico-8/nes). Fase 2 pixelart. Impura (I/O). | | `comfyui_matting_luma_to_alpha_py_ml` | `(image_path, *, out_path=None, gamma=1.0, black_point=0.0, premultiply=False, luma_weights=(.299,.587,.114)) -> dict` | Frame VFX sobre negro -> RGBA usando luminancia como alpha (translúcidos con additive blend). Impura (I/O). | | `comfyui_export_asset_to_godot_py_pipelines` | `(asset_path, kind, godot_project, *, name=None, reimport=True, godot_bin=None) -> dict` | Copia el asset a `res://assets//` por `kind` + escribe `.import` + filtro Nearest si pixelart + reimport headless. Pipeline impuro. | | `godot_map_asset_dir_py_core` | `(kind) -> str` | Mapea `kind` -> subcarpeta de `res://assets/`. Pura. | | `godot_clean_asset_name_py_core` | `(filename, *, override=None) -> str` | Normaliza el nombre `_NNNNN_.` a snake_case seguro para `res://`. Pura. | ## Ejemplo end-to-end con builder (Fase 1 GPU → Fase 2 CPU → Godot) Flujo completo pixel-art: construir workflow → generar en ComfyUI → pixel-perfect → Godot. ```python import sys, os sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions")) from ml.comfyui_build_pixelart_workflow import comfyui_build_pixelart_workflow from ml.comfyui_submit_workflow import comfyui_submit_workflow from ml.comfyui_wait_result import comfyui_wait_result from ml.comfyui_fetch_output_image import comfyui_fetch_output_image from ml.comfyui_pixelize_image import comfyui_pixelize_image # 1. Construir (puro) + 2. generar (GPU) wf = comfyui_build_pixelart_workflow("isometric tiny house, pixel, 32x32 style", use_lcm=True, seed=42) pid = comfyui_submit_workflow(wf)["prompt_id"] outs = comfyui_wait_result(pid, timeout=300) fn = next(img["filename"] for o in outs.values() for img in o.get("images", [])) raw = comfyui_fetch_output_image(fn, dest_dir="/tmp")["out_path"] # 3. pixel-perfect (CPU) -> 4. export Godot (ver ejemplo de abajo) px = comfyui_pixelize_image(raw, "/tmp/house_pixel.png", downscale=8, colors=16) ``` VFX: `comfyui_build_vfx_spritesheet_workflow(prompt, num_frames=8)` → submit → fetch N frames → `comfyui_matting_luma_to_alpha` por frame → montar sheet RGBA con `Image.alpha_composite` (NO `comfyui_build_grid`, que aplana el alpha). ## Ejemplo canónico de post-proceso Flujo: crudo generado en ComfyUI -> pixelizar -> exportar a Godot con Nearest. ```python import sys, os sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions")) from ml.comfyui_pixelize_image import comfyui_pixelize_image from ml.comfyui_matting_luma_to_alpha import comfyui_matting_luma_to_alpha from pipelines.comfyui_export_asset_to_godot import comfyui_export_asset_to_godot OUT = os.path.expanduser("~/ComfyUI/output") PROJ = os.path.expanduser("~/gamedev/projects/crossy_road") # 1. Pixelizar un sprite crudo (SDXL+pixel-art-xl) a 16 colores px = comfyui_pixelize_image(f"{OUT}/hero_00001_.png", "/tmp/hero_pixel.png", downscale=8, colors=16) # 2. Exportarlo a Godot como pixelart (carpeta sprites/, filtro Nearest, reimport) exp = comfyui_export_asset_to_godot("/tmp/hero_pixel.png", "pixelart", PROJ) print(exp["dest_res_path"], exp["pixelart_filter_set"], exp["reimported"]) # Rama VFX: frame de humo sobre negro -> RGBA -> carpeta vfx/ rgba = comfyui_matting_luma_to_alpha(f"{OUT}/vfx_loop_00007_.png", gamma=1.2, black_point=0.04) comfyui_export_asset_to_godot(rgba["out_path"], "vfx", PROJ) ``` ## Fronteras (qué NO cubre) - **Montaje de spritesheet dedicado** (grid RGBA + JSON sidecar para Godot/Unity): no hay función propia todavía — el ejemplo VFX monta con `Image.alpha_composite` inline. `comfyui_build_grid` NO sirve (aplana el alpha sobre fondo oscuro). Pendiente de R4 (plan `reports/0140` F2). - **Pipelines one-shot** (build → submit → wait → fetch → post en una call) para pixelart/sprite/VFX: pendientes. Hoy se encadena a mano (ver ejemplos). Candidatos a promoción a pipeline (issue 0087) cuando el patrón se repita. - **Sprite turnaround multi-vista** (orquestar N poses con identidad fija + juez): el builder `comfyui_build_sprite_sheet_workflow` produce UN frame; la orquestación multi-pose es pipeline pendiente (plan `reports/0137` T2). - **Paletas lospec por red** (`load_lospec_palette`): no incluido. `pixelize` usa paletas fijas embebidas (game-boy/pico-8/nes) o lista de hex, sin HTTP. - **TileSet / SpriteFrames `.tres`**: Godot no los deriva solos; `export_asset_to_godot` copia la textura y avisa, pero no genera el recurso (paso manual o futura función). ## Prerequisitos / notas - **Godot CLI** para el reimport headless: autodetectado en PATH y en `~/godot/Godot_v4.7-stable_linux.x86_64`. Si falta, `export_asset_to_godot` deja el `.import` escrito y lo anota (no falla). - **Filtro Nearest (Godot 4)**: se setea global en `project.godot` (`default_texture_filter=0`), no por `.import`. La función lo asegura para pixelart. - CPU-only: Pillow + numpy del venv del registry. Cero VRAM, cero red.