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で確認しています。

- 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に記録。
使い方
- フレーム・GIF・.aseprite、またはシート(適用まで)を入れる。
- スプライト作業画面でピボットとアニメーションを設定。
- パック&書き出しでPixiJS 8を選び、必要なら@2xの倍率違いを追加。
- Assets.loadで読み込み、アニメーションを再生。
出力されるファイル
| ファイル | 用途 |
|---|---|
run.json | anchor付きのフレーム、最初のページの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で使う
run.jsonとrun.pngをアプリの公開アセットの同じフォルダーに置きます。JSONのmeta.imageがPNGの名前です。- ドット絵なら、何かを読み込む前に
TextureSource.defaultOptions.scaleMode = "nearest"を設定します。 const sheet = await Assets.load("assets/run.json")。静止フレームはnew Sprite(sheet.textures["run_0"])です。- アニメーション全体を1つの速さで:
const hero = new AnimatedSprite(sheet.animations["run"])、hero.animationSpeed = …、hero.play()。 - 書き出した時間どおりに:
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既定の線形スケーリング | テクスチャソースのscaleMode | Assets.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 / 4 | atlas JSON (multiatlas) · .anims.json | 検証済み Phaser 3.90 + 4.2両バージョンでフレームとアニメーションを描画。 |
| PixiJS 8 | spritesheet JSON · animations · anchor · meta.scale | 検証済み PixiJS 8.21回転・複数ページのアトラスも確認。 |
| Aseprite JSON | hash / array · frameTags · slices | 検証済み Phaser load.aseprite · PixiJS 8.21タグの方向と繰り返し回数を保持。 |
| .aseprite | layers · tags · durations · slices | 検証済み Aseprite 1.3.18Asepriteのコマンドラインで開いて描画。 |
| Spine / libGDX | .atlas | 検証済み spine-canvas 4.2Spineランタイムで回転・トリム領域をすべて描画。libGDX本体は未実行。 |
| Starling / Sparrow XML | XML per page | 検証済み Phaser 4.2Phaser 3.90はトリムされたXMLフレームを誤って描画します(Phaser側の不具合)。「Sparrow XML (Phaser 3)」プリセットはトリムを切り、3.90・4.2とも合格。 |
| LÖVE | Lua quads · nerulio_atlas.lua · main.lua | 検証済み LÖVE 11.5同梱ヘルパーで全フレームを描画し、全アニメーションを再生。 |
| Defold | .atlas · .tilesource | ビルド確認 Defold bob.jar 1.13.1Defoldのコマンドラインビルダーでビルドし、生成テクスチャのUVを読み戻して確認。 |
| GameMaker | spr_<name>_stripN.png · gamemaker.json | 未検証GameMakerでは読み込んでいません(ヘッドレス実行不可)。ストリップはGameMakerの取り込み方式どおりに切ります。 |
| CSS sprites | .css · preview .html | 検証済み Chromium同梱スタイルシートで全クラスを描画。 |
| GIF / APNG | one 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のタイルセットは小さなエディタースクリプト)。