本文へスキップ

Godot アニメーションのフレーム時間 · ミリ秒で指定

Godot 4のAnimatedSprite2Dのフレームは、自分の時間を持ちません。durationは、1秒あたりのフレーム数で決まるアニメーション速度への倍率です。12 fpsで250 msなら3.0、ディレイが不ぞろいなGIFならほぼフレームごとに別の数値が必要です。Studioは時間をミリ秒のまま保持し、SpriteFramesリソースを書くときにフレームごとにこの割り算を行います。

Godot 4のSpriteFramesのフレームには、ミリ秒の時間がありません。長さは相対値(既定1.0)で、アニメーションのSpeed(FPS)がそれを秒に変えます。秒 = 長さ ÷(FPS × 再生速度)。そのため250msは10 FPSなら2.5、12 FPSなら3.0です。GIFや.asepriteの不ぞろいな時間を保つには、フレームごとに ms ÷(1000 ÷ FPS)を入れます。NerulioのGodot 4書き出しは全フレームでこの計算を行い、タグを10 FPSで書くので、長さは単純に ms ÷ 100 です。Godot 4専用、4.7.2で確認。

スプライトシート、連番フレーム、GIF・APNG、.asepriteファイルをドロップ
空のStudioを開く
ブラウザ内で処理し、ファイルはアップロードされません。
Studio のスプライト作業画面:アニメーションGIFを6フレームとして読み込み、タイムラインに各フレームの120msの遅延が残り、オニオンスキンをオンにした画面
CC0アセット(OpenGameArt)を開いた実際のStudio画面です。
書き出し先
  • Godot 4検証済み· Godot 4.7.2
  • Unity 6検証済み· Unity 6000.5.3f1
  • Phaser 3 / 4検証済み· Phaser 3.90 + 4.2
  • PixiJS 8検証済み· PixiJS 8.21
  • Aseprite JSON検証済み· Phaser load.aseprite · PixiJS 8.21
  • .aseprite検証済み· Aseprite 1.3.18
  • Spine / libGDX検証済み· spine-canvas 4.2
  • Starling / Sparrow XML検証済み· Phaser 4.2
  • LÖVE検証済み· LÖVE 11.5
  • Defoldビルド確認· Defold bob.jar 1.13.1
  • GameMaker未検証
  • CSS sprites検証済み· Chromium
  • GIF / APNGデコード確認· Pillow

Godot 4がフレームの時間を決める仕組み

SpriteFramesのアニメーションごとに毎秒フレーム数(Speed)があり、フレームごとに相対の長さがあります。Godot 4.7のクラスリファレンスの規則は「絶対の長さ = 相対の長さ ÷(アニメーションのFPS × |再生速度|)」で、再生速度はノードのspeed_scaleにplay()へ渡したcustom_speedを掛けた値です。10 FPSでは1ティックが100msなので、1.0は100ms、2.5は250msです。

長さは比率なので、Speedを変えるとアニメーション全体が同じリズムのまま速く・遅くなります。20 FPSにすれば全フレームが半分になります。画像をドラッグしたりAdd Frames from Sprite Sheetで追加したフレームはすべて1.0なので、そうして作ったアニメーションは全フレームが同じ長さです。不ぞろいな時間は、SpriteFramesパネルでフレームごとにFrame Durationへ入力するか、コードでadd_frame(anim, texture, duration)やset_frame()を使って設定します。

ループはアニメーションごとのオン・オフで、回数ではありません。ループするアニメーションはanimation_finishedを出さず(animation_loopedを使います)、ループしないアニメーションは最後のフレームで止まってこのシグナルを出します。Godot 4.7には、折り返しのたびに端のフレームを1回だけ表示するピンポンのループモードもあります。

Nerulioは全フレームをミリ秒のまま保持し(GIF・APNGのディレイ、Asepriteの長さ、タイムラインで入力した値)、.tresを書くときにだけ換算します。タグは10 FPSで書き、タグのないプロジェクトは12 FPSの暗黙のアニメーション1つになります。逆再生とピンポンはフレーム順に展開し(ピンポンはGodot自体のモードと同じく端のフレームを重複させない)、繰り返し∞はループオン、回数指定はループオフになります。

Speed(FPS)
アニメーションの毎秒ティック数。1000 ÷ FPSが1ティックの長さ(ms)。
相対の長さ
ティック単位のフレームの長さ(既定1.0)。SpriteFramesパネルのFrame Duration。
再生速度
speed_scale × play()のcustom_speed。全フレームの時間がこれで割られます。
ループ
オンなら繰り返し、animation_finishedを出さない。オフなら1回再生して止まる。

例:ディレイが不ぞろいなGIF

GIFで書き出した6コマの歩きを、NerulioがGodot向けに書く値と、それを時間に読み戻す方法です。

GIF "walk":6フレーム、ディレイ 80, 80, 80, 160, 80, 240 ms   (1周720 ms)
Nerulio → Godot:speed 10 FPS、1ティック = 1000 / 10 = 100 ms
  duration = 80/100 … = 0.8, 0.8, 0.8, 1.6, 0.8, 2.4

秒に戻す:duration /(FPS × 再生速度)
  2.4 / (10 × 1) = 0.24秒        speed_scale 2なら:2.4 / (10 × 2) = 0.12秒
  1周:(0.8 × 4 + 1.6 + 2.4) / 10 = 0.72秒 = 720 ms

同じ時間を12 FPS(1ティック83.33 ms)で手入力すると:
  80 → 0.96    160 → 1.92    240 → 2.88

どちらも同じように再生され、リソース内の数値だけが違います。あとでSpeedを20 FPSにすると全フレームが半分になり、1周は360msになります。

できること

  • ミリ秒の時間を、どこから来たものでもそのまま使用。GIF・APNGのディレイ、Asepriteのフレームごとの時間、タイムラインで1フレームずつまたはまとめて入力した値。
  • アニメーションのspeedはタグのfps、各フレームのdurationは ms ÷ (1000 ÷ fps)。小数点以下6桁まで書き出し。
  • 逆再生とピンポンはフレーム順に展開するので、Godotはプレビューと同じ順で順方向に再生。ピンポンの端のフレームが2回出ることはありません。
  • 繰り返し0(無限ループ)ならloopをオン。回数の決まったタグはloopオフで書き出します。SpriteFramesには繰り返し回数の項目がないためです。
  • Godot 4.7.2で読み込んだリソースを読み戻し、アニメーションごとのspeed・loop・フレームごとのdurationが書いた値と一致することを確認。

使い方

  1. GIF、.asepriteファイル、スプライトシートのどれかをドロップ。
  2. タイムラインで各フレームのmsを確認し、複数フレームを選んでまとめて変更。
  3. Enterでタグを方向・繰り返しどおりに再生し、違和感のある所を直す。
  4. パック & 書き出しを開き、Godot 4を選ぶ。
  5. .tresをAnimatedSprite2DのSprite Framesに設定するか、同梱の.tscnを開く。

出力されるファイル

時間の情報はGodot 4バンドルの2つのファイルに入っています。

ファイル用途
hero.tresアニメーションごとのspeed(タグは10.0)、loop、フレームごとに小数点以下6桁までのduration。
hero.tscn最初のアニメーションをspeed_scale 1で自動再生するAnimatedSprite2D。

Godot 4で時間を確認・変更する

  1. hero.tresをAnimatedSprite2DのSprite Framesプロパティに読み込むか、hero.tscnを開きます。
  2. SpriteFramesパネルでアニメーションを選ぶとSpeedは10 FPSです。長いフレームを選ぶとFrame Durationは2.4です。そのフレームだけ変えるならここに新しい値を入れます。
  3. アニメーション全体のテンポを変えるときは、フレームではなくSpeedを変えます。10 → 12 FPSで全フレームが1 ÷ 1.2になり、リズムは保たれます。
  4. コードで読むならsprite_frames.get_frame_duration("walk", 5) / sprite_frames.get_animation_speed("walk")で0.24秒が得られます。
  5. 1回きりの動作はループをオフにしてanimation_finishedをつなぎ、ループする動作にはanimation_loopedを使います。
  6. Speed Scaleは1のままにし、play("walk")はカスタム速度なしで呼びます。そうしないと全フレームの時間がその値で割られます。

結果の確認

  • スクリプトで全フレームのduration ÷ speedを合計します。上の歩きならGIFの1周と同じ0.72秒です。
  • 240msのフレームのFrame Durationは2.4、160msのフレームは1.6のはずです。
  • ブラウザのGIFと並べて再生します。最後のフレームで止まる時間が同じはずです。

fpsごとのms → Godotのduration

フレーム時間アニメーションのspeed(fps)1単位の長さ.tresのduration
100 ms10100 ms1.0
250 ms1283.33 ms3.0
120 ms10100 ms1.2
50 ms2050 ms1.0
500 ms1283.33 ms6.0

計算式:duration = ms ÷ (1000 ÷ speed)。Studioがspeedにタグのfpsを書き、全フレームを変換するので、この数値を手で入力することはありません。

トラブルシューティング

症状考えられる原因確認方法対処
元は不ぞろいなのに全フレームが同じ長さドラッグやシートから追加したため全部1.0フレームを1つずつ選んでFrame Durationを確認フレームごとに ms ÷(1000 ÷ FPS)を入れるか、Nerulioから書き出す
GIFの2倍の速さになるspeed_scaleが2、またはplay()にカスタム速度を渡した再生中にget_playing_speed()を確認Speed Scaleを1にし、play()は2つ目の引数なしで呼ぶ
250msのはずのフレームが一瞬で過ぎる相対の長さに秒(0.25)を入れた10 FPSでは0.25は25msms ÷(1000 ÷ FPS)で入力:10 FPSで250msは2.5
animation_finishedが来ないループするアニメーションはこのシグナルを出さないSpriteFramesパネルのループ切り替え1回きりの動作はループをオフにするかanimation_loopedを使う
ピンポンのアニメーションが端で一瞬止まる順序を0-1-2-3-3-2-1-0と手で書き、端のフレームが2回出るアニメーションのフレーム一覧を確認重複したフレームを消すか、Godotのピンポンループを使う。Nerulioは0-1-2-3-2-1で書く
GIFの一部のフレームが10msではなく100msになるGIFに0や1(1/100秒)と書かれ、ブラウザはそれを100msで再生するNerulioの読み込みでディレイの判断として表示されるブラウザ方式のままにするか、書き出し前にファイル本来の値へ切り替える

ほかの方法

方法こんなときに向いています
SpriteFramesパネルでFrame Durationを手入力長く止めたいフレームが数枚だけのとき。書き出しの手間がありません。
frameを正確な時刻にキーを打つSprite2D + AnimationPlayerタイムライン上で秒単位に時間を決め、音やヒットボックスなど別のトラックも同じクリップに置きたいとき。
時間が均一ならSpeedだけで全フレームが同じ長さなら、Speed = 1000 ÷ msにして長さは1.0のまま。Godotでスプライトシートを使うを参照。

エンジン別の書き出しと検証方法

「検証済み」は、実際のCC0アセットから書き出したファイルを本物のエンジン・ツールで読み込み、元データと比較したという意味です。読み込んでいない形式はそのように表示します。

書き出し先ファイル検証
Godot 4.tres SpriteFrames · .tscn AnimatedSprite2D · .png.import検証済み Godot 4.7.2Godotで読み込み、全アニメーションフレームを描画して元ファイルと比較。シーンはNearestフィルターで、回転は使いません。
Unity 6.unity.json · Editor/NerulioSpriteImporter.cs · AnimationClip検証済み Unity 6000.5.3f1バッチモードでスプライト範囲・ピボット・ピクセル・クリップのキーと長さを確認。
Phaser 3 / 4atlas JSON (multiatlas) · .anims.json検証済み Phaser 3.90 + 4.2両バージョンでフレームとアニメーションを描画。
PixiJS 8spritesheet JSON · animations · anchor · meta.scale検証済み PixiJS 8.21回転・複数ページのアトラスも確認。
Aseprite JSONhash / array · frameTags · slices検証済み Phaser load.aseprite · PixiJS 8.21タグの方向と繰り返し回数を保持。
.asepritelayers · tags · durations · slices検証済み Aseprite 1.3.18Asepriteのコマンドラインで開いて描画。
Spine / libGDX.atlas検証済み spine-canvas 4.2Spineランタイムで回転・トリム領域をすべて描画。libGDX本体は未実行。
Starling / Sparrow XMLXML per page検証済み Phaser 4.2Phaser 3.90はトリムされたXMLフレームを誤って描画します(Phaser側の不具合)。「Sparrow XML (Phaser 3)」プリセットはトリムを切り、3.90・4.2とも合格。
LÖVELua quads · nerulio_atlas.lua · main.lua検証済み LÖVE 11.5同梱ヘルパーで全フレームを描画し、全アニメーションを再生。
Defold.atlas · .tilesourceビルド確認 Defold bob.jar 1.13.1Defoldのコマンドラインビルダーでビルドし、生成テクスチャのUVを読み戻して確認。
GameMakerspr_<name>_stripN.png · gamemaker.json未検証GameMakerでは読み込んでいません(ヘッドレス実行不可)。ストリップはGameMakerの取り込み方式どおりに切ります。
CSS sprites.css · preview .html検証済み Chromium同梱スタイルシートで全クラスを描画。
GIF / APNGone file per animationデコード確認 Pillow書き出し後に全フレームと遅延を再デコード。APNGは完全一致。

検証の根拠

GIFデコーダー:実在のGIF 29本で408/408フレームがPillowと一致、APNG 16本で132/132。.aseprite:コーパス231ファイルを読み込み・書き直し、Aseprite 1.3.18で開いてタグ・長さ・ピクセルが231/231一致。書き出し:実在のCC0アセットで実際のエンジンを49回実行し47回合格。失敗2回はPhaser 3.90のトリムXMLの不具合で、Phaser 3プリセットで回避できます。

制限

  • SpriteFramesには繰り返し回数がありません。Asepriteの「3回再生」はループオフになり、残りの再生はスクリプトで扱います。
  • Godot 3にはフレームごとの長さがなく、書き出したリソースを読めません。
  • グリッド・島の推定は信頼度つきのプレビューで、「適用」を押すまで何も切りません。すべての選択に代替案があり、ワンクリックで切り替えられます。
  • レイヤーが正確に合成できない.asepriteファイル(グループの不透明度など)は統合して読み込み、理由を読み込みパネルに表示します。
  • 読み込んだアトラス内の回転フレームは回転したままで、その書き出しは未検証です。
  • 画像は一辺32,768px(2億6,800万ピクセル)まで。ブラウザのメモリにも依存します。

バージョンと出典

検証の実行ではGodot 4.7.2がNerulioの.tresを読み込み、アニメーションごとのspeed・ループ・フレームごとのdurationを読み戻した値が書いた値と一致しました。長さの式、speed_scale、get_playing_speed()、シグナルはGodot 4.7のクラスリファレンスに、Frame Durationの欄の名前はGodot 4.7のエディターのソースに基づきます。

参照した公式ドキュメント

よくある質問

フレームのdurationが0.25ではなく3.0になるのはなぜ?

Godot 4はフレームの長さをアニメーション速度に対する比率で表します。12 fpsでは1単位が1000 ÷ 12 ≈ 83.3 msなので、250 msのフレームは3.0です。あとでspeedを変えても、全フレームが一緒に伸び縮みして比率は保たれます。

Godot 3でもこの時間は読めますか?

いいえ。Godot 3のSpriteFramesにはフレームごとのdurationがなく、リソース形式も異なります。確認したのはGodot 4(4.7.2)だけです。

ノードのspeed_scaleは影響しますか?

AnimatedSprite2Dのspeed_scaleはアニメーション全体に掛かります。1のままなら、Studioで決めたミリ秒どおりに再生されます。

GIFで0や1(1/100秒)と書かれたフレームはどうなりますか?

ブラウザはそうしたフレームを引き延ばして再生します。そのためGIFの読み込みでは「ブラウザでの再生どおり」か「ファイルに書かれた値」かを切り替えられる判断として表示し、残した方がSpriteFramesに入ります。

無料ですか?アカウントは必要ですか?

このページの機能はすべて無料で、アカウントなしで使えます。透かしや制限された書き出しはありません。

スプライトはアップロードされますか?

いいえ。Studioはこのブラウザのタブ内でデコード・パック・書き出しを行います。プロジェクトはブラウザ(IndexedDB)に自動保存され、.nerulioファイルとして保存できます。

Asepriteやエンジンのプラグインは必要ですか?

いいえ。.asepriteファイルはブラウザで読み書きします。エンジン別のバンドルには必要なものが入っています(Godotはインポート設定とシーン、UnityとGodotのタイルセットは小さなエディタースクリプト)。