LÖVE(Love2D)スプライトアトラスとQuad · 範囲の手書き不要
LÖVEにはアトラス形式がないため、フレームの範囲はふつう手で書きます。この書き出しはそれをLuaのQuadテーブルとして書き、アニメーションを描いて進めるnerulio_atlas.luaも付け、LÖVE 11.5でフレームごとに実行しました。
LÖVEにはアトラスのファイル形式がありません。love.graphics.newImageでPNGを読み込み、フレームごとにlove.graphics.newQuad(x, y, w, h, ページ幅, ページ高さ)でQuadを作り、love.graphics.draw(image, quad, x, y, r, sx, sy, ox, oy)で描きます。Nerulioはフレームをパックしてこれらの数値をLuaのテーブル(矩形、トリムのオフセット、ピボット、秒単位の長さを持つアニメーション)として書き、Quadを作ってアニメーションを進める小さなヘルパーnerulio_atlas.luaを添えます。LÖVE 11.5がこのヘルパーで全フレームとアニメーションを描画しました。

- LÖVE検証済み· LÖVE 11.5
- 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
- Defoldビルド確認· Defold bob.jar 1.13.1
- GameMaker未検証
- CSS sprites検証済み· Chromium
- GIF / APNGデコード確認· Pillow
Quad、原点、トリムしたフレーム
Quadは画像の矩形にすぎません。描くと矩形の左上がx, yに置かれます。love.graphics.drawの最後の引数で原点のオフセットox, oyを渡すと変わり、これでフレームを足元や中心を基準に描けます。拡大や回転もこの点を中心に行われます。
トリムしたフレームは見えるピクセルだけを保存しているので、原点をトリム分だけ補正する必要があります。原点 = ピボット − トリムのオフセット。書き出したテーブルでは、px, pyがフレーム全体のキャンバス上のピボット、ox, oyが保存ピクセルのキャンバス上の開始位置です。ヘルパーは原点(px − ox, py − oy)で描きます。
LÖVEプリセットは押し出し1px、間隔2pxでパックし、回転しません。そのためどのQuadも正立していて、周りに縁のピクセルの複製があります。ヘルパーは画像を読み込む前にlove.graphics.setDefaultFilter("nearest", "nearest")を呼ぶので、4倍でもドット絵がくっきりしたままです。
- Quad
- x、y、幅、高さと画像の大きさから
love.graphics.newQuadで作る、画像の矩形。 - drawのox, oy
love.graphics.drawの原点オフセット。x, yに来る点。- テーブルのox, oy
- トリムしたフレームの保存ピクセルが、元のキャンバス上で始まる位置。
- durations
- アニメーションのフレームごとの秒数。タグのミリ秒から変換した値。
例:テーブルから画面までの1フレーム
run.lua (ページ run.png 64 × 37:押し出し1、間隔2、回転なし)
["run_0"] = { page = 1, x = 1, y = 20, w = 18, h = 15, ox = 10, oy = 10, sw = 40, sh = 29, px = 20, py = 29 }
["run"] = { loop = true, frames = { "run_0", …, "run_5" }, durations = { 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 } }
quad = love.graphics.newQuad(1, 20, 18, 15, 64, 37)
原点 = (px − ox, py − oy) = (20 − 10, 29 − 10) = (10, 19)
love.graphics.draw(image, quad, 200, 200, 0, 4, 4, 10, 19)
→ フレームの足元が (200, 200) に来て、4倍で描かれる
ページ上の間隔:run_5はx = 1–21、その押し出し列はx = 22、
間隔x = 23–24、run_3の押し出し列x = 25、run_3はx = 26から保存された断片は自分の押し出しの帯の1px内側にあり、次の断片とは少なくとも4px(1 + 2 + 1)離れています。そのため小数の位置や倍率で描いても、縁では自分の色を読みます。
できること
- 既定で1pxの縁の拡張入りアトラスPNG(非整数位置でのにじみを防止)。
- ピボット・アニメーション・フレームごとの長さ入りのLua Quadテーブル。
- nerulio_atlas.luaヘルパーと、全アニメーションをNearestで描くmain.lua。
- この書き出し先は回転しないので、すべてのQuadはまっすぐです。
使い方
- フレーム・GIF・.aseprite、またはシート(適用まで)を入れる。
- アニメーションとピボットを設定。
- パック&書き出しでLÖVEを選ぶ。
- ファイルをmain.luaの横に置き、nerulio_atlasをrequire。
出力されるファイル
| ファイル | 用途 |
|---|---|
run.lua | images、pages(w, h)、frames(x, y, w, h、トリムのox/oy、キャンバスのsw/sh、ピボットpx/py)、order、animations(ループ、フレーム、秒単位の長さ)。 |
nerulio_atlas.lua | Atlas.load(path)が画像とQuadを作り、atlas:draw(key, x, y, sx, sy)がピボット基準で描き、atlas:play(name)がupdate(dt)とdrawを持つ小さなプレーヤーを返す。 |
main.lua | 最初のアニメーションを4倍で再生する例(フォルダーでlove .)。 |
run.png | 全フレームの周りに1pxの押し出しがあるページ。 |
LÖVE 11で使う
run.png、run.lua、nerulio_atlas.luaをゲームのフォルダーに入れます。main.luaの隣か、パスをAtlas.loadに渡すサブフォルダーに置きます。love.loadでAtlas = require("nerulio_atlas")、atlas = Atlas.load("run.lua")、anim = atlas:play("run")。love.update(dt)でanim:update(dt)、love.draw()でanim:draw(x, y, 4)を呼ぶと、ピボットがx, yに来るように4倍で描きます。- ヘルパーなしなら:
local data = love.filesystem.load("run.lua")()で読み、フレームごとにlove.graphics.newQuad(f.x, f.y, f.w, f.h, page.w, page.h)を作って、原点f.px − f.ox, f.py − f.oyで描きます。
結果の確認
- 書き出したフォルダーで
love .を実行すると、main.luaが最初のアニメーションを4倍、ピクセルの縁が四角いまま再生します。 - run_0とrun_4を同じ点に描きます。トリムのオフセットが(10, 10)と(10, 8)で違っても、足元は一直線にそろうはずです。
runの1周は0.6秒です。0.1秒の長さが6つ。
トラブルシューティング
| 症状 | 考えられる原因 | 確認方法 | 対処 |
|---|---|---|---|
| アニメーション中にフレームが上下に跳ねる | Quadをトリムとピボットの原点なしで、それぞれの左上基準で描いている | 2つのフレームを同じ点に描き、Studioのプレビューと比べる | 原点px − ox, py − oyを渡すか、ヘルパーのdrawを使う |
| 拡大するとピクセルがぼやける | setDefaultFilter("nearest", "nearest")より前に画像を読み込んだ、または自分の画像のフィルターが線形 | newImageとの順番でフィルター設定を確認 | 既定のフィルターを先に設定する(ヘルパーはそうしている)か、image:setFilter("nearest", "nearest")を呼ぶ |
| 小数の位置や倍率で、隣のフレームの細い線が見える | Quadの周りに押し出しも間隔もない手作りのシート | フレームを半ピクセルずつ動かし、縁を4倍で見る | LÖVEプリセット(押し出し1、間隔2)でパックし直す |
module 'nerulio_atlas' not found | ヘルパーがrequireのパスにない | love.filesystem.getInfo("nerulio_atlas.lua") | main.luaの隣に置くか、フォルダー付きでrequireする。例:require("lib.nerulio_atlas") |
| アニメーションが最後のフレームで止まる | タグがループしないため、プレーヤーが最後のフレームを保持している | data.animations.run.loop | 書き出し前にタグを無限ループにするか、atlas:play("run")でプレーヤーを作り直す |
ほかの方法
| 方法 | こんなときに向いています |
|---|---|
| 均一なグリッドとループで計算したQuad | 全フレームが同じ大きさでトリムがないとき。セルごとにlove.graphics.newQuad(col * w, row * h, w, h, sheetW, sheetH)で、データファイルは不要です。 |
| LuaのJSONライブラリで読むTexturePackerのJSON | すでにJSONのアトラスがあるとき。各frameの矩形から同じようにQuadを作りますが、トリムしたフレームは原点にspriteSourceSizeのオフセットを反映する必要があります。 |
| 同じフレームを別のエンジンで | Defoldアトラス、Phaserのテクスチャアトラス。 |
エンジン別の書き出しと検証方法
「検証済み」は、実際の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)」プリセットを使ってください。
バージョンと出典
LÖVE 11.5(ポータブル版)で、書き出したnerulio_atlas.luaをrequireする検査用スクリプトを実行し、全フレームをCanvasに描き、ヘルパーのプレーヤーで全アニメーションを進め、1フレームを4倍で描きました。ピクセルを読み戻して元のフレームと比較しています。関数のシグネチャはLÖVE 11.5のソースに従います。
参照した公式ドキュメント
よくある質問
LÖVEで何を確認しましたか?
LÖVE 11.5が同梱ヘルパーで全フレームを描画し、全アニメーションを進め、4倍でもくっきりでした。
ヘルパーは必須ですか?
いいえ。QuadテーブルはふつうのLuaデータで、ヘルパーは描画と時間管理の一例にすぎません。
無料ですか?アカウントは必要ですか?
このページの機能はすべて無料で、アカウントなしで使えます。透かしや制限された書き出しはありません。
スプライトはアップロードされますか?
いいえ。Studioはこのブラウザのタブ内でデコード・パック・書き出しを行います。プロジェクトはブラウザ(IndexedDB)に自動保存され、.nerulioファイルとして保存できます。
Asepriteやエンジンのプラグインは必要ですか?
いいえ。.asepriteファイルはブラウザで読み書きします。エンジン別のバンドルには必要なものが入っています(Godotはインポート設定とシーン、UnityとGodotのタイルセットは小さなエディタースクリプト)。