Zum Hauptinhalt springen

FreeMinecraftModels Animationen

FreeMinecraftModels importiert Animationsdaten aus .bbmodel- und .fmmodel-Dateien. Diese Seite beschreibt reservierte Namen, Frame-Zeiten, Interpolation, Loop-Modi und inverse Kinematik (IK).

Für Bone-Namensregeln und den Rest des Import-Vertrags siehe Hinweise zur Modellerstellung.

Die fünf Zustandsanimationen​

FreeMinecraftModels bindet genau fünf kleingeschriebene Animationsnamen an automatische Laufzeitzustände. Alles andere im Modell ist eine benutzerdefinierte Animation.

AnimationsnameLooptWann sie abgespielt wird
spawnneinEinmal, wenn das Modell erzeugt wird. Geht am Ende in idle über
idlejaBei horizontalem Stillstand; wechselt zu walk, wenn die X- oder Z-Geschwindigkeit ungleich null ist
walkjaBei horizontaler Bewegung; wechselt am Boden mit X- und Z-Geschwindigkeit null zu idle
attackneinWenn ausgelöst; kehrt am Ende zu idle zurück
deathneinBei removeWithDeathAnimation()

Regeln, die sich aus dem Aufbau der Zustandsmaschine ergeben:

  • Der Startzustand ist spawn, sofern das Modell eine solche Animation besitzt, sonst idle. Ein Modell mit keiner von beiden hat keinen aktuellen Zustand, es animiert also nichts, bis etwas explizit abgespielt wird.
  • Nur Animationen, die im Modell vorhanden sind, erhalten einen Zustand. Ein Modell mit walk, aber ohne idle, verlässt walk von selbst nie wieder.
  • Der Wechsel zwischen Idle und Walk liest die horizontale Geschwindigkeit der zugrunde liegenden Entity; reine Vertikalbewegung startet keine Laufanimation. Statische Entities und Spieler-Verkleidungen haben für diesen Zweck keine zugrunde liegende Entity; stationäre Props bewegen sich nicht horizontal. Ihre zusätzlichen Animationen werden durch Skripte, die API oder FMMs Verkleidungs-Controller gesteuert.
jump existiert hier nur als Enum

JUMP existiert im Enum AnimationStateType, und der Walk-Zustand fordert tatsächlich einen Jump-Übergang an, wenn die Entität den Boden verlässt — aber es wird nie ein Jump-Zustand registriert, sodass diese Anforderung ins Leere läuft. Eine Animation namens jump ist nicht tot: Sie verhält sich einfach wie jede andere benutzerdefinierte Animation und muss manuell ausgelöst werden. Spieler-Verkleidungen sind die Ausnahme — sie verwenden einen separaten Controller, in dem jump sehr wohl angebunden ist.

Spieler-Verkleidungen verwenden einen anderen Satz​

Eine Spieler-Verkleidung ergänzt einen Controller pro Tick, der Animationen über dieselbe Wiedergabe-Engine anfordert. Seine fünf reservierten Namen sind attack, jump, sneak, walk und idle. Er prüft sie in dieser Reihenfolge, solange der Countdown einer einmaligen Animation nicht aktiv ist. Fehlt idle, warnt er in der Konsole. Die vollständige Tabelle und die zeitliche Einschränkung dieser Animationen stehen unter Spieler-Verkleidungen.

Benutzerdefinierte Animationen​

Jede Animation, deren Name keiner der obigen fünf ist, kann trotzdem namentlich abgespielt werden:

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 blendet nicht über. true reiht die Animation ein, damit sie startet, nachdem der Tick des aktuellen Zustands abgeschlossen ist; false unterbricht und wechselt sofort.
  • Es gibt nur einen Warteplatz; eine spätere eingereihte Anfrage ersetzt die vorherige. Eine benutzerdefinierte Animation startet ohne aktuellen Zustand sofort. Ein eingereihter eingebauter Zustand kann bei einem aktuellen Zustand von null nicht fortschreiten; verwende dann blend=false.
  • loop gilt nur für benutzerdefinierte Animationen. Ein eingebauter Zustand verwendet unabhängig vom übergebenen Wert seine eigene Loop-Einstellung.
  • Verwende für benutzerdefinierte Animationen und hasAnimation die exakte Schreibweise: Beide Suchvorgänge unterscheiden Groß- und Kleinschreibung. Wiedergabeanfragen für eingebaute Zustände akzeptieren andere Schreibweisen, ihre Registrierung verlangt aber weiterhin den kleingeschriebenen Namen im Modell.
  • Wenn eine nicht wiederholte benutzerdefinierte Animation endet, fordert sie den gespeicherten zuletzt bestätigten eingebauten Zustand an, oder idle, wenn keiner erfasst wurde. Gemeint ist der letzte eingebaute Zustand, den der Manager zuvor verlassen hat. Dieser kann vom unmittelbar vor der benutzerdefinierten Animation aktiven Zustand abweichen. Existiert der angeforderte Rückkehrzustand nicht, bleibt der benutzerdefinierte Zustand ohne weitere Frame-Aktualisierungen ausgewählt.
  • playAnimation gibt bei unbekannten Namen false zurück, außer bei den unten beschriebenen unterdrückten Angriffsanfragen. Ein erfolgreicher Rückgabewert bedeutet, dass die Anfrage angenommen wurde, nicht dass bereits ein sichtbarer Frame abgespielt wurde.
  • stopCurrentAnimations() wechselt zu idle, sofern das Modell eine solche Animation hat; andernfalls verlässt es den aktuellen Zustand und lässt das Modell ohne aktive Animation zurück.
  • Während eine benutzerdefinierte Animation läuft, wird eine Anfrage für attack, attack_melee oder attack_ranged verschluckt, damit eine geskriptete Sequenz nicht durch normalen Kampf unterbrochen wird.
  • Der Todeszustand ist in der gemeinsamen Zustandsmaschine endgültig: Neue Anfragen geben false zurück, eingereihte Übergänge werden verworfen und das Stoppen von Animationen ändert diesen Zustand nicht. Die öffentliche Stoppmethode sendet zusätzlich eine separate Stoppanfrage an Bedrock; verwende sie daher nicht zur Steuerung einer Todessequenz auf beiden Clients.

Timing und Dauer​

  • Blockbench speichert die Animationslänge in Sekunden. FMM rechnet sie mit ceil(seconds x 20) um, die Dauer ist also immer eine ganze Anzahl Ticks und kurze Animationen werden auf- statt abgerundet.
  • Jede Animation wird beim Import in ein flaches Frame-Array pro Tick gebacken. Die Wiedergabe ist ein Array-Zugriff pro Tick, keine Live-Interpolation.
  • Loopende Animationen indexieren mit counter % duration; nicht loopende werden auf den letzten Frame begrenzt und stellen danach keine weiteren Änderungen mehr dar.
  • Keyframe-Zeiten behalten ihre gebrochene Tick-Position (20 x time, nicht gerundet). Ein Keyframe bei 0,37 s trägt damit zur Interpolation zwischen Ticks bei. Frames werden bei ganzen Ticks von 0 bis duration - 1 abgetastet. Ein Keyframe genau am deklarierten Ende beeinflusst die Interpolation, wird aber nicht selbst abgetastet. Platziere eine Endpose vor dieser Grenze, wenn ein dargestellter Frame sie erreichen muss.
  • Wenn zwei Keyframes desselben Kanals auf exakt derselben Zeit landen, gewinnt der in der Dateireihenfolge spätere.
  • Ein Keyframe mit einer nicht endlichen Zeit bricht diesen Track ab und erzeugt eine einzelne Warnung Malformed animation timeline for model ... pro Animation, während die übrigen Animationen des Modells weiter konvertiert werden.

Animationen der Länge null sind gültig​

Eine Animation mit der Länge 0 (oder negativ) wird als absichtliche statische Pose behandelt — eine gängige Wahl bei Möbeln und anderen Props, die einen benannten Eintrag „keine Animation“ brauchen. Sie wird stillschweigend und ohne Warnung übersprungen und trägt keine Frames bei.

Loop-Modi​

Blockbenchs Loop-Einstellung steuert die exportierte Bedrock-Animation:

Blockbench-Loop-ModusBedrock-Export
loop"loop": true
once"loop": false
hold"loop": "hold_on_last_frame"

Die Java-Wiedergabe verwendet die Wiederholungsregel des eingebauten Zustands beziehungsweise das loop-Argument einer benutzerdefinierten Animation. Sie übernimmt die Wiederholungsregel nicht aus diesem Blockbench-Feld. Daher können sich Java- und Bedrock-Wiedergabe unterscheiden, wenn diese Einstellungen voneinander abweichen.

Interpolationstypen​

Jeder Keyframe trägt seinen eigenen Interpolationstyp, und das Segment, das in einen Keyframe hineinführt, verwendet den Typ dieses Keyframes. Vier werden unterstützt:

Blockbench-TypVerhalten in FMM
linearGeradlinige lineare Interpolation
catmullromGeglättete Interpolation (Ease-in/Ease-out)
bezierAngenähert mit festen Kontrollpunkten 0.42 / 0.58 — FMM liest keine Bezier-Anfasser pro Keyframe
stepRastet auf den vorherigen Wert ein, bis der nächste Keyframe kommt

Alles außerhalb dieses Satzes kann nicht geparst werden und wird als fehlerhafte Timeline gemeldet.

Animierte Kanäle​

Pro Bone werden drei Kanäle gebacken: Rotation, Position und Skalierung. Wichtige Hinweise für die Erstellung:

  • Positionswerte werden durch 16 geteilt (Blockbench-Pixel zu Blöcken).
  • Rotationswerte werden in Radiant umgerechnet.
  • Blockbench format_version 5 und neuer dreht das Vorzeichen der X- und Y-Rotation sowie der X-Position um. FMM gleicht das anhand der deklarierten Formatversion automatisch aus, korrigiere also nicht von Hand — mische aber auch keine v5-Deklaration mit v4-förmigen Daten.
  • Ein Bone ohne Keyframes auf einem Kanal behält für diesen Kanal seinen Ruhewert; ein Bone ganz ohne Frames für einen bestimmten Tick wird auf Rotation 0,0,0, Translation 0,0,0 und Skalierung 1,1,1 zurückgesetzt.
  • Keyframe-Datenpunkte dürfen in der .bbmodel als Zeichenketten geschrieben sein. FMM parst sie als einfache Zahlen — eine leere Zeichenkette wird zu 1 bei der Skalierung und sonst zu 0, und alles Nicht-Parsebare protokolliert Failed to parse supposed number value ... und wird zu 0. Molang-Ausdrücke werden nicht ausgewertet.
  • Es wird nur der erste Datenpunkt eines Keyframes gelesen, Blockbenchs getrennte Vorher-/Nachher-Werte eines Step-Keyframes fallen also zu einem zusammen.

Was nicht animiert wird​

  • Der hitbox-Bone. Animationsspuren, die auf ihn zielen, werden vollständig übersprungen.
  • Sound- und Timeline-Instruction-Keyframes in Blockbench-Effektspuren. FMM importiert aus einer Effektspur nur die Partikel-Keyframes; siehe Partikel. Spiele Sounds stattdessen über ein Lua-Skript oder dein eigenes Plugin ab.
  • Bones, die nicht per Name aufgelöst werden konnten. Eine Spur, die auf einen fehlenden Bone zeigt, protokolliert Failed to get bone <name> from model <model>! und wird übersprungen.

Inverse Kinematik (IK)​

Blockbench-Null-Objekte fungieren als IK-Controller. FMM löst Ketten zur Laufzeit mit FABRIK (Forward And Backward Reaching Inverse Kinematics) auf, begrenzt auf 10 Iterationen mit einer Toleranz von 0.001.

Wie das zusammenspielt:

  1. Ein Null-Objekt mit ik_source (Wurzel-Bone) und ik_target (End-Bone oder Locator) definiert eine Kette. Die Erkennung prüft die Hierarchie und unterstützt auch Suchen unter Geschwistern und nach unten; Geschwisterziele auf der Wurzelebene ergeben eine Kette nur mit dem Quell-Bone.
  2. Nur die Positions-Keyframes des Null-Objekts steuern IK. Sie ergeben pro Frame einen Versatz, der zur Ruheposition des Ziel-Bones oder Locators addiert wird. Der Solver verwendet die gespeicherte Ruheposition des Controllers nicht als Zielbasis. Rotation und Skalierung steuern IK nicht.
  3. In jedem Tick erhalten die zugeordneten IK-Ketten der aktuellen Animation ihre Zielversätze und werden gelöst. Eine zugeordnete Kette ohne Frame-Daten wird zurückgesetzt. Jeder Animationswechsel sowie stopCurrentAnimations() setzt die IK-Rotationen aller Ketten zurück, sodass eine IK-Pose nicht in die nächste Animation übernommen wird.
  4. lock_ik_target_rotation wird gelesen und gespeichert, vom aktuellen Solver aber nicht angewendet.

Ketten, die nicht aufgelöst werden können, werden mit einer benannten Konsolenwarnung übersprungen — siehe Hinweise zur Modellerstellung für die genauen Meldungen und die Erstellungsbeschränkungen.

Bedrock-Export​

Jedes konvertierte Modell schreibt zusätzlich eine Bedrock-Animationsdatei unter animations/<model_id>.animation.json im erzeugten Bundle:

  • Animationsbezeichner lauten animation.fmm.<model_id>.a_<hex>, wobei <hex> die UTF-8-Bytes des Animationsnamens in hexadezimaler Schreibweise sind. Zwei Namen, die sich nur in Zeichen unterscheiden, die Bedrock nicht erlaubt, erhalten dadurch getrennte Bezeichner.
  • animation_length ist die Dauer in Sekunden, nach unten auf 0.05 begrenzt, damit eine Ein-Tick-Animation weiterhin gültig ist.
  • Der Loop-Modus wird wie in der Tabelle Loop-Modi gezeigt abgebildet.
  • Ein Modell ohne Animationen erhält trotzdem einen einzelnen No-Op-idle-Eintrag, damit die Bedrock-Entitätsdefinition gültig bleibt.
  • Die Geometrie schließt hitbox, generierte Namensschild-Bones fmm_nametag_bone_* und Mountpoints m_ aus; selbst erstellte tag_-Anker bleiben enthalten. Der Animationsexport schreibt vorberechnete Bone-Spuren ohne denselben Filter für sichtbare Bones. Animiere daher keine ausgeschlossenen Mount-Bones in der Erwartung sichtbarer Geometrie.
  • Der Bedrock-Animationsexport verwendet vorberechnete Frames für Rotation, Position und Skalierung. Er übernimmt nicht die zur Laufzeit berechneten IK-Rotationen in diese Spuren; prüfe IK-abhängige Modelle gesondert auf Bedrock.
  • Pro Animation wird ein Animations-Controller erzeugt, umgeschaltet über eine Entitätseigenschaft — so spielt FMM eine bestimmte Animation auf einem Bedrock-Client ab.
  • Partikel-Keyframes werden zur particle_effects-Timeline der Animation, sodass Bedrock-Clients sie nativ abspielen. Siehe Partikel.

Siehe Ressourcenpaket-Ausgabe dafür, wo das Bundle auf der Festplatte landet.

Animationen aus anderen Systemen abspielen​

AufruferEinstiegspunkt
Plugin (Java)ModeledEntity#playAnimation(String, boolean blend, boolean loop) / #stopCurrentAnimations() / #hasAnimation(String)
Prop-Lua-Skriptcontext.prop:play_animation(name, blend, loop) / context.prop:stop_animation()
Beliebige Lua-Entitätstabelleentity.model:play_animation(name, blend, loop) / entity.model:stop_animations() (verfügbar, wenn entity.is_modeled true ist)

Die zugehörigen Schnittstellen beschreiben der API- und Entwicklerleitfaden und Lua: Prop-API.

Die Lua-Standardwerte unterscheiden sich: context.prop:play_animation(name) verwendet blend=true, loop=true, während entity.model:play_animation(name) die Werte false, false verwendet. Übergib beide booleschen Werte ausdrücklich, wenn der Unterschied wichtig ist.

Fehlerbehebung​

Meine Animation läuft nie automatisch. Die gemeinsame Zustandsmaschine erkennt die kleingeschriebenen Namen spawn, idle, walk, attack und death. Bewegung steuert die Übergänge zwischen idle und walk; Angriff und Tod benötigen weiterhin ihren Laufzeitauslöser. Andere Namen brauchen einen expliziten Aufruf von playAnimation / play_animation, außer den zusätzlichen Namen, die der Verkleidungs-Controller auswählt.

Mein Modell tut überhaupt nichts. Wahrscheinlich hat es weder eine spawn- noch eine idle-Animation, sodass bei der Erzeugung kein Zustand betreten wird. Füge eine idle hinzu.

Meine Animation läuft, aber nichts bewegt sich. Prüfe die Animationslänge. Eine Animation der Länge null wird als statische Pose behandelt und absichtlich stillschweigend übersprungen.

Rotationen sind gespiegelt. Prüfe meta.format_version in der .bbmodel. FMM dreht das Vorzeichen der X-/Y-Rotation ab Formatversion 5 um; eine Datei, die die eine Version deklariert, aber die Daten der anderen enthält, kommt gespiegelt heraus.

Die Konsole meldet Malformed animation timeline for model .... Eine Spur in dieser Animation konnte nicht gelesen oder interpoliert werden. Die Warnung erscheint einmal pro Animation und benennt den beteiligten Bone bzw. IK-Controller; die übrigen Animationen des Modells werden weiterhin konvertiert.

Sounds in meiner Blockbench-Timeline tun nichts. Sound-Keyframes werden nicht importiert. Löse Sounds über ein Lua-Skript oder dein eigenes Plugin aus. Partikel-Keyframes werden importiert; falls sie nicht erscheinen, siehe Partikel.