Animaciones de FreeMinecraftModels
FreeMinecraftModels importa datos de animación de archivos .bbmodel y .fmmodel. Esta página explica los nombres reservados, los tiempos de los fotogramas, la interpolación, los modos de bucle y la cinemática inversa (IK).
Para las reglas de nomenclatura de huesos y el resto del contrato de importación, consulta las Notas de Creación de Modelos.
Las cinco animaciones de estado
FreeMinecraftModels vincula exactamente cinco nombres de animación en minúsculas a estados automáticos durante la ejecución. Todo lo demás en el modelo es una animación personalizada.
| Nombre de la animación | En bucle | Cuándo se reproduce |
|---|---|---|
spawn | no | Una vez, al crear el modelo. Pasa a idle cuando termina |
idle | sí | Sin movimiento horizontal; cambia a caminar cuando la velocidad en X o Z es distinta de cero |
walk | sí | Con movimiento horizontal; cambia a reposo cuando la entidad está en el suelo y las velocidades en X y Z son cero |
attack | no | Cuando se activa; vuelve a idle cuando termina |
death | no | Con removeWithDeathAnimation() |
Reglas que se derivan de cómo está construida la máquina de estados:
- El estado inicial es
spawnsi el modelo lo tiene y, si no,idle. Un modelo que no tenga ninguno de los dos no tiene estado actual, por lo que no se anima nada hasta que algo se reproduzca explícitamente. - Solo las animaciones que existen en el modelo obtienen un estado. Un modelo con
walkpero sinidlenunca sale dewalkpor sí mismo. - El cambio idle/walk lee la velocidad horizontal de la entidad subyacente; el movimiento vertical por sí solo no activa la animación de caminar. Las entidades estáticas y los disfraces de jugador no tienen una entidad subyacente para este propósito, y los elementos decorativos inmóviles no tienen movimiento horizontal. Sus animaciones adicionales se controlan mediante scripts, la API o el controlador de disfraces de FMM.
jump solo existe en el enumJUMP existe en el enum AnimationStateType, y el estado de caminar sí solicita una transición de salto cuando la entidad deja el suelo, pero nunca se registra ningún estado de salto, así que esa solicitud no resuelve a nada. Una animación llamada jump no está muerta: simplemente se comporta como cualquier otra animación personalizada y hay que activarla manualmente. Los disfraces de jugador son la excepción: usan un controlador aparte en el que jump sí está conectado.
Los disfraces de jugador usan otro conjunto
Un disfraz de jugador añade un controlador por tick que solicita animaciones mediante el mismo motor de reproducción. Sus cinco nombres reservados son attack, jump, sneak, walk e idle, evaluados en ese orden de prioridad cuando no está activa la cuenta atrás de una animación de una sola ejecución. Avisa en la consola si el modelo no tiene idle. Consulta Disfraces de Jugador para ver la tabla completa y la limitación temporal de esas animaciones.
Animaciones personalizadas
Cualquier animación cuyo nombre no sea uno de los cinco anteriores puede reproducirse igualmente por nombre:
modeledEntity.playAnimation("open", /* blend */ true, /* loop */ false);
modeledEntity.stopCurrentAnimations();
boolean exists = modeledEntity.hasAnimation("open");
context.prop:play_animation("open", true, false)
context.prop:stop_animation()
blendno hace una fusión progresiva.truepone la animación en cola para que empiece después de que se complete el tick del estado actual;falseinterrumpe y cambia de inmediato.- Solo hay una posición en cola; una nueva solicitud en cola sustituye a la anterior. Una animación personalizada empieza inmediatamente si no hay estado actual. Un estado integrado en cola no puede avanzar desde un estado actual nulo; usa
blend=falseen ese caso. loopsolo se aplica a las animaciones personalizadas. Un estado integrado usa su propia configuración de bucle sin importar lo que pases.- Usa nombres exactos para las animaciones personalizadas y
hasAnimation: ambas búsquedas distinguen mayúsculas y minúsculas. Las solicitudes de reproducción de estados integrados aceptan otras combinaciones de mayúsculas, pero registrar el estado sigue requiriendo su nombre en minúsculas en el modelo. - Cuando una animación personalizada sin bucle termina, solicita el último estado integrado confirmado que se haya guardado, o
idlesi no se guardó ninguno. Se trata del último estado integrado que el gestor abandonó anteriormente, que puede diferir del estado activo justo antes de la animación personalizada. Si el estado de retorno solicitado no existe, el estado personalizado sigue seleccionado sin actualizar más fotogramas. playAnimationdevuelvefalsepara un nombre desconocido, salvo las solicitudes de ataque suprimidas que se describen más abajo. Un resultado positivo indica que la solicitud se aceptó, no que ya se haya mostrado un fotograma.stopCurrentAnimations()pasa aidlecuando el modelo tiene esa animación; en caso contrario sale del estado actual y deja el modelo sin ninguna animación activa.- Mientras se está reproduciendo una animación personalizada, una solicitud de
attack,attack_meleeoattack_rangedse descarta para que una secuencia scriptada no se vea interrumpida por el combate rutinario. - La muerte es terminal en la máquina de estados compartida: las nuevas solicitudes devuelven
false, se descartan las transiciones en cola y detener las animaciones no cambia ese estado. El método público de parada también envía una solicitud de parada independiente a Bedrock; no lo uses para controlar una secuencia de muerte en ambos clientes.
Tiempos y duración
- Blockbench guarda la duración de la animación en segundos. FMM la convierte con
ceil(seconds x 20), así que la duración es siempre un número entero de ticks y las animaciones cortas se redondean hacia arriba en lugar de hacia abajo. - Cada animación se hornea en un array plano de frames por tick durante la importación. La reproducción es una consulta al array por tick, no una interpolación en vivo.
- Las animaciones en bucle se indexan con
counter % duration; las que no están en bucle se fijan al último frame y dejan de renderizar más cambios. - Los tiempos de los fotogramas clave conservan su posición de tick fraccionaria (
20 x time, sin redondear), por lo que uno situado en 0,37 s contribuye a la interpolación entre ticks. Los fotogramas se muestrean en ticks enteros de0aduration - 1; un fotograma clave situado exactamente en el final declarado influye en la interpolación, pero no se muestrea directamente. Sitúa la pose final antes de ese límite si un fotograma visible debe alcanzarla. - Si dos keyframes del mismo canal caen exactamente en el mismo tiempo, gana el que aparece después en el orden del archivo.
- Un keyframe con un tiempo no finito aborta esa pista y produce un único aviso
Malformed animation timeline for model ...por animación, mientras que las demás animaciones del modelo se siguen convirtiendo.
Las animaciones de longitud cero son válidas
Una animación cuya longitud sea 0 (o negativa) se trata como una pose estática intencionada: una decisión de creación habitual en muebles y otros props que necesitan una entrada llamada "sin animación". Se omite en silencio, sin aviso, y no aporta ningún frame.
Modos de bucle
La configuración de bucle de Blockbench controla la animación exportada a Bedrock:
| Modo de bucle de Blockbench | Exportación a Bedrock |
|---|---|
loop | "loop": true |
once | "loop": false |
hold | "loop": "hold_on_last_frame" |
La reproducción en Java usa la política de bucle del estado integrado o el argumento loop de una animación personalizada. No elige esa política a partir de este campo de Blockbench, por lo que la reproducción en Java y Bedrock puede diferir si sus ajustes no coinciden.
Tipos de interpolación
Cada keyframe lleva su propio tipo de interpolación, y el segmento que entra hacia un keyframe usa el tipo de ese keyframe. Se admiten cuatro:
| Tipo de Blockbench | Comportamiento en FMM |
|---|---|
linear | Interpolación lineal directa |
catmullrom | Interpolación suavizada (ease in/out) |
bezier | Aproximada con puntos de control fijos de 0.42 / 0.58: FMM no lee los manejadores bezier de cada keyframe |
step | Salta al valor anterior hasta el siguiente keyframe |
Cualquier cosa fuera de ese conjunto no se puede analizar y se reporta como una timeline malformada.
Canales animados
Se hornean tres canales por hueso: rotación, posición y escala. Notas que importan al crear modelos:
- Los valores de posición se dividen entre 16 (píxeles de Blockbench a bloques).
- Los valores de rotación se convierten a radianes.
- La
format_version5 de Blockbench y posteriores invierten el signo de la rotación X e Y y de la posición X. FMM lo compensa automáticamente según la versión de formato declarada, así que no lo corrijas a mano, pero tampoco mezcles una declaración v5 con datos con forma de v4. - Un hueso sin keyframes en un canal conserva su valor de reposo para ese canal; un hueso sin ningún frame para un tick dado se restablece a rotación
0,0,0, traslación0,0,0, escala1,1,1. - Los puntos de datos de los keyframes pueden estar escritos como cadenas de texto en el
.bbmodel. FMM los analiza como números simples: una cadena vacía se convierte en1para la escala y en0en los demás casos, y cualquier cosa que no se pueda analizar registraFailed to parse supposed number value ...y se convierte en0. Las expresiones Molang no se evalúan. - Solo se lee el primer punto de datos de un keyframe, así que los valores pre/post separados de Blockbench en un keyframe step se colapsan en uno solo.
Qué no se anima
- El hueso
hitbox. Las pistas de animación que lo tengan como objetivo se omiten directamente. - Los fotogramas clave de sonido y de instrucciones de timeline de las pistas de efectos de Blockbench. FMM solo importa los fotogramas clave de partículas de una pista de efectos; consulta Partículas. Reproduce los sonidos desde un script de Lua o desde tu propio plugin.
- Los huesos que no se pudieron resolver por nombre. Una pista que apunte a un hueso inexistente registra
Failed to get bone <name> from model <model>!y se omite.
Cinemática inversa (IK)
Los null objects de Blockbench actúan como controladores de IK. FMM resuelve las cadenas en runtime con FABRIK (Forward And Backward Reaching Inverse Kinematics), limitado a 10 iteraciones con una tolerancia de 0.001.
Cómo encaja todo:
- Un null object con
ik_source(hueso raíz) eik_target(hueso final o locator) define una cadena. La detección comprueba la jerarquía y también admite búsquedas entre hermanos y hacia abajo; los objetivos hermanos en el nivel raíz producen una cadena formada solo por el hueso de origen. - Solo los fotogramas clave de posición del null object controlan la IK. Se convierten en un desplazamiento por fotograma que se suma a la posición de reposo del hueso o locator objetivo. El solucionador no usa la posición de reposo almacenada del controlador como base del objetivo. La rotación y la escala no controlan la IK.
- En cada tick, las cadenas IK asociadas a la animación actual reciben sus desplazamientos y se resuelven. Una cadena asociada sin datos para ese fotograma se limpia. Cada cambio de animación, y también
stopCurrentAnimations(), limpia las rotaciones IK de todas las cadenas, así que una pose de IK no pasa a la siguiente animación. lock_ik_target_rotationse lee y almacena, pero el solucionador actual no lo aplica.
Las cadenas que no se pueden resolver se omiten con un aviso de consola que las nombra; consulta las Notas de Creación de Modelos para ver los mensajes exactos y las restricciones de creación.
Exportación a Bedrock
Cada modelo convertido escribe además un archivo de animación de Bedrock en animations/<model_id>.animation.json dentro del bundle generado:
- Los identificadores de animación son
animation.fmm.<model_id>.a_<hex>, donde<hex>son los bytes UTF-8 del nombre de la animación escritos en hexadecimal. Por eso, dos nombres que solo difieren en caracteres que Bedrock no admite reciben identificadores distintos. animation_lengthes la duración en segundos, con un mínimo de0.05para que una animación de un solo tick siga siendo válida.- El modo de bucle se mapea como se muestra en la tabla de Modos de Bucle.
- Un modelo sin animaciones sigue recibiendo una única entrada
idlesin efecto para que la definición de entidad de Bedrock siga siendo válida. - La geometría excluye
hitbox, los huesos de etiqueta generadosfmm_nametag_bone_*y los puntos de montajem_; conserva los anclajestag_creados por el autor. El exportador de animaciones escribe las pistas precalculadas sin aplicar ese mismo filtro de huesos visuales, así que no animes huesos de montaje excluidos esperando ver su geometría. - La exportación de animaciones Bedrock usa fotogramas precalculados de rotación, posición y escala de huesos. No incorpora a esas pistas las rotaciones del solucionador IK durante la ejecución; verifica por separado en Bedrock los modelos que dependan de IK.
- Se genera un controlador de animación por animación, conmutado por una propiedad de entidad, que es la forma en que FMM reproduce una animación concreta en un cliente de Bedrock.
- Los fotogramas clave de partículas se convierten en la timeline
particle_effectsde la animación, así que los clientes de Bedrock los reproducen de forma nativa. Consulta Partículas.
Consulta Salida del Resource Pack para saber dónde acaba el bundle en el disco.
Reproducir animaciones desde otros sistemas
| Quien llama | Punto de entrada |
|---|---|
| Plugin (Java) | ModeledEntity#playAnimation(String, boolean blend, boolean loop) / #stopCurrentAnimations() / #hasAnimation(String) |
| Script de Lua de un prop | context.prop:play_animation(name, blend, loop) / context.prop:stop_animation() |
| Cualquier tabla de entidad de Lua | entity.model:play_animation(name, blend, loop) / entity.model:stop_animations() (disponible cuando entity.is_modeled es true) |
Consulta la Guía de API y desarrollo y Lua: API de elementos decorativos para conocer las interfaces relacionadas.
Los valores predeterminados de Lua son distintos: context.prop:play_animation(name) usa blend=true, loop=true, mientras que entity.model:play_animation(name) usa false, false. Pasa ambos booleanos explícitamente cuando esa diferencia sea relevante.
Solución de problemas
Mi animación nunca se reproduce automáticamente.
La máquina de estados compartida reconoce spawn, idle, walk, attack y death en minúsculas. El movimiento controla las transiciones idle/walk; los ataques y la muerte siguen necesitando su activación durante la ejecución. Los demás nombres requieren una llamada explícita a playAnimation / play_animation, salvo los nombres adicionales que selecciona el controlador de disfraces.
Mi modelo no hace absolutamente nada.
Lo más probable es que no tenga ni una animación spawn ni una idle, así que no se entra en ningún estado al crearlo. Añade un idle.
Mi animación se reproduce pero nada se mueve. Revisa la duración de la animación. Una animación de longitud cero se trata como una pose estática y se omite en silencio por diseño.
Las rotaciones salen invertidas.
Revisa meta.format_version en el .bbmodel. FMM invierte el signo de la rotación X/Y para la versión de formato 5 y posteriores; un archivo que declare una versión pero contenga los datos de la otra saldrá invertido.
La consola dice Malformed animation timeline for model ....
Una pista de esa animación no se pudo leer o interpolar. El aviso se dispara una vez por animación y nombra el hueso o el controlador de IK implicado; las demás animaciones del modelo se siguen convirtiendo.
Los sonidos de mi timeline de Blockbench no hacen nada. Los fotogramas clave de sonido no se importan. Activa los sonidos desde un script de Lua o desde tu propio plugin. Los fotogramas clave de partículas sí se importan; si no se ven, consulta Partículas.