FreeMinecraftModelsのアニメーション
FreeMinecraftModels は .bbmodel と .fmmodel からアニメーションをインポートします。このページでは予約名、フレームのタイミング、補間、ループモード、インバースキネマティクス(IK)を説明します。
ボーンの命名規則やインポート規約の残りの部分については、モデル作成の注意事項を参照してください。
5つのステートアニメーション
FreeMinecraftModelsは、ちょうど5つの小文字のアニメーション名を自動的なランタイムステートに紐付けます。モデル内のそれ以外のものはすべてカスタムアニメーションです。
| アニメーション名 | ループ | 再生タイミング |
|---|---|---|
spawn | しない | モデル生成時に1回。終了するとidleへ遷移します |
idle | する | 水平方向に静止している間。X または Z の速度が0でなくなると walk に切り替わります |
walk | する | 水平方向に移動している間。接地しており、X と Z の速度がともに0になると idle に切り替わります |
attack | しない | トリガーされたとき。終了するとidleに戻ります |
death | しない | removeWithDeathAnimation()が呼ばれたとき |
ステートマシンの構築方法から導かれるルール:
- 開始ステートは、モデルに
spawnがあればそれ、なければidleです。どちらも持たないモデルには現在ステートが存在しないため、明示的に何かを再生するまで一切アニメーションしません。 - モデル内に存在するアニメーションだけがステートを得ます。
walkはあるがidleがないモデルは、自力でwalkから抜け出すことがありません。 - idle/walk の切り替えは、基となるエンティティの水平速度を読み取ります。垂直移動だけでは歩行を開始しません。静的エンティティとプレイヤーディスガイズには、この用途の基となるエンティティがなく、静止したプロップにも水平移動はありません。追加のアニメーションはスクリプト、API、FMM のディスガイズコントローラーで制御します。
jumpは列挙値のみJUMPはAnimationStateType列挙型に存在し、walkステートもエンティティが地面を離れたときにjumpへの遷移を要求します。しかしjumpステートは一切登録されないため、その要求は何も起こしません。ただしjumpという名前のアニメーションが無効というわけではありません。単に他のカスタムアニメーションと同じように振る舞い、手動でトリガーする必要があるだけです。プレイヤーディスガイズは例外で、jumpが実際に組み込まれている別のコントローラーを使用します。
プレイヤーディスガイズは別のセットを使用します
プレイヤーディスガイズは、同じ再生エンジンにアニメーションを要求するティック単位のコントローラーを追加します。5つの予約名は attack、jump、sneak、walk、idle で、単発再生のカウントダウンが動作していない間、この優先順位で評価します。モデルに idle がない場合はコンソールに警告します。全一覧と単発再生の時間制約は、プレイヤーディスガイズを参照してください。
カスタムアニメーション
上記5つ以外の名前を持つアニメーションも、名前を指定して再生できます:
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**はクロスフェードを行いません。trueは現在のステートのティックが完了した後に開始するようアニメーションをキューに入れ、falseは割り込んで即座に切り替えます。 - キューの枠は1つだけです。後のキュー要求が前の要求を置き換えます。現在のステートがなければ、カスタムアニメーションは即座に開始します。組み込みステートをキューに入れた場合、現在のステートが null だと進行できないため、
blend=falseを使用してください。 - **
loop**はカスタムアニメーションにのみ適用されます。組み込みステートは、何を渡しても自身のループ設定を使用します。 - カスタムアニメーションと
hasAnimationは大文字と小文字を区別するため、正確な名前を指定してください。組み込みステートの再生要求では異なる大文字・小文字も使えますが、登録にはモデル内の小文字名が必要です。 - ループしないカスタムアニメーションが終了すると、保存された最後にコミットされた組み込みステートへの遷移を要求します。記録がなければ
idleを要求します。これはマネージャーが以前に離れた最後の組み込みステートで、カスタムアニメーション直前のステートとは限りません。戻り先が存在しなければ、カスタムステートが選択されたままフレーム更新を停止します。 - 不明な名前に対して
playAnimationはfalseを返します。ただし、後述する抑制対象の攻撃要求は例外です。成功の戻り値は要求の受理を意味し、表示フレームが再生された証明ではありません。 stopCurrentAnimations()は、モデルにidleがあればそこへ遷移します。ない場合は現在のステートを抜け、アクティブなアニメーションがない状態のままになります。- カスタムアニメーションの再生中は、
attack、attack_melee、attack_rangedの要求は握りつぶされます。これは、通常の戦闘によってスクリプト化されたシーケンスが中断されないようにするためです。 - 共有ステートマシンでは死亡ステートが終端です。新しい再生要求は
falseを返し、待機中の遷移は破棄され、アニメーション停止でもこのステートは変わりません。ただし公開の停止メソッドは Bedrock に別の停止要求も送るため、両クライアントの死亡シーケンスを制御する目的では使用しないでください。
タイミングと再生時間
- Blockbench はアニメーションの長さを秒で保存します。FMM は
ceil(seconds x 20)で変換するため、再生時間は常に整数ティックとなり、短いアニメーションも切り上げられます。 - すべてのアニメーションは、インポート時にティックごとのフラットなフレーム配列にベイクされます。再生はティックごとの配列参照であり、リアルタイム補間ではありません。
- ループするアニメーションは
counter % durationでインデックスを取り、ループしないものは最終フレームでクランプされ、それ以降は変化の描画を停止します。 - キーフレームの時刻は小数のティック位置を保持します(
20 x time、丸めなし)。0.37秒のキーフレームもティック間の補間に寄与します。フレームは整数ティックの0からduration - 1でサンプリングするため、宣言された終了時刻ちょうどのキーフレームは補間に影響しても、それ自体はサンプリングされません。表示フレームで最終ポーズに到達させる必要がある場合は、この境界より前に配置してください。 - 同じチャンネル上の2つのキーフレームがまったく同じ時刻に位置する場合、ファイル順で後のものが優先されます。
- 時刻が有限でないキーフレームはそのトラックを中断させ、アニメーションごとに1回だけ
Malformed animation timeline for model ...という警告を出しますが、モデルの他のアニメーションは変換され続けます。
長さ0のアニメーションは有効です
長さが0(または負の値)のアニメーションは、意図的な静止ポーズとして扱われます。これは、「アニメーションなし」という名前付きエントリを必要とする家具やその他のプロップでよく使われるオーサリング上の選択です。警告なしで静かにスキップされ、フレームを一切生成しません。
ループモード
Blockbench のループ設定は、エクスポートする Bedrock アニメーションを制御します。
| Blockbenchのループモード | Bedrockエクスポート |
|---|---|
loop | "loop": true |
once | "loop": false |
hold | "loop": "hold_on_last_frame" |
Java の再生は、組み込みステートのループ方針またはカスタムアニメーションに渡す loop 引数を使用します。この Blockbench のフィールドからは選択しないため、設定が異なると Java と Bedrock の再生も異なります。
補間タイプ
各キーフレームは独自の補間タイプを持ち、あるキーフレームへ向かう区間はそのキーフレームのタイプを使用します。サポートされているのは4種類です:
| Blockbenchのタイプ | FMMでの挙動 |
|---|---|
linear | 単純な線形補間 |
catmullrom | 滑らかな補間(イーズイン/イーズアウト) |
bezier | 固定の0.42/0.58制御点で近似します。FMMはキーフレームごとのベジェハンドルを読み取りません |
step | 次のキーフレームまで直前の値にスナップします |
このセット以外のものは解析に失敗し、不正なタイムラインとして報告されます。
アニメーションされるチャンネル
ボーンごとに3つのチャンネル、回転、位置、スケールがベイクされます。オーサリング時に重要となる注意点:
- 位置の値は16で除算されます(Blockbenchのピクセルからブロックへ)。
- 回転の値はラジアンに変換されます。
- Blockbenchの
format_version5以降では、X・Y回転およびX位置の符号が反転します。 FMMは宣言されたフォーマットバージョンに基づいて自動的に補正するため、手作業で補正しないでください。ただし、v5の宣言とv4形式のデータを混在させることも避けてください。 - あるチャンネルにキーフレームがないボーンは、そのチャンネルの静止値を維持します。あるティックにフレームがまったくないボーンは、回転
0,0,0、移動0,0,0、スケール1,1,1にリセットされます。 - キーフレームのデータ値は
.bbmodel内で文字列として記述されている場合があります。FMMはこれを単なる数値として解析します。空文字列はスケールでは1、それ以外では0になり、解析できない値はFailed to parse supposed number value ...をログに出力して0になります。Molang式は評価されません。 - 読み取られるのはキーフレームの最初のデータ値のみです。そのため、stepキーフレームでBlockbenchが持つpre/postの個別値は1つに統合されます。
アニメーションされないもの
hitboxボーン。これを対象とするアニメーショントラックは完全にスキップされます。- Blockbenchのエフェクトトラックにあるサウンドとタイムラインインストラクションのキーフレーム。FMMがエフェクトトラックからインポートするのはパーティクルのキーフレームだけです。パーティクルを参照してください。サウンドは代わりにLuaスクリプトや独自のプラグインから再生してください。
- 名前で解決できなかったボーン。存在しないボーンを指すトラックは
Failed to get bone <name> from model <model>!をログに出力してスキップされます。
インバースキネマティクス(IK)
Blockbenchのnullオブジェクトは、IKコントローラーとして機能します。FMMはランタイムでチェーンをFABRIK(Forward And Backward Reaching Inverse Kinematics)により解き、最大10回の反復と0.001の許容誤差で打ち切ります。
構成要素の関係:
ik_source(ルートボーン)とik_target(末端ボーンまたはロケーター)を持つ null オブジェクトがチェーンを定義します。階層に加え、兄弟ボーンや子孫方向も検索します。ルート階層の兄弟ターゲットでは、ソースボーンだけのチェーンになります。- IK を制御するのは null オブジェクトの位置キーフレームだけです。フレームごとのオフセットを、ターゲットのボーンまたはロケーターの静止位置に加算します。ソルバーは保存されたコントローラーの静止位置を基準にしません。回転とスケールは IK を制御しません。
- 毎ティック、現在のアニメーションに対応付けられた IK チェーンにオフセットを適用して解きます。対応するチェーンにフレームデータがなければクリアします。アニメーションを切り替えるたびに、また
stopCurrentAnimations()を呼んだときにも、すべてのチェーンの IK 回転がクリアされるため、IK のポーズが次のアニメーションに持ち越されることはありません。 lock_ik_target_rotationは読み取って保存しますが、現在のソルバーでは適用しません。
解決できなかったチェーンは、名前付きのコンソール警告とともにスキップされます。正確なメッセージとオーサリング上の制約については、モデル作成の注意事項を参照してください。
Bedrockエクスポート
変換された各モデルは、生成されるバンドル内のanimations/<model_id>.animation.jsonにBedrock用アニメーションファイルも書き出します:
- アニメーション識別子は
animation.fmm.<model_id>.a_<hex>です。<hex>はアニメーション名のUTF-8バイト列を16進数で表したものです。そのため、Bedrockで使えない文字だけが異なる2つの名前にも、別々の識別子が割り当てられます。 animation_lengthは秒単位の再生時間で、最小値は0.05です。これにより1ティックのアニメーションも有効なままになります。- ループモードはループモードの表の通りにマッピングされます。
- アニメーションがまったくないモデルにも、Bedrockのエンティティ定義が有効であり続けるよう、何もしない
idleエントリが1つ生成されます。 - ジオメトリからは
hitbox、生成されたfmm_nametag_bone_*ネームタグボーン、m_マウントポイントを除外し、作成者のtag_アンカーは残します。アニメーション出力は同じ表示ボーンのフィルターを適用せずにベイク済みトラックを書き出します。除外されたマウントボーンを動かしても、表示ジオメトリが現れるわけではありません。 - Bedrock のアニメーション出力は、ベイク済みのボーンの回転・位置・スケールを使います。ランタイム IK ソルバーの回転はトラックに焼き込まれないため、IK に依存するモデルは Bedrock でも個別に確認してください。
- アニメーションごとに1つのアニメーションコントローラーが生成され、エンティティプロパティによって切り替えられます。これがFMMがBedrockクライアント上で特定のアニメーションを再生する仕組みです。
- パーティクルのキーフレームはアニメーションの
particle_effectsタイムラインになるため、Bedrockクライアントはそれをネイティブに再生します。パーティクルを参照してください。
バンドルがディスク上のどこに配置されるかについては、リソースパック出力を参照してください。
他のシステムからアニメーションを再生する
| 呼び出し元 | エントリポイント |
|---|---|
| プラグイン(Java) | ModeledEntity#playAnimation(String, boolean blend, boolean loop) / #stopCurrentAnimations() / #hasAnimation(String) |
| プロップのLuaスクリプト | context.prop:play_animation(name, blend, loop) / context.prop:stop_animation() |
| 任意のLuaエンティティテーブル | entity.model:play_animation(name, blend, loop) / entity.model:stop_animations()(entity.is_modeledがtrueのときに利用可能) |
関連するインターフェースは、API&開発者ガイドとLua:プロップAPIを参照してください。
Lua の既定値には違いがあります。context.prop:play_animation(name) は blend=true, loop=true、entity.model:play_animation(name) は false, false です。この違いが重要な場合は、両方の真偽値を明示してください。
トラブルシューティング
アニメーションが自動的に再生されません。
共有ステートマシンは小文字の spawn、idle、walk、attack、death を認識します。移動が idle/walk の遷移を制御しますが、攻撃と死亡には対応するランタイムのトリガーが必要です。それ以外の名前は、ディスガイズコントローラーが選ぶ追加の名前を除き、明示的な playAnimation / play_animation 呼び出しが必要です。
モデルがまったく動きません。
おそらくspawnもidleも持っていないため、生成時にどのステートにも入っていません。idleを追加してください。
アニメーションは再生されるのに何も動きません。 アニメーションの長さを確認してください。長さ0のアニメーションは静止ポーズとして扱われ、仕様として警告なしにスキップされます。
回転が反転しています。
.bbmodel内のmeta.format_versionを確認してください。FMMはフォーマットバージョン5以降でX・Y回転の符号を反転します。あるバージョンを宣言しながら別バージョンの形式のデータを持つファイルは、反転した状態で出力されます。
コンソールにMalformed animation timeline for model ...と表示されます。
そのアニメーション内の1つのトラックが読み取りまたは補間できませんでした。警告はアニメーションごとに1回発生し、関係するボーンまたはIKコントローラー名を示します。モデルの他のアニメーションは引き続き変換されます。
Blockbenchのタイムラインに置いたサウンドが何も起きません。 サウンドのキーフレームはインポートされません。サウンドはLuaスクリプトまたは独自のプラグインからトリガーしてください。パーティクルのキーフレームはインポートされます。表示されない場合はパーティクルを参照してください。