本文へスキップ

PixiJS スプライトシートJSON作成 · アニメーション・アンカー・スケール

フレームだけのスプライトシートJSONではPixiJSにアニメーションができず、meta.scaleが「1」のままの@2xのコピーは2倍の大きさで描かれます。この書き出しは両方を書き、フレームごとにピボットをanchorとして入れ、回転・複数ページのアトラスも含めてPixiJS 8.21で読み込み・描画しました。

PixiJS 8はJSONファイル1つからスプライトシートを読み込みます。const sheet = await Assets.load("run.json")がmeta.imageに書かれたPNGまで読み込むと、sheet.textures["run_0"]が1フレーム、sheet.animations["run"]が1つのアニメーションのテクスチャ一覧になります。Nerulioは再生順のanimationsマップ、ピボットから取ったフレームごとのanchor、@2xの倍率違いごとに合わせたmeta.scale、追加ページ用のrelated_multi_packsを入れてこのJSONを書き、フレームごとのミリ秒はmeta.nerulioに入れます。回転や複数ページのシートも含め、PixiJS 8.21で確認しています。

パックするフレーム画像、GIF、.asepriteファイルをドロップ
空のStudioを開く
ブラウザ内で処理し、ファイルはアップロードされません。
Studio のパック&書き出し:各フレームの枠が表示されたアトラスページ、使用率、書き出し先
CC0アセット(OpenGameArt)を開いた実際のStudio画面です。
書き出し先
  • PixiJS 8検証済み· PixiJS 8.21
  • Godot 4検証済み· Godot 4.7.2
  • Unity 6検証済み· Unity 6000.5.3f1
  • Phaser 3 / 4検証済み· Phaser 3.90 + 4.2
  • 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

PixiJSがJSONから読むもの

framesの項目1つがテクスチャ1つになります。frameはページ上の矩形、rotatedは断片が回転して保存されている印、spriteSourceSizeとsourceSizeはトリムしたフレームを元の大きさに戻す値です。anchorはテクスチャの既定のアンカーになり、そこから作ったスプライトはその点を基準に置かれます(0.5, 1 = 下中央)。

animationsマップはアニメーションごとにフレーム名を並べるだけで、時間の情報はありません。テクスチャだけで作ったAnimatedSpriteは、animationSpeedで調整する1つの速さで再生します。フレームごとに時間を与えるには、timeをミリ秒にした{ texture, time }オブジェクトを渡します。標準の形式にこの値の置き場がないため、Nerulioはmeta.nerulio.animationsにミリ秒を保存します。

meta.scaleはページの解像度です。"2"なら@2xページのテクスチャはPixiJSの単位で@1xと同じ大きさになり、細かさだけが2倍になります。追加ページはmeta.related_multi_packsに名前があり、最初のJSONと一緒に読み込まれます。

テクスチャ
1フレーム:ページの矩形とトリム、回転、既定のアンカー。
anchor
スプライトの位置に置かれるテクスチャ上の点。各軸0–1。
animations
名前 → フレーム名の順序付きリスト。PixiJSがテクスチャの配列に変えます。
meta.scale
ページの解像度。@1xは"1"、@2xは"2"。

例:走りの1フレームを、ファイルとPixiJSで並べて見る

run.json(ページ run.png 38 × 50、"scale": "1")
"run_0": { "frame": {"x":0,"y":34,"w":18,"h":15}, "rotated": false, "trimmed": true,
           "spriteSourceSize": {"x":10,"y":10,"w":18,"h":15}, "sourceSize": {"w":40,"h":29},
           "anchor": {"x":0.5,"y":1} }
"animations": { "run": ["run_0","run_1","run_2","run_3","run_4","run_5"] }
"meta": { …, "nerulio": { "animations": { "run": { "fps":10, "durationsMs":[100,100,100,100,100,100] } } } }

PixiJS 8.21がrun_0について読み戻した値:
  frame 18 × 15、位置 (0, 34)   trim (10, 10)   orig 40 × 29   rotate 0
  new Sprite(texture):幅40、高さ29、anchor (0.5, 1)

回転を許可すると同じ6フレームが33 × 57に収まり、うち5枚が回転して保存されます。検証の実行でPixiJS 8.21は回転したフレームを正しい向きで描いたため、PixiJSプリセットは回転を許可しています。

できること

  • 再生順のanimationsマップ入りスプライトシートJSON(TexturePackerハッシュ形式)。
  • スプライト作業画面で設定したピボットからフレームごとのanchor。
  • @0.5x〜@4xの倍率違い(ニアレスト)ごとに独自のmeta.scale、複数ページはrelated_multi_packsで連結。
  • PixiJSのanimationsマップには時間がないため、ステップごとのミリ秒をmeta.nerulioに記録。

使い方

  1. フレーム・GIF・.aseprite、またはシート(適用まで)を入れる。
  2. スプライト作業画面でピボットとアニメーションを設定。
  3. パック&書き出しでPixiJS 8を選び、必要なら@2xの倍率違いを追加。
  4. Assets.loadで読み込み、アニメーションを再生。

出力されるファイル

ファイル用途
run.jsonanchor付きのフレーム、最初のページのanimationsマップ、meta.scale、複数ページならmeta.related_multi_packs、アニメーションごとのfps・ループ・方向・durationsMsを持つmeta.nerulio。
run.pngページ。倍率違いがあればrun@2x.pngと専用のrun@2x.jsonなども。
README-PIXI.mdこのバンドルを読み込むコード。書き出したフレーム時間を使うAnimatedSprite付き。

PixiJS 8で使う

  1. run.jsonとrun.pngをアプリの公開アセットの同じフォルダーに置きます。JSONのmeta.imageがPNGの名前です。
  2. ドット絵なら、何かを読み込む前にTextureSource.defaultOptions.scaleMode = "nearest"を設定します。
  3. const sheet = await Assets.load("assets/run.json")。静止フレームはnew Sprite(sheet.textures["run_0"])です。
  4. アニメーション全体を1つの速さで:const hero = new AnimatedSprite(sheet.animations["run"])、hero.animationSpeed = …、hero.play()。
  5. 書き出した時間どおりに:const ms = sheet.data.meta.nerulio.animations.run.durationsMsのあとnew AnimatedSprite(sheet.animations.run.map((texture, i) => ({ texture, time: ms[i] })))、hero.play()、app.stage.addChild(hero)。

結果の確認

  • Object.keys(sheet.textures)にrun_0 … run_5があるはずです。
  • new Sprite(sheet.textures.run_0)は幅40・高さ29:保存された18 × 15の断片ではなく、フレーム全体の大きさのはずです。
  • 4倍に拡大します。最近傍なら縁は四角いままです。ぼやけるなら、読み込み前にスケールモードを設定していません。

トラブルシューティング

症状考えられる原因確認方法対処
sheet.animationsがundefinedか空JSONにanimationsマップがない(フレームだけを書く書き出しは多い)、または複数ページのシートの後ろのページを見ているJSONの最上位にanimationsがあるか確認。複数ページなら最初のページにあるタグ付きで書き出すか、リストを自分で作る:["run_0", …].map(n => sheet.textures[n])
スプライトが左上の角でぶら下がる・違う場所に置かれるJSONにanchorがない、またはコードで上書きしているsprite.anchor.x、sprite.anchor.y書き出したアンカーを使うか、書き出し前にピボットエディターでピボットを設定
@2xシートが2倍の大きさで描かれるそのJSONに"scale": "1"と書かれているsheet.data.meta.scale"2"に直すか、倍率違いで書き出す。@2xの倍率の問題を参照
アニメーションがエディターより速い・遅いテクスチャだけを渡すと、タグのミリ秒ではなくanimationSpeed(既定1)で決まる1つの速さで再生される望む時間とhero.animationSpeedを比べる書き出したミリ秒で{ texture, time }オブジェクトを使うか、animationSpeedを調整
ドット絵がぼやけるPixiJS既定の線形スケーリングテクスチャソースのscaleModeAssets.loadの前にTextureSource.defaultOptions.scaleMode = "nearest"

ほかの方法

方法こんなときに向いています
PixiJSで読むAsepriteのJSONすでにAsepriteから書き出した絵のとき。PixiJSはフレームは読みますが、frameTagsからアニメーションは作りません。AsepriteのJSONをPixiJSへを参照。
TexturePackerのPixiJS書き出しすでにTexturePackerを使っているとき。既定で回転するpixijs4の出力は、試した4セットすべてでPixiJS 8.21に合格しました。
PNGを1枚ずつAssets.loadフレームが数枚だけの試作。フレームごとにリクエスト1つとテクスチャ1つ、アトラスなし。

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

「検証済み」は、実際の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は完全一致。

検証の根拠

書き出し:実在のCC0アセットで実際のエンジンを49回実行し47回合格。失敗2回はPhaser 3.90のトリムXMLの不具合で、Phaser 3プリセットで回避。実在の5フレームセットでパッカー比較(CodeAndWeb無料Webパッカー、GAPTools、free-tex-packer):回転ありで5つすべて最小、回転なしで5つ中4つ。全フレームがピクセル単位で復元できます。

制限

  • ポリゴン(メッシュ)パッキングはありません。検証したエンジンのどれも、書き出しデータからメッシュアトラスを描画しないためです。
  • GameMakerのストリップはGameMaker本体では未検証です。
  • 拡大縮小バリエーション(@0.5x〜@4x)はニアレストネイバーのみです。
  • トリムしたStarling XMLはPhaser 3.90で正しく描画されないため、「Sparrow XML (Phaser 3)」プリセットを使ってください。

バージョンと出典

PixiJS 8.21.0(ChromiumのWebGL)で通常のAssets.loadにより読み込み、矩形、トリム、アンカー、アニメーション、回転したフレーム、複数ページのシートを読み戻して、全フレームを元画像と比較しました。例の数値はその実行で読み戻した値です。API名はPixiJS 8のAPIリファレンスに従います。

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

よくある質問

フレームの時間はどこに入る?

PixiJSのanimationsマップはフレーム名の一覧だけなので、ステップごとのミリ秒をmeta.nerulioに書きます。animationSpeedや長さを自分で設定するときに使ってください。

回転は使えますか?

PixiJSでは使えます。回転したフレームをPixiJS 8.21が正しく描画しました。

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

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

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

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

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

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