Aller au contenu principal

Animations de FreeMinecraftModels

FreeMinecraftModels importe les animations des fichiers .bbmodel et .fmmodel. Cette page présente les noms réservés, le minutage des images, l'interpolation, les modes de boucle et la cinématique inverse (IK).

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
idleouiEn l'absence de mouvement horizontal ; passe à walk si la vitesse sur X ou Z devient non nulle
walkouiPendant un mouvement horizontal ; passe à idle lorsque l'entité est au sol et que les vitesses sur X et Z sont toutes deux nulles
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 transition idle/walk lit la vitesse horizontale de l'entité sous-jacente : un mouvement uniquement vertical ne déclenche pas la marche. Les entités statiques et les déguisements n'ont pas d'entité sous-jacente utilisable à cette fin ; les props immobiles n'ont aucun mouvement horizontal. Leurs autres animations sont pilotées par les scripts, l'API ou 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 utilisent un autre ensemble d'animations​

Un déguisement ajoute un contrôleur par tick qui demande les animations au même moteur de lecture. Ses cinq noms réservés sont attack, jump, sneak, walk, idle, évalués dans cet ordre de priorité lorsque le compte à rebours d'une animation à lecture unique est inactif. Il avertit la console si le modèle n'a pas d'idle. Consultez les déguisements des joueurs pour le tableau complet et la limite de durée de ces animations.

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.
  • Il n'existe qu'un emplacement en attente : une nouvelle demande différée remplace la précédente. Une animation personnalisée démarre immédiatement sans état courant. Un état intégré mis en attente ne peut pas avancer depuis un état courant nul ; utilisez blend=false dans ce cas.
  • 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.
  • Utilisez les noms exacts pour les animations personnalisées et hasAnimation : ces deux recherches distinguent les majuscules des minuscules. Les demandes de lecture d'états intégrés acceptent une casse différente, mais leur enregistrement exige toujours le nom en minuscules dans le modèle.
  • Lorsqu'une animation personnalisée sans boucle se termine, elle demande le dernier état intégré validé enregistré, ou idle si aucun n'a été enregistré. Il s'agit du dernier état intégré que le gestionnaire a quitté, qui peut différer de celui actif juste avant l'animation personnalisée. Si l'état de retour demandé n'existe pas, l'état personnalisé reste sélectionné sans nouvelle mise à jour des images.
  • playAnimation renvoie false pour un nom inconnu, sauf pour les demandes d'attaque ignorées décrites ci-dessous. Un retour positif signifie que la demande a été acceptée, pas qu'une image visible a été affichée.
  • 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.
  • La mort est terminale dans la machine à états partagée : les nouvelles demandes renvoient false, les transitions en attente sont supprimées et arrêter les animations ne change pas cet état. La méthode publique d'arrêt envoie aussi une demande d'arrêt distincte à Bedrock ; ne l'utilisez pas pour gérer une séquence de mort sur les deux clients.

Cadence et durée​

  • Blockbench stocke la durée d'animation en secondes. FMM la convertit avec ceil(seconds x 20) : la durée est toujours un nombre entier de ticks, et les animations courtes sont arrondies vers le haut.
  • 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 images clés conservent leur position fractionnaire en ticks (20 x time, sans arrondi) : une image clé à 0,37 s contribue à l'interpolation entre ticks. Les images sont échantillonnées aux ticks entiers de 0 à duration - 1 ; une image clé placée exactement à la fin déclarée influence l'interpolation, mais n'est pas elle-même échantillonnée. Placez la pose finale avant cette limite si une image affichée doit l'atteindre.
  • 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 détermine l'animation exportée pour Bedrock :

Mode de boucle BlockbenchExport Bedrock
loop"loop": true
once"loop": false
hold"loop": "hold_on_last_frame"

La lecture Java utilise la règle de boucle de l'état intégré ou l'argument loop passé à une animation personnalisée. Elle ne détermine pas sa règle à partir de ce champ Blockbench : la lecture peut donc différer entre Java et Bedrock si ces réglages ne concordent pas.

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 images clés de son et d'instructions de timeline des pistes d'effets de Blockbench. FMM n'importe que les images clés de particules d'une piste d'effets ; voir Particules. Jouez plutôt les sons 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 avec ik_source (os racine) et ik_target (os terminal ou locator) définit une chaîne. La détection examine la hiérarchie et prend aussi en charge les recherches entre frères et vers les descendants ; des cibles sœurs au niveau racine produisent une chaîne contenant seulement l'os source.
  2. Seules les images clés de position du null object pilotent l'IK. Elles deviennent un décalage par image ajouté à la position de repos de l'os ou du locator cible. Le solveur n'utilise pas la position de repos stockée du contrôleur comme origine de la cible. La rotation et l'échelle ne pilotent pas l'IK.
  3. À chaque tick, les chaînes IK associées à l'animation courante reçoivent leurs décalages et sont résolues. Une chaîne associée sans données pour cette image est réinitialisée. Chaque changement d'animation, ainsi que stopCurrentAnimations(), efface les rotations IK de toutes les chaînes : une pose IK n'est donc pas reportée sur l'animation suivante.
  4. lock_ik_target_rotation est lu et stocké, mais le solveur actuel ne l'applique pas.

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>.a_<hex>, où <hex> correspond aux octets UTF-8 du nom de l'animation écrits en hexadécimal. Deux noms qui ne diffèrent que par des caractères interdits par Bedrock reçoivent donc des identifiants distincts.
  • 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.
  • La géométrie exclut hitbox, les os de nom générés fmm_nametag_bone_* et les points de monture m_ ; les ancrages tag_ créés par l'auteur restent présents. L'exporteur d'animations écrit les pistes précalculées sans appliquer le même filtre d'os visuels : n'animez donc pas un os de monture exclu en espérant voir sa géométrie.
  • L'export Bedrock utilise les images précalculées de rotation, position et échelle des os. Il n'y incorpore pas les rotations produites à l'exécution par le solveur IK ; vérifiez séparément sur Bedrock les modèles qui dépendent de l'IK.
  • 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.
  • Les images clés de particules deviennent la timeline particle_effects de l'animation, que les clients Bedrock jouent donc nativement. Voir Particules.

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)

Consultez le guide de l'API pour les développeurs et Lua : API des props pour les interfaces associées.

Les valeurs Lua par défaut diffèrent : context.prop:play_animation(name) utilise blend=true, loop=true, tandis que entity.model:play_animation(name) utilise false, false. Passez explicitement les deux booléens lorsque cette distinction compte.

Dépannage​

Mon animation ne se joue jamais automatiquement. La machine à états partagée reconnaît les noms minuscules spawn, idle, walk, attack et death. Le mouvement pilote les transitions idle/walk ; les attaques et la mort nécessitent toujours leur déclencheur à l'exécution. Les autres noms exigent un appel explicite à playAnimation / play_animation, sauf les noms supplémentaires sélectionnés par le contrôleur de déguisement.

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 de ma timeline Blockbench ne font rien. Les images clés de son ne sont pas importées. Déclenchez les sons depuis un script Lua ou votre propre plugin. Les images clés de particules sont importées ; si elles ne s'affichent pas, voir Particules.