Editor de Animaciones#
El Editor de Animaciones autora cada animación cuadro-a-cuadro del motor — el ciclo de
caminata de un personaje, el loop idle de un objeto o hotspot, la secuencia de cuadros de una
capa de parallax de sala, o el clip vinculado de un widget de GUI. Una animación es un
descriptor: { loop, next, pivotX, pivotY, dirs }, con clave por un id estable en el
animations.json del proyecto y resuelto en runtime a través de getAnim() de
core/animations.js. Acá no hay lógica de reproducción escrita a mano por clip — el editor
solo edita datos; el avance de cuadros es una única función pura compartida (tickAnim())
por la que pasa idénticamente cada categoría.
Llegas a él desde el Hub (con el dev server corriendo). El panel izquierdo lista las animaciones, filtrables por categoría (Personajes / Hotspots-Objetos / Salas / Objetos de inventario / GUIs) y, dentro de una categoría, por entidad. El área de trabajo central tiene la barra de flags, las pestañas de dirección, la línea de tiempo de cuadros y el inspector por cuadro; el panel derecho de vista previa reproduce el clip en vivo.
Los avisos de rutina — "12 cuadro(s) eliminado(s)", "copiado", un nudge aplicado — aparecen como un toast abajo a la derecha y se van solos; una ráfaga reescribe un mismo toast en lugar de apilarlos. Los errores son la excepción: se quedan hasta que los descartas con un clic.
Categorías y claves#
- Personaje — clips walk/idle/blink/talk, con clave
<charId>_<tipo>(p. ej.alex_walk,alex_idle). + Nueva Anim para esta categoría nunca te deja tipear la clave directamente: eliges el personaje de un desplegable y un tipo (WALK / IDLE / BLINK / TALK / OTHER para un sufijo personalizado), y el editor compone el id él mismo — la misma disciplina anti-typo que cualquier otro picker autocableado de la suite.resolveAnimDef()(encore/animations.js) es lo que realmente elige el registro en runtime, con una cascada si falta el clip exacto:idlenunca sustituye nada (un idle faltante dibuja un sprite estático);walkcae aidle; cualquier otra cosa cae aidley luego awalk. Esa única respuesta decide las dos mitades — los cuadros que ves y elloop/loopStart/speed/nextque los mueve — así que un clip nunca puede reproducirse con los parámetros de loop de otro. Conviene saberlo si autoras un personaje con ciclo de caminata pero sin idle: quieto dibuja su sprite estático y no toma ningún parámetro de loop, en vez de tomar prestados los del clip de caminar. - Hotspot/Objeto — un único slot no-direccional
state(sin división left/right/up/down), usado para la animación idle/secuencia de cuadros vinculada de un hotspot (la tarjeta Animación de hotspot del Editor de Salas conecta una de estas). Esa tarjeta es el default, no la última palabra: una interacción puede pasar un prop a otro clip en runtime conSTARTANIM:<hotspot>|<clip>, y el selector de clip de ahí lista exactamente las animaciones de Hotspot/Objeto y Sala que autoras acá — por su id completo, ya que estas categorías no llevan prefijo de entidad como los clips de personaje. - Sala — secuencias de cuadros de capas de parallax (
room.layers[]), también un único slotstate. Igual que la categoría GUI, estas se resuelven en runtime desde el registro (animations.json) por id — las rutas de cuadro horneadas enroom.layers[].anim.srcsson solo una instantánea de respaldo, usada únicamente cuando el registro no tiene un registro que coincida. Un guard de carpeta mantiene los ids locales a la sala: si un registro con el mismo nombre pertenece a una sala distinta, la capa cae a su propia instantánea horneada en vez de tomar prestados los cuadros equivocados. - Objeto de inventario — una única ruta de sprite por objeto, sin estructura
dirs. - GUI — con clave
<guiId>_<nombreAnim>, no-direccional, guardada bajoassets/guis/<guiId>/animations/<nombreAnim>/. Esta es la categoría que referencia el doc del Editor de GUIs: un widget (sprite, botón, o panel) puede vincular una de estas, y el cuadro actual del clip vinculado reemplaza el PNG estático del widget al dibujar.
Todas las categorías comparten una sola forma, y el motor lee esa y nada más: los cuadros viven bajo
dirs — dirs.state para las categorías no-direccionales, dirs.left / right / up / down
para personajes. Un registro escrito a mano en cualquier otra forma (cuadros en el nivel de arriba,
un campo fps en vez del ms por cuadro) no es un error que el editor pueda cometer, pero sí uno
que puede cometer un animations.json editado a mano: el juego arranca y el clip simplemente nunca
se reproduce. Como el motor no puede adivinar qué querías decir, lo dice al arrancar — la consola del
navegador recibe una línea [ignitor] animations.json: "<id>" has no "dirs" map por cada registro
inreproducible, listando las claves que el registro sí tiene. Si una animación que autoraste en
otro lado se niega a reproducirse, esa línea es el primer lugar donde mirar.
Y cuando no resuelve nada — ni clip, ni un spriteLeft en el personaje — el actor se dibuja como la
caja magenta de marcador del motor, no art: <charId>. Esa caja es la respuesta honesta: quiere
decir que el personaje no tiene arte, no que un archivo falló al cargar.
Autoría de cuadros#
Los cuadros se agregan a la línea de tiempo de tres formas: soltando/eligiendo imágenes
directamente en la zona de drop, duplicando un cuadro existente como copia independiente para
retocar cuadro a cuadro, o el panel MP4 Import — un pipeline de chroma-key
(auto/magenta/negro/verde/hex-personalizado, con limpieza de edge-key y un slider de
tolerancia) que corre un job respaldado por ffmpeg a través del endpoint /import-video,
distribuyendo N cuadros parejo a lo largo del video fuente desde un offset de inicio
configurable. Las importaciones de personaje anclan los cuadros a un canvas alineado por los
pies por defecto (los ciclos de caminata funcionan mejor así); las importaciones de
objeto/sala/objeto de inventario pueden en cambio mantener el tamaño de cuadro nativo del MP4.
Todo el arte que traigas se re-encoda a PNG al entrar, así que un slot nunca queda con un
.jpg al lado del .png que venía a reemplazar — las herramientas de cuadros trabajan en PNG, y
un archivo suelto en otro formato les sería invisible.
También hay un panel Normalize para arreglar en lote el tamaño/anclaje del canvas de cuadros después del hecho, con una vista previa dry-run y su propio deshacer. Apúntalo a todo el directorio o solo a tu selección, y deja Respetar normalizados marcado para saltar los cuadros que ya están exactamente en el canvas target — desmárcalo para forzar una pasada completa, por ejemplo tras cambiar el alto del personaje con el mismo canvas. Cuando un lote mezcla cuadros de distintos tamaños de origen, los agrupa por tamaño y calcula un bounding box compartido por grupo, así un cuadro gigante entre treinta no puede arrastrar hacia abajo la escala de todo el personaje.
Los presets de clase de tamaño son la guía — se derivan de la altura lógica de tu proyecto, así un personaje se lee del mismo tamaño a cualquier resolución — pero Custom es realmente custom: los sliders van de 2 a 4096 en todas las categorías, que es lo que te permite normalizar al tamaño nativo del material que importaste en vez de bajarlo a un preset. Si llevas el lienzo de un personaje más allá de la altura lógica de tu proyecto, el panel te lo dice, porque el motor dibuja al actor a la altura nativa de su sprite: ese personaje va a salir más alto que la pantalla. Es un aviso, no un límite — un gigante es algo legítimo de authorear.
Los tamaños de cuadro se chequean solos. Cada miniatura lleva un badge W×H en su esquina: verde cuando el cuadro coincide con el resto de su animación, rojo cuando se desvía, con el detalle en un tooltip. 📐 Validar tamaños escanea toda la animación cargada — todas las direcciones de una — y lista los cuadros fuera de norma (dirección, índice, tamaño, archivo). "Normal" acá significa el tamaño de cuadro más común de esta misma animación, no un preset fijo: autorar a un canvas personalizado es perfectamente legítimo, así que los hermanos de un cuadro son la única referencia honesta. Es lo que caza una importación que nunca pasó por Normalize — un cuadro solitario de 1440×2560 entre hermanos de 320×512, que si no solo aparece después como un personaje que se agiganta en la vista previa.
Retimear en lote. La duración de un cuadro se edita de a uno en el inspector, pero la barra de lote tiene su propio control ms por cuadro — un campo, un dial y dos botones. = Fijar escribe esa duración en cada cuadro objetivo; + Sumar se la suma a la que ya tiene, en negativo para acortarla. "Objetivo" es tu multiselección, o todos los cuadros de la dirección en la que estás cuando no hay nada seleccionado, el mismo fallback que usan los controles de Espejo, Habilitado y Empujar offset — así que "pon todo el ciclo en 80ms" no necesita selección alguna. Las duraciones se recortan a 1–9999ms, y cuando el recorte muerde de verdad el toast lo dice, en vez de reportar un éxito limpio sobre cuadros que aplanó. El campo conserva su valor a propósito después de aplicar, porque retimear un ciclo autorado por lado significa aplicar el mismo número en la pestaña de la dirección siguiente.
Qué se puede permitir un ciclo smooth#
Al lado del conteo de cuadros hay un chip de presupuesto — 141 MB · 7,2 refrescos/cuadro —
que reporta los dos techos independientes bajo los que tiene que quedarse un ciclo. Se pone ámbar
cuando uno de los dos está en riesgo, y el tooltip dice cuál. Los dos números son de la dirección
que estás viendo, porque solo una dirección se reproduce a la vez.
Memoria. El motor guarda una imagen por cuadro y el navegador guarda los píxeles decodificados
detrás. El tamaño decodificado es ancho × alto × 4 bytes — el peso del PNG es irrelevante: un PNG
de 400 KB de un sprite de 720×1280 decodifica a 3,5 MB. Medido en una máquina de 32 GB, un set que
cicla aguanta bien hasta unos 366 MB y se cae a un precipicio en 380 MB: pasado eso el
navegador descarta cuadros y los vuelve a decodificar en cada vuelta, lo que cuesta 11-12 ms por
cuadro contra un presupuesto de 16,7 ms y empeora a medida que el ciclo avanza. Esa es la falla de
"arranca suave y después se entrecorta". El techo está acotado por bytes, no por cantidad de
cuadros: 116 cuadros a 360×640 (102 MB) van holgados, los mismos 116 a 720×1280 (408 MB) no.
El chip avisa bastante antes de ese precipicio, a 96 MB, a propósito: el navegador dimensiona esa caché según la memoria del sistema, así que la laptop de 8 GB de un jugador tiene mucho menos margen que la máquina donde se midió el techo, y dos personajes animando a la vez la comparten. Si lo cruzas, pasa la dirección por 🔧 Normalizar hasta la clase de tamaño de tu proyecto — al personaje se lo dibuja escalado a la altura del actor igual, así que autorar a la resolución del video de origen no compra nada más que memoria.
(El mismo precipicio aparece en el editor empaquetado: WebView2 y Chromium midieron idéntico, así que esto es la caché de imágenes compartida del navegador, no algo que controlen Ignitor ni el shell de escritorio.)
Ritmo. El avance de cuadros acumula tiempo real, así que en una pantalla de 60 Hz cada cuadro
termina durando un número entero de refrescos. Cuando la duración efectiva — ms ÷ speed — no es
múltiplo de 16,7 ms, el largo del hold alterna y el ciclo salta por más chicos que sean los sprites.
120 ms a speed 4 son 30 ms, o sea 1,8 refrescos: los cuadros alternan entre uno y dos refrescos,
44 % fuera de su duración autorada. El mismo clip a speed 1 son 7,2 refrescos y desvía apenas
11 %, que no se ve — por eso el chip sigue el desvío, no la alineación exacta, y se queda callado
con el default de 120 ms. (Ese 120 es la duración con la que se estampa un cuadro nuevo. Un
cuadro que no trae ms en absoluto — solo posible en JSON escrito o importado a mano — es otra
pregunta, y la responde el motor: aguanta 100 ms. Para esos el editor lee el número del motor,
así que lo que dice el thumbnail es lo que suena en el juego.) Los múltiplos de 16,7 ms son exactos:
100, 83, 67, 50, 33, 17 ms. ms
es un número entero, así que de esos solo 50 y 100 caen justo en un refresco — los otros van
redondeados (83 haciendo de 83,33) y desvían bastante menos de un uno por ciento, que tampoco se ve.
Y si quieres la fracción exacta igual, autórala como ms ÷ speed: 100 ms a speed 3 son 33,33 ms, dos
refrescos al dígito.
Un lado que tiene un solo cuadro no recibe veredicto de ritmo, y el tooltip lo dice. El judder es una duración de retención que alterna, y un cuadro solo nunca avanza: es una pose fija, no un ciclo, así que no hay entre qué alternar. Importa para las poses de idle, que suelen ser de un cuadro por lado mientras su hermana de caminata tiene cien.
Este techo es la razón por la que una importación de video hay que adelgazarla además de achicarla. Un ciclo de caminata lee bien con 8-16 cuadros por lado; 116 es densidad de video, y a esa densidad necesitas una duración de cuadro tan corta que cae entre refrescos.
Adelgazar un ciclo capturado#
Un clip sacado de un video o de un render 3D casi nunca cicla solo. El último cuadro no engancha con el primero, así que el ciclo pega un salto por vuelta por más buenos que sean los cuadros sueltos. Adelgazar son entonces dos decisiones, no una: qué ventana conservar, y recién después cuántos cuadros conservar adentro de ella.
Primero encuentra la ventana. Recorre el clip cuadro a cuadro y busca dónde la pose vuelve a repetirse — el mismo pie adelante, el mismo braceo, la misma inclinación de la cabeza. Ese tramo es un ciclo; lo que queda antes y después es otra toma, apenas distinta, del mismo movimiento, y por eso quedarse con "los primeros 40" igual suele saltar. Recorta a la ventana, confirma que el último cuadro fluye hacia el primero, y recién ahí empieza a tirar cuadros.
Después adelgaza por un divisor entero de la ventana, para que el espaciado quede parejo y el enganche siga funcionando. Una ventana de 40 cuadros baja limpio a 20 y de nuevo a 10; una de 36 te da 18, 12 o 9. Sacar cuadros de forma despareja — eliminar "los aburridos" — le deja al ciclo una renguera muy difícil de diagnosticar después, porque cada cuadro que quedó se ve correcto por su cuenta.
Ajustar un ciclo de caminata a la velocidad#
Un ciclo de caminata responde a un segundo reloj: el Walk speed del personaje (walkSpeed, píxeles
por segundo, en el Editor de Personajes). Las piernas se animan en el lugar y el actor se desplaza por
el cuarto por su cuenta, así que los dos coinciden solo cuando una vuelta completa del ciclo dura
exactamente lo que el actor tarda en cubrir una zancada de piso. Muy lento y los pies patinan; muy
rápido y el personaje hace moon-walk.
La zancada puedes leerla del arte mismo. Recorre la ventana mirando el pie que está apoyado en el suelo: se corre para atrás de manera pareja sobre el canvas, y lo que recorre a lo largo de todo el ciclo es la zancada, medida en píxeles de sprite. Escálala por el tamaño al que el actor se dibuja de verdad — su Size adjust, la zona de escala del cuarto, cualquier sesgo de tamaño del área caminable — y tienes la zancada en píxeles del cuarto. Entonces:
velocidad = zancada ÷ duración del ciclo
Una zancada de 147 px de cuarto con un ciclo de 10 cuadros a 50 ms — media vuelta por segundo — pide
unos 295 px/s. Corre esos mismos diez cuadros a 100 ms y la vuelta pasa a durar un segundo, así que el
mismo arte ahora pide 147 px/s; deja el personaje en 295 y va a cubrir el doble de piso del que sus
piernas dicen. Por eso también adelgazar un ciclo es seguro pero re-temporizarlo no: saca cuadros
manteniendo el largo de la vuelta y nada patina, pero cambia el ms y cambiaste a qué velocidad
puede caminar el personaje.
Un cuarto cuya zona de escala achica a los personajes con la distancia les cambia la zancada mientras caminan, y la velocidad se mantiene plana — así que un ciclo ajustado adelante patina atrás. Los faders Far Speed / Near Speed de la zona son el arreglo: ponlos en la misma proporción que las escalas lejana y cercana de la zona, y la velocidad del actor sigue a su tamaño en todo el cuarto.
Cada cuadro lleva: una ruta sprite, una duración ms, un flag mirror, offsetX/offsetY
(corrección de deriva), scaleW/scaleH (squash/stretch), y una opacity (0 = transparente,
1 = opaco, honrada al dibujar). Los cuadros se pueden deshabilitar individualmente — se saltean
en los previews, en el export y en el juego, sin borrarlos —, reordenar, multi-seleccionar,
copiar a otra dirección o incluso pegar entre animaciones vía un portapapeles propio del editor.
Un cuadro deshabilitado tampoco cuenta para el loop start, así que el punto de loop se queda en
el cuadro que elegiste; si deshabilitas todos los de un lado, ese lado degrada igual que si no
tuviera arte (su espejo horizontal, después la dirección autorada más cercana, después el sprite
estático).
Las animaciones de objeto llevan un campo por cuadro más: rot, en grados, que gira el sprite
sobre su centro. Se compone con la rotación autorada del propio hotspot y con lo que haya girado un
token en tiempo de ejecución, así que un molino o un cartel que se balancea es una columna de números
en vez de una carpeta de PNG pre-rotados — y a diferencia de hornear el ángulo en el modal
✎ Tweak, nunca reescribe un píxel, o sea que el arte queda a calidad completa por más pasadas que
hagas. El hit-test sigue la rotación: el cursor cae sobre los píxeles que ves, no sobre la caja sin
rotar del sprite. El campo sólo aparece en las animaciones de objeto, porque el dibujo del sprite del
hotspot es el único lugar donde el motor lee transformaciones por cuadro — los cuadros de personaje
giran sobre los pies e interactúan con mirror, que son decisiones de diseño abiertas, y las
animaciones de capa de sala y de GUI no leen ningún campo por cuadro.
Para contenido de personaje, un set de controles Auto-estabilizar hornea esos offsets
por-cuadro por ti. Auto-estabilizar X y Auto-estabilizar Y nivelan la línea de pies
detectada en todos los cuadros de una pasada (o dejan el torso quieto, en clips _walk/_run),
y un toggle de vista previa 🦶 Ancla de pies fija el píxel opaco más bajo al punto de suelo
para que lo que autoras coincida con cómo el motor planta al personaje. Todo es no destructivo —
un Reiniciar por dirección deshace los offsets horneados — y apagar el Ancla de pies te deja
ajustar el offsetY horneado a mano sin que el auto-anclaje te pelee.
Señales de token de efecto por cuadro#
La sección Frame Events del inspector es la superficie de autoría para el mecanismo
fired de core/animations.js: cada cuadro puede llevar una lista de strings de token de
efecto (f.events), lo más común SOUND:<id> para señales de paso/foley en cuadros
específicos de caminata, pero se acepta cualquier token libre a través de un botón + Free
token junto al picker tipado + Add SOUND (respaldado por los ids de audio del proyecto).
tickAnim() solo emite estos tokens en su arreglo fired cuando la reproducción cruza ese
cuadro — nunca los ejecuta; quien llama (el shell) los despacha a través del mismo pipeline
applyEffects() que cualquier otro token de efecto del motor.
Puntos de loop#
El checkbox loop de una animación controla si se sostiene en su último cuadro o si
envuelve. Cuando hace loop, un índice opcional de loop start divide el clip en una intro de
un solo disparo (cuadros 0..loopStart-1, reproducidos una vez al entrar) y un sostenido que
hace loop para siempre (cuadros loopStart..fin, repetidos desde loopStart en cada ciclo) —
visualizado en la línea de tiempo como una insignia en el primer cuadro sostenido, con los
cuadros de intro teñidos distinto. tickAnim() clampea un loopStart malo (NaN, no entero, o
fuera de rango) de vuelta a un índice válido en vez de confiar ciegamente en él, y el editor
refleja el mismo clamp en vivo cada vez que borras cuadros por debajo de un valor de loop-start
ya guardado.
Los dos ajustes son el valor global por defecto de la animación, y cualquier dirección concreta puede llevarle la contraria. Cada lado tiene su propio loop — Global, On u Off — y su propio loop start, y cada campo hereda el valor de la animación por separado hasta que digas otra cosa. Un lado con override queda marcado con ⟲ en su pestaña, y la línea de tiempo, la vista previa y el sandbox muestran el lado que estás mirando en vez del ajuste global, así lo que ves es lo que esa dirección hará de verdad. Así es como le das a un mismo clip un caminar que loopea en tres direcciones y se queda quieto en la cuarta, sin partirlo en dos animaciones. Cuando el motor sirve una dirección espejando otra, manda el override del lado autorado.
Campos de reproducción relacionados en la misma barra de flags: next (un clip de
seguimiento al que encadenar cuando este termina, o — random — para dejar que el motor elija
entre un subconjunto curado en cada ciclo), hold (un rango random de ms antes de avanzar,
activo solo en clips en loop), settle (un delay walk→idle, leído solo del clip _idle), y
speed (un multiplicador de reproducción, componible con una velocidad por-instancia en
hotspots de objeto). Todos los previews honran speed — el canvas del propio editor, el
Preview Sandbox y el reproductor del retrato del character editor —, así que lo que ves es el
ritmo al que va a correr el juego.
next también funciona en animaciones de objeto y room, no solo en personajes: un hotspot
que toca un clip de un disparo encadena a su seguimiento cuando el clip termina, y uno en loop
encadena al final de cada ciclo (o después de su hold, si tiene uno). Las formas
— sequence — y — random — se comportan igual que en un personaje.
Ojo con una cosa al elegir el seguimiento de un clip de objeto: las animaciones de objeto y room
se indexan por su nombre completo, sin recortarle ningún prefijo de entidad. El selector te
las ofrece así — grandfatherclock_ticktock, no ticktock — porque ese nombre completo es la
clave que busca el motor. Si tienes un proyecto viejo cuyo next apunta a un nombre corto que el
motor no puede resolver, vuelve a elegirlo de la lista y va a guardar el que sí resuelve.
Loop y loop start también se pueden anular por dirección: un control compacto pegado a la derecha de los tabs de dirección (Global / On / Off, más su propio campo de loop-start) edita solo el lado del tab activo — dejarlo en Global hereda los valores del header. Un tab con override lleva una marca pequeña ⟲, así un lado que anulaste y no volviste a visitar sigue siendo visible en vez de perderse. Es disperso a propósito: solo los lados que de verdad tocaste guardan un override, el resto sigue el default global.
Direcciones y espejado#
Las categorías direccionales (personaje) autoran hasta 8 pestañas de dirección: los 4
cardinales siempre están visibles, y un checkbox diagonals revela las 4 pestañas
diagonales (upleft/upright/downleft/downright) — desmarcado por defecto, así el contenido de 4
direcciones existente se ve exactamente igual que siempre. core/direction.js es el resolver
detrás de esto: DIR_MIRROR empareja left↔right y cada diagonal con su hermana horizontal, así
que autorar un solo lado de un par simétrico alcanza — en runtime, resolveFrames()
prefiere primero una dirección autorada exacta, después cae a dibujar los cuadros de la
hermana horizontal espejados, y solo después de eso degrada a la dirección autorada más
cercana por ángulo. El editor expone la misma relación de espejo directamente: una fila
Copy to de botones por dirección duplica los cuadros seleccionados entre pestañas
(atenuando la pestaña que coincide con la actual, ya que copiar a sí misma es un no-op), y un
checkbox mirror por cuadro marca un cuadro para dibujarse espejado horizontalmente a partir
de su arte guardado.
Vista previa y reproducción#
El panel de vista previa derecho reproduce en vivo la combinación de animación/dirección seleccionada — Play, Loop, Flip facing, una barra de scrub, onion-skin, marcadores de pivote, y zoom/pan. Dibuja el sprite y su fondo con el mismo filtrado que usa tu juego, así un proyecto pixel-art se previsualiza nítido en vez de más blando de lo que se publica. El zoom vuelve a renderizar el cuadro en vez de magnificar lo ya dibujado, así el arte de alta resolución conserva su detalle a medida que te acercas — y un botón 1:1 salta directo al zoom donde un píxel del PNG de origen cubre un píxel de tu pantalla, que es el zoom que quieres antes de retocar nada. El 1:1 además reencuadra: recentra sobre el arte del propio cuadro en vez de arrastrar el pan que tenías, así aterriza en la misma vista siempre, mires donde mires antes — a ese zoom el canvas es varias veces el panel, y un pan heredado te dejaba el personaje fuera de la vista. Si un sprite es demasiado alto para llegar a 1:1 dentro del rango de zoom, el botón te lo dice en vez de quedarse corto en silencio. Un botón Preview Sandbox abre el modal de comparación movible (compartido con la propia vista previa de animación ▶ del Editor de Personajes) para chequear la escala y el timing de un clip contra otro personaje o un fondo de juego en vivo. Con — piso en blanco — elegido reproduce el clip sobre un piso liso a la resolución del proyecto; elige un cuarto y el clip pasa a reproducirse dentro del frame real del juego — la sala en su viewport, walk-behinds, efectos de profundidad, capas de parallax, zonas de luz y la barra GUI principal real del proyecto, cada una con su toggle. Ahí es donde compruebas que un walk cycle pasa por detrás del mostrador por el que tiene que pasar, y que el personaje no queda medio comido por la barra de verbos, antes de lanzar el juego. El readout conserva la altura en pantalla del clip y el área caminable que lo escala, así que los números de tamaño no desaparecen al meterlo en una sala.
Un modo TWEAK cambia el mismo canvas a un editor cuadro por cuadro, y aloja dos clases de edición. Las herramientas de píxel — lápiz, borrador, balde, gotero, borrado mágico por color, un sello clonador (mantén Alt y haz clic para fijar la fuente de la que copia), y una herramienta de selección que arrastra un rectángulo al que después puedes aplicar Recortar, Rellenar o Borrar — retocan el arte directamente; la punta del pincel puede ser redonda o cuadrada y se dibuja bajo el cursor, así ves su huella antes de aplicarla. Las transformaciones geométricas remodelan el cuadro entero: espejar en horizontal/vertical y rotar 90° en sentido horario/antihorario son instantáneos, mientras que scale y warp abren un workspace de handles interactivos — scale arrastra una caja de ocho tiradores (con aspect-lock por defecto, o de un solo eje desde un borde); warp arrastra las cuatro esquinas de un quad, más rotación libre y skew — sobre un fondo damero para juzgar la edición contra la transparencia. Todo comparte una sola pila de deshacer/rehacer, y nada toca el PNG real hasta que un Save explícito escribe el cuadro editado de vuelta al archivo de asset — nada acá es destructivo hasta ese guardado.
Como un mismo PNG puede respaldar más de un cuadro o animación, TWEAK protege contra pisar un sprite compartido: antes de aplicar una edición a un archivo que otros cuadros también usan, te avisa y lista exactamente qué animaciones lo comparten, así nunca reescribes arte por debajo de otro clip sin darte cuenta.
En la práctica ese aviso casi no debería aparecerte sobre cuadros que agregaste tú, porque toda forma de agregar una copia de un cuadro le da su propio PNG: ⎘+ Duplicar, Pegar y las flechas Copiar a stagean una copia física de la imagen y apuntan el cuadro nuevo ahí. Así retocar el cuadro N+1 no toca el N, y pintarle un aro a los cuadros que miran a la derecha no alcanza a los que miran a la izquierda. Los objetos de inventario son la única excepción: el sprite de un item es un archivo único por definición, sin ranura por cuadro a la que copiar, así que sus cuadros siguen compartiendo.
O sea que el aviso es sobre arte que heredaste: dos animaciones apuntando al mismo archivo
porque las puso ahí un animations.json editado a mano, un proyecto viejo o una reutilización
deliberada. Cuando salta, nombra las animaciones involucradas, así puedes decidir si la edición va
para todas.
Esperar a ese Guardar explícito dejaría tus pinceladas sin dónde vivir, así que TWEAK mantiene en disco una copia de trabajo de cada cuadro sin guardar, que se refresca unos segundos después de que dejas de pintar. Nunca toca el arte: es un borrador, queda aparte y se descarta apenas guardas el cuadro. Si el editor se cierra, se cae o se recarga con cuadros sucios, al reabrir esa animación te ofrece restaurar los retoques sin guardar, y si aceptas vuelves a TWEAK con las pinceladas intactas. La misma red cubre el caso en que otra cosa reescribe un archivo que estabas pintando — un re-import o un normalize sobre esos cuadros: tus pinceladas se conservan y un aviso te dice que quedaron desfasadas de lo que hay en disco, para que mires antes de guardar encima de la versión nueva.
Deshacer#
El editor mantiene un historial de deshacer para el trabajo en sí — ↩ y ↪ en la barra, o
Ctrl+Z y Ctrl+Y (Ctrl+Shift+Z también sirve), en Mac con Cmd. Cubre las ediciones que
antes eran de ida sin vuelta: offsets de cuadro, espejado, reordenar, señales por cuadro, y
borrar o renombrar una animación entera. Se guardan cincuenta pasos por defecto, y puedes
subir o bajar ese número en Hub Config si prefieres cambiar memoria por más
memoria.
Dos cosas conviene saber. Guardar limpia el historial: una vez que la edición está en disco, el editor deja de ofrecerte volver más atrás. Y deshacer algo que movió archivos corta la rama de rehacer a propósito, en vez de dejarte un "rehacer" que tendría que recrear un archivo que ya no existe.
Flujo de trabajo#
- Elige una categoría (y, si aplica, una entidad) en el panel izquierdo, después + Nueva Anim — las animaciones de personaje y de GUI componen su clave desde pickers, nunca texto libre.
- Agrega cuadros: arrastra/suelta PNGs, duplica cuadros existentes, o corre un MP4 Import con chroma keying.
- Ajusta ms, mirror, offsetX/Y, y scaleW/H por cuadro en el inspector; adjunta
Frame Events (señales
SOUND:o tokens libres) en los cuadros que deban dispararlos. - Define el loop del clip, el loop start opcional, next, hold, y speed.
- Para contenido direccional (personaje), autora primero los cardinales; activa diagonals solo para las direcciones que necesiten arte propio — las hermanas espejadas son gratis.
- Chequea el timing y la alineación en el panel de vista previa, cambiando a modo TWEAK para arreglos a nivel de píxel.
- Guarda para escribir el
animations.jsondel proyecto.
Guardar fusiona, no sobrescribe. El
animations.jsonlo escribe más que este editor: el 🎬 Enviar a animación del editor de Cuartos, su botón gemelo en el de Personajes y el bake de FX agregan entradas al mismo archivo. Al guardar, esta pestaña manda el registro que leyó más lo que cambiaste, y el servidor junta las dos partes: las animaciones que aparecieron mientras tenías la pestaña abierta se conservan (un toast avisa "conservada(s) N anim(s) creada(s) fuera" y la lista se refresca para mostrarlas), y las que borraste siguen borradas. Lo único que no puede reconciliar es que dos editores toquen la misma animación a la vez — ahí gana el último Guardar.El editor es dueño de los DATOS, nunca del MOTOR. El propio comentario de cabecera de
core/animations.jslo dice sin vueltas: este archivo es "lógica de motor mantenida a mano ÚNICAMENTE — el editor nunca lo regenera." Guardar acá solo hace un post a/save-animations, que escribeanimations.json; el avance de cuadros (tickAnim()), los accesores del registro, y la cascada de resolución enresolveFrames()viven todos en JS escrito a mano que ningún botón del editor toca. Si necesitas un comportamiento de reproducción nuevo — un modo de loop nuevo, una regla de fallback nueva, algo nuevo quefiredpueda llevar — eso es una edición manual acore/animations.jsen sí, no algo alcanzable haciendo clic por esta herramienta. Todo lo que la UI del editor controla (listas de cuadros, timing, puntos de loop, flags de espejo, eventos) es DATO que fluye a través de ese código de motor sin tocar.