Aller au contenu principal

Animations de FreeMinecraftModels

Cette page documente ce que FreeMinecraftModels fait réellement des données d'animation contenues dans un fichier .bbmodel ou .fmmodel : quels noms sont spéciaux, comment les keyframes sont précalculées, quels modes d'interpolation et de boucle sont respectés, et comment l'IK est pilotée. Elle est volontairement conservatrice — tout ce qui suit est visible dans le pipeline d'importation et d'exécution.

Pour les règles de nommage des os et le reste du contrat d'importation, voir les Notes de création de modèles.

Les cinq animations d'état

FreeMinecraftModels associe exactement cinq noms d'animation en minuscules à des états d'exécution automatiques. Tout le reste dans le modèle est une animation personnalisée.

Nom de l'animationBoucleQuand elle se joue
spawnnonUne seule fois, à la création du modèle. Enchaîne sur idle quand elle se termine
idleouiTant que la vélocité de l'entité sous-jacente est inférieure ou égale à 0.08
walkouiTant que la vélocité de l'entité sous-jacente est supérieure à 0.08
attacknonLorsqu'elle est déclenchée ; revient à idle quand elle se termine
deathnonLors de removeWithDeathAnimation()

Règles qui découlent de la façon dont la machine à états est construite :

  • L'état de départ est spawn si le modèle en possède un, sinon idle. Un modèle qui n'a ni l'un ni l'autre n'a aucun état courant : rien ne s'anime tant que quelque chose n'est pas joué explicitement.
  • Seules les animations qui existent dans le modèle obtiennent un état. Un modèle avec un walk mais sans idle ne sort jamais de walk de lui-même.
  • La bascule idle/walk lit la vélocité de l'entité sous-jacente : c'est donc en réalité une fonctionnalité de DynamicEntity. Les entités statiques et les déguisements de joueurs n'ont pas d'entité sous-jacente à cet effet et restent simplement en idle ; l'armor stand qui sert de support à un prop ne bouge pas, donc les props restent eux aussi en idle. Ces trois cas pilotent leurs véritables animations via des scripts, l'API, ou (pour les déguisements) le contrôleur de déguisement de FMM.
jump n'existe qu'en tant qu'entrée d'énumération ici

JUMP existe dans l'énumération AnimationStateType, et l'état walk demande bien une transition de saut lorsque l'entité quitte le sol — mais aucun état de saut n'est jamais enregistré, donc cette demande n'aboutit à rien. Une animation nommée jump n'est pas morte pour autant : elle se comporte comme n'importe quelle autre animation personnalisée et doit être déclenchée manuellement. Les déguisements de joueurs font exception — ils utilisent un contrôleur distinct où jump est bien câblé.

Les déguisements de joueurs utilisent un jeu différent

Un déguisement de joueur n'exécute pas la machine à états ci-dessus. Il possède son propre contrôleur exécuté à chaque tick avec cinq noms réservés — attack, jump, sneak, walk, idle — évalués dans cet ordre de priorité, et il avertit dans la console si le modèle n'a pas d'idle. Voir Déguisements des joueurs pour le tableau complet et la mise en garde sur le timing des animations à coup unique.

Animations personnalisées

Toute animation dont le nom n'est pas l'un des cinq ci-dessus peut quand même être jouée par son nom :

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()
  • blend n'effectue pas de fondu enchaîné. true met l'animation en file d'attente pour qu'elle démarre après la fin du tick de l'état courant ; false interrompt et bascule immédiatement.
  • loop ne s'applique qu'aux animations personnalisées. Un état intégré utilise son propre réglage de boucle, quelle que soit la valeur passée.
  • Lorsqu'une animation personnalisée sans boucle se termine, l'entité revient au dernier état intégré validé (ce qu'elle faisait auparavant), ou à idle s'il n'y en avait aucun.
  • playAnimation renvoie false lorsque le nom ne correspond ni à un état enregistré ni à une animation du modèle.
  • stopCurrentAnimations() bascule vers idle si le modèle en possède un ; sinon il quitte l'état courant et laisse le modèle sans animation active.
  • Pendant qu'une animation personnalisée est en cours, une demande de attack, attack_melee ou attack_ranged est ignorée afin qu'une séquence scriptée ne soit pas interrompue par un combat de routine.

Timing et durée

  • Blockbench stocke la longueur d'animation en secondes. FMM la convertit avec ceil(secondes x 20), donc la durée est toujours un nombre entier de ticks et les animations courtes sont arrondies vers le haut plutôt que vers le bas.
  • Chaque animation est précalculée à l'importation dans un tableau plat d'images par tick. La lecture est une simple consultation de tableau par tick, pas une interpolation en direct.
  • Les animations en boucle s'indexent avec counter % duration ; celles sans boucle se bloquent sur la dernière image puis cessent d'appliquer tout changement.
  • Les temps des keyframes conservent leur position fractionnaire en ticks (20 x time, non arrondi), donc une keyframe à 0,37 s tombe entre deux ticks et est interpolée correctement au lieu d'être calée ou perdue. La dernière keyframe d'une animation est préservée plutôt que tronquée par l'arrondi.
  • Si deux keyframes du même canal tombent exactement au même instant, c'est la dernière dans l'ordre du fichier qui l'emporte.
  • Une keyframe dont le temps n'est pas un nombre fini interrompt cette piste et produit un unique avertissement Malformed animation timeline for model ... par animation, tandis que les autres animations du modèle continuent d'être converties.

Les animations de longueur nulle sont valides

Une animation dont la longueur est 0 (ou négative) est traitée comme une pose statique intentionnelle — un choix de création courant pour le mobilier et les autres props qui ont besoin d'une entrée « aucune animation » nommée. Elle est ignorée silencieusement, sans avertissement, et ne produit aucune image.

Modes de boucle

Le réglage de boucle de Blockbench est lu directement depuis l'animation :

Mode de boucle BlockbenchExécution JavaExport Bedrock
loopse répète indéfiniment"loop": true
oncese joue jusqu'au bout puis s'arrête"loop": false
holdse joue jusqu'au bout et maintient la dernière image"loop": "hold_on_last_frame"

Types d'interpolation

Chaque keyframe porte son propre type d'interpolation, et le segment qui mène à une keyframe utilise le type de cette keyframe. Quatre types sont pris en charge :

Type BlockbenchComportement dans FMM
linearInterpolation linéaire simple
catmullromInterpolation lissée (accélération/décélération)
bezierApproximée avec des points de contrôle fixes 0.42 / 0.58 — FMM ne lit pas les poignées de Bézier par keyframe
stepReste sur la valeur précédente jusqu'à la keyframe suivante

Tout ce qui sort de cet ensemble échoue à l'analyse et est signalé comme une timeline malformée.

Canaux animés

Trois canaux sont précalculés par os : rotation, position et échelle. Points importants pour la création :

  • Les valeurs de position sont divisées par 16 (pixels Blockbench vers blocs).
  • Les valeurs de rotation sont converties en radians.
  • Les format_version 5 et ultérieures de Blockbench inversent le signe des rotations X et Y ainsi que de la position X. FMM compense automatiquement en fonction de la version de format déclarée, donc ne corrigez pas cela à la main — mais ne mélangez pas non plus une déclaration v5 avec des données au format v4.
  • Un os sans keyframe sur un canal conserve sa valeur de repos pour ce canal ; un os sans aucune image pour un tick donné est réinitialisé à rotation 0,0,0, translation 0,0,0, échelle 1,1,1.
  • Les points de données des keyframes peuvent être écrits sous forme de chaînes dans le .bbmodel. FMM les analyse comme de simples nombres — une chaîne vide devient 1 pour l'échelle et 0 sinon, et tout ce qui est inanalysable journalise Failed to parse supposed number value ... et devient 0. Les expressions Molang ne sont pas évaluées.
  • Seul le premier point de données d'une keyframe est lu, donc les valeurs pre/post distinctes de Blockbench sur une keyframe step sont fusionnées en une seule.

Ce qui n'est pas animé

  • L'os hitbox. Les pistes d'animation qui le ciblent sont purement et simplement ignorées.
  • Les pistes d'effets de Blockbench (animateurs de son, de particules et d'instructions de timeline). Tout animateur dont le type n'est pas bone ou null_object est ignoré, donc FMM ne déclenchera ni sons ni particules depuis une timeline d'animation. Pilotez-les plutôt depuis un script Lua ou depuis votre propre plugin.
  • Les os qui n'ont pas pu être résolus par leur nom. Une piste pointant vers un os manquant journalise Failed to get bone <name> from model <model>! et est ignorée.

Cinématique inverse (IK)

Les null objects de Blockbench servent de contrôleurs d'IK. FMM résout les chaînes à l'exécution avec FABRIK (Forward And Backward Reaching Inverse Kinematics), plafonné à 10 itérations avec une tolérance de 0.001.

Comment tout cela s'articule :

  1. Un null object possédant à la fois ik_source (os racine de la chaîne) et ik_target (os terminal ou locator) définit une chaîne. La chaîne est découverte en remontant la hiérarchie depuis la cible jusqu'à la source.
  2. Seules les keyframes de position du null object sont lues. Elles deviennent un décalage d'objectif par image, relatif à la position de repos du contrôleur ; les pistes de rotation et d'échelle d'un null object sont ignorées.
  3. À chaque tick, le décalage d'objectif de l'image courante est appliqué et la chaîne est résolue. Sur une image sans données d'IK, les rotations d'IK de la chaîne sont réinitialisées à la place.
  4. lock_ik_target_rotation sur le null object est lu depuis le modèle.

Les chaînes qui ne peuvent pas être résolues sont ignorées avec un avertissement console nommé — voir les Notes de création de modèles pour les messages exacts et les contraintes de création.

Export Bedrock

Chaque modèle converti écrit également un fichier d'animation Bedrock dans animations/<model_id>.animation.json à l'intérieur du bundle généré :

  • Les identifiants d'animation sont animation.fmm.<model_id>.<animation_name>, avec des noms nettoyés pour Bedrock.
  • animation_length est la durée en secondes, avec un plancher à 0.05 afin qu'une animation d'un seul tick reste valide.
  • Le mode de boucle est mappé comme indiqué dans le tableau Modes de boucle.
  • Un modèle sans aucune animation reçoit tout de même une entrée idle inerte afin que la définition d'entité Bedrock reste valide.
  • Seuls les os visuels sont exportés. hitbox, les os de nametag fmm_nametag_bone_* générés automatiquement et les os de point de monture m_ sont exclus de la géométrie et donc du bloc d'os d'animation. L'os tag_ que vous créez n'est pas exclu — il est exporté comme n'importe quel autre os (généralement vide, sans cube) ; seul son équivalent nametag généré est filtré.
  • Un contrôleur d'animation est généré par animation, commuté par une propriété d'entité : c'est ainsi que FMM joue une animation précise sur un client Bedrock.

Voir Sortie du resource pack pour savoir où le bundle atterrit sur le disque.

Jouer des animations depuis d'autres systèmes

AppelantPoint d'entrée
Plugin (Java)ModeledEntity#playAnimation(String, boolean blend, boolean loop) / #stopCurrentAnimations() / #hasAnimation(String)
Script Lua de propcontext.prop:play_animation(name, blend, loop) / context.prop:stop_animation()
Toute table d'entité Luaentity.model:play_animation(name, blend, loop) / entity.model:stop_animations() (disponible lorsque entity.is_modeled vaut true)

Voir le Guide API et développeur et Lua : API Prop et Item pour les surfaces environnantes.

Dépannage

Mon animation ne se joue jamais automatiquement. Seules spawn, idle, walk, attack et death se déclenchent d'elles-mêmes. Tout le reste nécessite un appel explicite à playAnimation / play_animation.

Mon modèle ne fait strictement rien. Il n'a probablement ni animation spawn ni animation idle, donc aucun état n'est activé à la création. Ajoutez une animation idle.

Mon animation se joue mais rien ne bouge. Vérifiez la longueur de l'animation. Une animation de longueur nulle est traitée comme une pose statique et est ignorée silencieusement, par conception.

Les rotations sont en miroir. Vérifiez meta.format_version dans le .bbmodel. FMM inverse le signe des rotations X/Y pour la version de format 5 et ultérieures ; un fichier qui déclare une version mais contient les données de l'autre ressortira en miroir.

La console affiche Malformed animation timeline for model .... Une piste de cette animation n'a pas pu être lue ou interpolée. L'avertissement se déclenche une fois par animation et nomme l'os ou le contrôleur d'IK concerné ; les autres animations du modèle sont tout de même converties.

Les sons et particules de ma timeline Blockbench ne font rien. Les pistes d'effets ne sont pas importées. Déclenchez-les depuis un script Lua ou votre propre plugin.