feat(gamedev-2d): pipeline walk_cycle_oneshot — personaje andando en pixel-art animado
Promueve el caso 1 del report 0217 (animacion de sprites de personaje) a un pipeline one-shot: de un prompt de personaje a un sprite sheet + GIF/WEBP en loop, frame-by-frame dirigido por pose (ControlNet OpenPose + seed fija + Rembg) con cada frame pixelizado a NxN RGBA. Nuevas funciones reutilizables (issue 0087, crecimiento por composicion): - comfyui_walk_cycle_oneshot (pipeline): orquesta poses -> generacion -> pixelizado -> ensamblado. No-throw, salta frames que fallan. Modo openpose (esqueletos reales) con fallback prompt-pose. - render_openpose_walk_skeletons: dibuja N esqueletos OpenPose COCO-18 del walk cycle (el insumo que el report 0217 marco como faltante). - comfyui_pixelize_sprite_png: PNG existente -> NxN RGBA pixel-art real (compone crop_to_content + pixeloe_downscale + comfyui_pixelize_image). - assemble_animated_sprite: frames RGBA -> sprite sheet horizontal + WEBP/GIF loop. - comfyui_build_walk_cycle_workflow (pura): grafo API del workflow animado para la UI (ControlNet OpenPose -> KSampler xN seed fija -> ImageBatch -> Rembg -> SaveAnimatedWEBP). Verificado en GPU: GIF/WEBP de caballero andando, 4 frames 32x32 (y 64x64) RGBA con fondo transparente y 16 colores, identidad de silueta consistente, piernas que cambian. Metodo de poses usado: OpenPose real (sin fallback). Evidencia en report 0221. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
---
|
||||
name: comfyui_walk_cycle_oneshot
|
||||
kind: pipeline
|
||||
lang: py
|
||||
domain: pipelines
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def comfyui_walk_cycle_oneshot(character: str, *, frames: int = 4, size: int = 32, colors: int = 16, fps: int = 8, checkpoint: str = 'IMG_dreamshaper_8.safetensors', ref_image: str | None = None, server: str = '127.0.0.1:8188', dest_dir: str = '~/ComfyUI/output', seed: int = 0, pose_method: str = 'auto', controlnet_strength: float = 0.7, engine: str = 'pixeloe', palette=None, fmt: str = 'webp', **gen_kwargs) -> dict"
|
||||
description: "Pipeline one-shot: de un prompt de personaje a una animacion de walk cycle en pixel-art (sprite sheet + GIF/WEBP en loop). Genera N frames frame-by-frame dirigidos por pose (ControlNet OpenPose con esqueletos del walk cycle, o fase del paso por prompt como fallback), con seed fija para identidad consistente y Rembg para alpha, y pixeliza cada frame a un grid duro size x size RGBA. Materializa el caso 1 de la investigacion de animacion de sprites (report 0217): personaje = frame-by-frame pose-driven, NUNCA modelos de video. Compone render_openpose_walk_skeletons + comfyui_build_sprite_sheet_workflow + submit/wait/fetch + comfyui_pixelize_sprite_png + assemble_animated_sprite. Impuro: red + GPU + disco. No-throw, salta frames que fallan."
|
||||
tags: [gamedev-2d, comfyui, pixelart, sprite, animation, walk-cycle, controlnet, openpose, launcher]
|
||||
uses_functions: ["render_openpose_walk_skeletons_py_ml", "comfyui_build_sprite_sheet_workflow_py_ml", "comfyui_submit_workflow_py_ml", "comfyui_wait_result_py_ml", "comfyui_fetch_output_image_py_ml", "comfyui_pixelize_sprite_png_py_ml", "assemble_animated_sprite_py_ml"]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: ["os", "sys"]
|
||||
params:
|
||||
- name: character
|
||||
desc: "Prompt del personaje (ej. 'pixel art knight, full body, side view'). No vacio. La identidad se mantiene entre frames con seed fija."
|
||||
- name: frames
|
||||
desc: "Numero de frames del ciclo (>=2, recomendado 4-8). 4 = las 4 fases canonicas contact-L / passing / contact-R / passing."
|
||||
- name: size
|
||||
desc: "Lado del grid pixel-art final por frame en pixeles (32 sprites pequenos, 64 personajes con mas detalle)."
|
||||
- name: colors
|
||||
desc: "Numero de colores de la paleta libre por frame (cuantizacion MEDIANCUT) cuando palette es None."
|
||||
- name: fps
|
||||
desc: "Cadencia de la animacion en frames por segundo (duration = 1000/fps ms por frame)."
|
||||
- name: checkpoint
|
||||
desc: "Checkpoint SD1.5 (ControlNet OpenPose + IPAdapter-FaceID solo instalados en SD1.5; default 'IMG_dreamshaper_8.safetensors')."
|
||||
- name: ref_image
|
||||
desc: "Imagen de cara de referencia en el input/ del servidor para IPAdapter-FaceID (segunda ancla de identidad). None = solo seed + prompt."
|
||||
- name: server
|
||||
desc: "host:port del servidor ComfyUI (sin esquema). Default 127.0.0.1:8188."
|
||||
- name: dest_dir
|
||||
desc: "Directorio donde guardar frames + sprite sheet + animacion (se expande ~)."
|
||||
- name: seed
|
||||
desc: "Semilla FIJA del KSampler para TODOS los frames (identidad estable entre poses)."
|
||||
- name: pose_method
|
||||
desc: "'openpose' (esqueletos OpenPose -> ControlNet, control exacto), 'prompt' (fase del paso descrita en el prompt, sin esqueletos) o 'auto' (intenta openpose, cae a prompt si el render falla)."
|
||||
- name: controlnet_strength
|
||||
desc: "Fuerza del ControlNet OpenPose (0.7 da buen control sin aplastar el estilo). Solo aplica en modo openpose."
|
||||
- name: engine
|
||||
desc: "Motor de downscale del pixelizado: 'pixeloe' (contrast-aware, conserva silueta) o 'nearest'."
|
||||
- name: palette
|
||||
desc: "None (paleta libre a colors), nombre builtin ('pico-8','nes','game-boy') o lista de hex. Fija ignora colors."
|
||||
- name: fmt
|
||||
desc: "Formato de la animacion: 'webp' (recomendado, alpha real) o 'gif' (alpha binario)."
|
||||
output: "dict {ok, frames:[paths], spritesheet_path, animation_path, size, n_frames, seed, pose_method_used, skipped:[idx], error}. ok=True si se produjo la animacion con >=1 frame. n_frames puede ser < frames si alguno fallo (se salta y se sigue)."
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/pipelines/comfyui_walk_cycle_oneshot.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join("python", "functions"))
|
||||
from pipelines.comfyui_walk_cycle_oneshot import comfyui_walk_cycle_oneshot
|
||||
|
||||
# Requiere el servidor ComfyUI vivo en 127.0.0.1:8188 (GPU).
|
||||
res = comfyui_walk_cycle_oneshot(
|
||||
"pixel art knight, full body, side view",
|
||||
frames=4, size=32, colors=16, fps=8, seed=42,
|
||||
dest_dir="/tmp/comfy_walk_cycle",
|
||||
)
|
||||
print(res["ok"], res["n_frames"], res["animation_path"], res["pose_method_used"])
|
||||
# -> True 4 /tmp/comfy_walk_cycle/walk_cycle.webp openpose
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando quieras una animacion de un personaje en pixel-art (caminar, correr) lista
|
||||
para un juego 2D, en un solo paso: das el prompt del personaje y recibes el sprite
|
||||
sheet + el GIF/WEBP en loop. Es la promocion a pipeline (issue 0087) de la receta
|
||||
del caso 1 del report 0217 — el camino correcto para sprites limpios de personaje
|
||||
con alpha, frente a AnimateDiff o modelos de video (que ensucian el alpha y no
|
||||
clavan la pose). Para una sola pose estatica usa `comfyui_pixelart_real_oneshot`;
|
||||
para varias vistas direccionales (8-way) usa
|
||||
`comfyui_build_directional_sprite_workflow`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Server vivo + GPU**: requiere ComfyUI en `server` con la GPU libre. El report
|
||||
recomienda `POST /free` antes de cargas pesadas de modelo. Cada frame reusa el
|
||||
mismo checkpoint, asi que el modelo solo se carga una vez.
|
||||
- **Poses OpenPose**: en modo `openpose` los esqueletos se escriben en el `input/`
|
||||
del servidor (asume server local; para un server remoto haria falta subirlos con
|
||||
`POST /upload/image`). Si el ControlNet no produce variacion de piernas
|
||||
reconocible, usa `pose_method="prompt"`: a 32x32 el detalle de pose se simplifica
|
||||
y la fase del paso por prompt + seed fija da un walk reconocible.
|
||||
- **Identidad**: la `seed` es FIJA para todos los frames — esa es la ancla de
|
||||
identidad. Cambiar la seed entre frames rompe la consistencia del personaje.
|
||||
`ref_image` (IPAdapter-FaceID) es una segunda ancla opcional; sobre un sprite de
|
||||
cuerpo entero pequeno aporta sobre todo paleta/ropa (ver report 0217).
|
||||
- **No-throw, salta frames**: si un frame falla (red, GPU, build) se anade a
|
||||
`skipped` y la animacion se monta con los que queden. ok=False solo si NINGUN
|
||||
frame sale.
|
||||
- **Loop suave**: con `frames=4` el ciclo (contact-L, passing, contact-R, passing)
|
||||
ya cierra el bucle — el frame siguiente al ultimo vuelve a la primera fase.
|
||||
- **WEBP vs GIF**: `fmt="webp"` conserva alpha real (lossless); `fmt="gif"` solo
|
||||
tiene alpha binario (1 bit). Para sprites con transparencia, usa WEBP.
|
||||
|
||||
## Capability growth log
|
||||
|
||||
- v1.0.0 (2026-06-28) — version inicial. Caso 1 del report 0217 promovido a
|
||||
pipeline one-shot: walk cycle pixel-art con poses OpenPose (o fallback prompt),
|
||||
seed fija para identidad, Rembg para alpha, pixelizado a NxN RGBA, sprite sheet +
|
||||
WEBP/GIF en loop.
|
||||
Reference in New Issue
Block a user