用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/CodySwannGT/lisa --skill phaser-asset-pipeline命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
any non-trivial request —…
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
基于 SOC 职业分类
正在显示 SKILL.md
| name | phaser-asset-pipeline |
| description | setting up or changing the… |
This skill packs raw art; it does not conjure it. The inputs under
assets/src/** are real, licensed source art you sourced first — they do not
magically pre-exist, and they are not procedural generateTexture rectangles.
Before you can pack anything, use [[phaser-asset-sourcing]]: it covers where
game art comes from (curated CC0 packs like Kenney and Ninja Adventure → deriving
CC0 art → asking the human), the strict CC0-or-equivalent license gate with
assets/LICENSES.md evidence, provenance via a committed ingest script, and why
generateTexture placeholders are tracked art debt rather than a finished state.
The rule of thumb: source → assets/src → pack (this skill) → typed keys.
The pipeline turns committed source art into runtime form — it is a build step,
not a substitute for having art. Raw source art lives in assets/src/** (PNGs,
audio, font sources); a build step packs it into public/assets/** (atlases,
audio sprites, bitmap fonts) and codegens typed key constants into
src/assets.ts. The payoff: a missing or renamed asset is a compile error,
not a green square or silent-missing audio at runtime. No raw string asset /
scene / event keys anywhere — they all come from the generated module (the
official loading-assets skill covers loading those keys at runtime).
| Path | Role |
|---|---|
assets/src/sprites/** | Raw individual PNGs — the art source of truth (sourced per [[phaser-asset-sourcing]]) |
assets/src/audio/** | Raw audio clips (wav/ogg sources) |
assets/src/fonts/** | Font sources for BMFont generation |
assets/LICENSES.md | License evidence per source pack (CC0 quote + evidence URL) — see [[phaser-asset-sourcing]] |
scripts/ingest-assets.mjs | The provenance record — how each frame was carved from its source pack into assets/src (re-runnable) |
public/assets/** | Generated packed output (atlases, audiosprites, fonts, pack.json) — git-ignored or committed as a build artifact |
src/assets.ts | Generated typed key constants — never edited by hand |
scripts/pack-assets.mjs | The pipeline driver (runs the three packers + codegen) |
free-tex-packer-core packs many PNGs into atlas pages plus a JSON manifest, in
a Node script (no GUI, runs in CI):
// scripts/pack-assets.mjs (excerpt)
import { packAsync } from "free-tex-packer-core";
import { glob } from "glob";
import { readFile, writeFile } from "node:fs/promises";
const images = await Promise.all(
(await glob("assets/src/sprites/**/*.png")).map(async p => ({
path: p.replace("assets/src/sprites/", ""), contents: await readFile(p),
})));
const files = await packAsync(images, {
textureName: "game", width: 4096, height: 4096,
packer: "MaxRectsPacker", packerMethod: "Smart", // see gotcha below
padding: 2, extrude: 1, // 1px extrude kills bleeding at integer scales
allowRotation: false, allowTrim: false, // pixel-art-safe (see below)
detectIdentical: ,
: , : , : ,
});
( f files) (, f. ?? f.);
The frame names in the manifest become the atlas frame keys you reference as
generated constants. Atlas everything that renders together — loose images break
sprite batching (the official loading-assets skill). Full-screen backdrops and
parallax layers are the exception: copy them verbatim into public/assets/images
and load.image them — they would bloat an atlas. Even this exception keeps the
no-raw-string promise: the codegen step emits one generated constant per copied
image (the Img record below), and the loader derives the file path from the
key in exactly one place — a call site never spells out a path string.
Packer gotcha (verified in the reference implementation): use
packer: "MaxRectsPacker" with packerMethod: "Smart". The other packer methods
produce degenerate single-row strip atlases that waste enormous texture space
and can blow past the max texture size. For pixel art, set allowTrim: false
and allowRotation: false: trimming makes animation frames different sizes so
anchors swim between frames, and rotation makes Phaser frame-flipping non-trivial.
Add extrude: 1 to stop edge bleeding when the game renders at integer scales.
Keep inputs sorted and emit no timestamps so the packed output is
deterministic and can be committed + git diff --exit-code-checked in CI.
audiosprite concatenates short SFX into one file (in multiple codecs) plus a
JSON map of { start, end } per clip — one request, one decode, instead of dozens:
// package.json script
"pack:audio": "audiosprite --output public/assets/audio/sfx --export ogg,m4a --format howler2 assets/src/audio/*.wav"
Provide at least two codecs (ogg + m4a) so every browser can play one. Load
with this.load.audioSprite(Audio.Sfx, "audio/sfx.json") and play by sprite key.
BitmapText is the performant choice for high-churn text (scores, timers) —
the official loading-assets skill / [[phaser-i18n]]. Generate the .fnt (XML/JSON) + PNG page
from a font source with a BMFont tool (msdf-bmfont-xml or the bmfont CLI) in
the same script:
"pack:font": "msdf-bmfont -o public/assets/fonts/ui --font-size 42 --texture-size 1024 1024 assets/src/fonts/ui.ttf"
Load with this.load.bitmapFont(Font.UI, "fonts/ui.png", "fonts/ui.fnt").
src/assets.tsAfter packing, walk the generated manifests and emit as const key maps. This is
the step that turns runtime failures into compile failures:
// scripts/pack-assets.mjs (codegen excerpt)
const atlas = JSON.parse(await readFile("public/assets/atlases/game.json", "utf8"));
const frames = Object.keys(atlas.frames); // raw keys, e.g. "player/idle"
const sfx = Object.keys(JSON.parse(await readFile("public/assets/audio/sfx.json","utf8")).spritemap);
const images = (await readdir("public/assets/images", { recursive: true }))
.filter(f => f.endsWith(".png")).sort(); // standalone backdrops/parallax layers
const out = `// AUTO-GENERATED by scripts/pack-assets.mjs — do not edit.
export const Tex = { GameAtlas: "game" } as const;
export const Frame = { ${frames.map(k => `${toIdent(k)}: "${k}"`).join(", ")} } as const;
export const Img = { ${images.map(p => `${toIdent(p)}: "img-${p.replace(/\.png$/u, "")}"`).join(", ")} } as const;
export const Audio = { Sfx: "sfx" } as const;
export const Sfx = { } as const;
export const Font = { UI: "ui" } as const;
export const SceneKeys = { Boot:"Boot", Preloader:"Preloader", MainMenu:"MainMenu", Game:"Game" } as const;
export const GameEvent = { ScoreChanged:"score-changed", EnemyDied:"enemy-died" } as const;
`;
(, out);
Scenes import Tex, Frame, Img, Sfx, Font, SceneKeys, GameEvent —
never an inline string. Rename art, re-run the pipeline, and every now-stale
reference fails bun run typecheck immediately. (Scene/event keys are kept in
the same generated module so they share the no-raw-string discipline; edit them
via the codegen template, not by hand.)
Animation keys are the one deliberately hand-authored layer. Codegen cannot
know how frames group into cycles, so animation keys live in a hand-written
as const record (e.g. AnimKeys / FxAnims) whose frame sequences are built
from the generated Frame constants — and the contract test below proves every
frame those definitions reference exists in the committed atlases. The typed-key
guarantee is therefore: generated constants for textures, frames, images,
audio, and fonts; hand-authored typed constants + contract-test coverage for
animation keys. Nothing is a raw inline string either way.
The pipeline runs before dev and before build so generated output is never stale:
// package.json
"scripts": {
"assets": "node scripts/pack-assets.mjs",
"predev": "bun run assets",
"prebuild": "bun run assets",
"dev": "vite",
"build": "vite build"
}
For fast iteration, a --watch mode on the pack script re-packs changed source
art. In CI the pack step runs once before vite build; the build then hashes the
generated files for immutable caching ([[phaser-build-deploy]]).
assets/src/** is the source of truth; public/assets/** and src/assets.ts
are generated — treat them as build output (re-runnable, not hand-edited).
One atlas per render group, one audiosprite per SFX set, BMFont for hot text.
A contract test asserts every frame name the game derives by convention
(e.g. battlerWalkFrame(ref, dir, n), FX-strip frame lists, per-speaker
portrait keys) resolves to a real entry in the committed atlas JSON under
public/assets. The other constants in src/assets.ts (SceneKeys,
GameEvent, Font, Audio, standalone Img entries) are plain generated
literals already enforced by TypeScript at every use site, so they don't need
a runtime check — only names built at runtime from a pattern can go stale
silently without one. This keeps that narrower guarantee honest: a renamed or
missing frame fails a headless Vitest assertion instead of rendering a silent
black square. The test reads the packed JSON directly — zero Phaser
([[phaser-testing]]):
const frames = new Set(Object.keys(
JSON.parse(readFileSync("public/assets/atlases/battlers.json", "utf8")).frames));
for (const ref of BATTLER_REFS)
for (const dir of DIRS) {
expect(frames).toContain(battlerIdleFrame(ref, dir));
expect(frames).toContain(battlerAttackFrame(ref, dir));
for ( n = ; n < ; n++)
(frames).((ref, dir, n));
}
The pipeline is verified by deleting a source PNG and re-running bun run assets:
the corresponding generated constant disappears and bun run typecheck fails at
every use site (proving keys are compile-checked). Then boot the game and confirm
the atlas/audiosprite/font load with no FILE_LOAD_ERROR and render/play
correctly.
Ship a --check mode on the pack script: it re-packs into a temp staging area
and exits non-zero if any committed output under public/assets — atlases,
standalone images, audio, fonts alike — or src/assets.ts is stale, so CI
proves the committed artifacts (and every generated key they back) are current.
The generated src/assets.ts is the single source of asset/scene/event keys —
the no-raw-string-keys rule depends on it existing.