Files
fn_registry/docs/capabilities/gamedev-2d.md
T
egutierrez 6add50311b feat(gamedev): comfyui_build_splash_art_workflow — splash/loading screen key art (apaisado 16:9, cinematográfico, espacio para título)
Builder PURO (dict API format) del grupo gamedev/gamedev-2d, hermano de
comfyui_build_card_art_workflow y comfyui_build_parallax_background_workflow.

Genera la ilustración grande de una pantalla de portada / loading screen / key
art en formato pantalla apaisado 16:9 (~1024x576), composición cinematográfica
(wide shot) con aire para superponer el título del juego. Compone
comfyui_build_hires_fix_workflow (si hires) o comfyui_build_txt2img_workflow +
comfyui_inject_lora (estilo opcional). Genera SOLO la ilustración: el negativo
por defecto rechaza text/title/logo/UI/frame para que el motor componga el
título encima.

- 9 tests offline verde (golden hires, apaisado width>height, batch_size, sin
  hires, dims/mood/lora reflejados, error scene vacío, determinismo).
- .md autosuficiente (Ejemplo + Cuando usarla + Gotchas) + fila en
  docs/capabilities/gamedev-2d.md.
- Probado e2e en GPU 8GB lowvram: 1 splash real (héroe ante castillo oscuro en
  tormenta), 1024x576 -> 1536x864 (16:9 exacto) tras hires, 54s, SD1.5
  dreamshaper_8. Evidencia en reports/0159.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 00:23:16 +02:00

16 KiB
Raw Blame History

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 expressioncomfyui_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 strengtha, 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<height, ~512×768), composición centrada + iluminación dramática ({subject}, {card_style}, dramatic lighting, detailed illustration, centered composition, full art…). hires=True → 2ª pasada de detalle (comfyui_build_hires_fix_workflow); si no, txt2img + LoRA estilo opcional. Genera SOLO la ilustración — el marco/título/stats los pone el motor/post (negativo rechaza card frame/border/text/stats/UI). Set coherente = mismo card_style/checkpoint/lora, varía solo subject. Probado e2e en GPU con SD1.5 (reports/0153); ⚠️ el path hires=True falla hoy por bug del builder comfyui_build_hires_fix_workflow (nodo UltimateSDUpscale pide batch_size) — usar hires=False hasta el fix. SD1.5/SDXL.
comfyui_build_enemy_creature_workflow_py_ml (creature, *, variant=None, style="game creature, full body", checkpoint="dreamshaper_8…", size=512, transparent=True, seed=0, lora=None, …) -> 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.
comfyui_build_topdown_sprite_workflow_py_ml (subject, *, direction="south", style="top-down game sprite, RPG", checkpoint="dreamshaper_8…", size=512, transparent=True, seed=0, lora=None, …) -> dict UN sprite en vista CENITAL (top-down) estilo RPG clásico/roguelike (Zelda, juegos cenitales): personaje/objeto visto desde arriba, centrado, fondo limpio recortable a alpha ({subject}, top-down view, overhead view, {direction} facing, {style}, centered, plain background, game asset…) → txt2img cuadrado + LoRA estilo opcional + Rembg (alpha). direction (south/north/east/west) para el sprite de movimiento: las 4 vistas del MISMO personaje = misma subject/style/seed, varía solo direction → montar con comfyui_build_grid. DISTINTO de sprite_sheet (vista lateral/frontal de plataformas): el negativo por defecto rechaza side/front/3-4/isometric/perspective para forzar la cenital. Con SD1.5 sin LoRA sale picado alto; cenital estricto pide LoRA top-down + cfg alto. Probado e2e en GPU con SD1.5 (reports/0156). SD1.5.
comfyui_build_splash_art_workflow_py_ml (scene, *, mood="epic, cinematic", checkpoint="juggernaut_xl_v11…", width=1024, height=576, hires=True, seed=0, lora=None, …) -> dict LA ilustración grande de UN splash / pantalla de carga / key art en formato pantalla apaisado 16:9 (width>height, ~1024×576), composición cinematográfica ({scene}, {mood}, key art, game splash screen, dramatic lighting, cinematic composition, wide shot, epic scale, atmospheric…). hires=True → 2ª pasada de detalle (comfyui_build_hires_fix_workflow) para verse a pantalla completa; si no, txt2img + LoRA estilo opcional. Genera SOLO la ilustración — el título/logo/barra de carga los pone el motor/post (negativo rechaza text/title/logo/UI/frame/watermark), dejando aire para superponer el título. Set coherente = mismo mood/checkpoint/lora, varía solo scene. Probado e2e en GPU con SD1.5 + hires (1024×576 → 1536×864, 54s, reports/0159). SD1.5/SDXL.

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/<dir>/ 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 <prefijo>_NNNNN_.<ext> 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.

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.

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.