@unboxy/phaser-sdk 0.2.34 → 0.2.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -115,6 +115,56 @@ function collectAssetIds(entities) {
115
115
  walk(e);
116
116
  return Array.from(ids);
117
117
  }
118
+ /**
119
+ * Walk every `kind=spritesheet` asset in the manifest and register each one's
120
+ * `animations[]` as a Phaser animation, so behavior code can call
121
+ * `sprite.play('walk-down')` directly. Idempotent — uses `scene.anims.exists()`
122
+ * to skip duplicates (Phaser's anims.create throws otherwise).
123
+ *
124
+ * <p>Animation keys are scene-global. Two sheets registering the same name
125
+ * (e.g. two characters both with `walk-down`) → first wins, second logs a
126
+ * warning. Sheets that need disambiguation should namespace their keys
127
+ * (e.g. `cow:walk-down`) at metadata time.
128
+ */
129
+ function registerSheetAnimations(scene, manifest) {
130
+ const assets = manifest.assets ?? [];
131
+ for (const asset of assets) {
132
+ if (asset.kind !== 'spritesheet')
133
+ continue;
134
+ if (!asset.animations || asset.animations.length === 0)
135
+ continue;
136
+ if (!scene.textures.exists(asset.textureKey))
137
+ continue; // not loaded — skip
138
+ const defaultFps = asset.fps && asset.fps > 0 ? asset.fps : 8;
139
+ for (const anim of asset.animations) {
140
+ if (!anim.name)
141
+ continue;
142
+ if (scene.anims.exists(anim.name)) {
143
+ // Don't error out on collision — first registration wins.
144
+ // eslint-disable-next-line no-console
145
+ console.warn(`[unboxy/scene] animation key '${anim.name}' already registered; skipping (asset '${asset.id}'). Disambiguate by renaming in the asset's animations metadata.`);
146
+ continue;
147
+ }
148
+ const fps = anim.fps && anim.fps > 0 ? anim.fps : defaultFps;
149
+ const loop = anim.loop !== false; // default true
150
+ try {
151
+ scene.anims.create({
152
+ key: anim.name,
153
+ frames: scene.anims.generateFrameNumbers(asset.textureKey, {
154
+ start: anim.from,
155
+ end: anim.to,
156
+ }),
157
+ frameRate: fps,
158
+ repeat: loop ? -1 : 0,
159
+ });
160
+ }
161
+ catch (e) {
162
+ // eslint-disable-next-line no-console
163
+ console.warn(`[unboxy/scene] failed to register animation '${anim.name}' on '${asset.id}': ${String(e)}`);
164
+ }
165
+ }
166
+ }
167
+ }
118
168
  function queueAssetLoad(scene, asset) {
119
169
  if (scene.textures.exists(asset.textureKey))
120
170
  return;
@@ -182,6 +232,10 @@ export async function loadWorldScene(scene, sceneId, options = {}) {
182
232
  // Lazy preload of any new assets this scene needs, then await loader.
183
233
  preloadSceneAssets(scene, sceneFile, manifest);
184
234
  await runLoader(scene);
235
+ // Register Phaser animations declared in `manifest.assets[].animations`.
236
+ // Done after texture load + before entity spawn so behavior code can call
237
+ // `sprite.play('walk-down')` from frame 1 without manual `anims.create()`.
238
+ registerSheetAnimations(scene, manifest);
185
239
  // Spawn entities.
186
240
  const registry = attachEntityRegistry(scene);
187
241
  const ctx = {
@@ -46,6 +46,35 @@ export interface AssetRecord {
46
46
  atlasPath?: string;
47
47
  /** `'xml'` (Starling) or `'json'` (TexturePacker). */
48
48
  atlasFormat?: 'xml' | 'json';
49
+ /**
50
+ * For `spritesheet`: named animation ranges. SDK auto-registers these as
51
+ * Phaser anims at preload, so behavior code can call
52
+ * `sprite.play('walk-down')` without manual `anims.create()` setup.
53
+ *
54
+ * <p>Pack import populates this from vision detection (4-direction RPG /
55
+ * sidescroller / multi-action layouts) or from Aseprite `frameTags`. The
56
+ * agent can also add/edit entries by hand when wiring a sheet asset.
57
+ *
58
+ * <p>Animation keys are scene-global in Phaser; if two sheets register
59
+ * the same name, the second registration is skipped with a warning.
60
+ * Disambiguate by namespacing the name (e.g. `cow-walk-down`) when
61
+ * collisions matter.
62
+ */
63
+ animations?: SheetAnimation[];
64
+ /** Default playback rate for `animations[]`. Defaults to 8. */
65
+ fps?: number;
66
+ }
67
+ export interface SheetAnimation {
68
+ /** Phaser animation key — used as `sprite.play('<name>')`. */
69
+ name: string;
70
+ /** First frame index, inclusive. */
71
+ from: number;
72
+ /** Last frame index, inclusive. */
73
+ to: number;
74
+ /** When true, Phaser registers with `repeat: -1`. Defaults to true. */
75
+ loop?: boolean;
76
+ /** Override the asset-level fps for this specific animation. */
77
+ fps?: number;
49
78
  }
50
79
  export interface Manifest {
51
80
  schemaVersion: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboxy/phaser-sdk",
3
- "version": "0.2.34",
3
+ "version": "0.2.35",
4
4
  "description": "Unboxy Phaser 3 SDK — game infrastructure for the Unboxy platform",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",