@unboxy/phaser-sdk 0.2.37 → 0.2.38

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.
package/dist/index.d.ts CHANGED
@@ -27,6 +27,6 @@ export type { RenderScriptModule } from './scene/renderScripts.js';
27
27
  export { setupEditorBridge } from './editor/EditorBridge.js';
28
28
  export { EditorOverlayScene, EDITOR_OVERLAY_KEY } from './editor/EditorOverlayScene.js';
29
29
  export type { EditorEntityPatch, EditorEnterMessage, EditorExitMessage, EditorGetSceneMessage, EditorApplyEditMessage, EditorSetSelectionMessage, EditorPanZoomMessage, EditorHostToSdkMessage, EditorSceneLoadedMessage, EditorSelectionPickedMessage, EditorDragEndMessage, EditorShortcutMessage, EditorCreateEntityMessage, EditorDeleteEntityMessage, EditorSetEditModeMessage, EditorSelectionRectMessage, EditorSdkToHostMessage, } from './protocol.js';
30
- export { SCHEMA_VERSION, } from './scene/types.js';
31
- export type { Manifest, SceneRef, HudRef, AssetRecord, AssetKind, SceneType, SceneFile, WorldScene, HudScene, WorldSceneConfig, CameraConfig, WorldEntity, WorldEntityKind, NonGroupWorldEntity, SpriteEntity, PrimitiveEntity, CodeRenderedEntity, GroupEntity, TilemapEntity, TriggerEntity, TriggerShape, WorldVisual, SpriteVisual, PrimitiveVisual, PrimitiveRectVisual, PrimitiveCircleVisual, CodeRenderedVisual, HudEntity, HudEntityKind, HudEntityBase, HudTextEntity, HudImageEntity, HudIconButtonEntity, HudProgressBarEntity, HudPanelEntity, HudVisual, HudTextVisual, HudImageVisual, HudIconButtonVisual, HudProgressBarVisual, HudPanelVisual, HudTextSource, HudNumberSource, HudLayer, Transform, Anchor, AnchorSide, NinePatchConfig, } from './scene/types.js';
30
+ export { SCHEMA_VERSION, isPerFrameNinePatch, } from './scene/types.js';
31
+ export type { Manifest, SceneRef, HudRef, AssetRecord, AssetKind, SceneType, SceneFile, WorldScene, HudScene, WorldSceneConfig, CameraConfig, WorldEntity, WorldEntityKind, NonGroupWorldEntity, SpriteEntity, PrimitiveEntity, CodeRenderedEntity, GroupEntity, TilemapEntity, TriggerEntity, TriggerShape, WorldVisual, SpriteVisual, PrimitiveVisual, PrimitiveRectVisual, PrimitiveCircleVisual, CodeRenderedVisual, HudEntity, HudEntityKind, HudEntityBase, HudTextEntity, HudImageEntity, HudIconButtonEntity, HudProgressBarEntity, HudPanelEntity, HudVisual, HudTextVisual, HudImageVisual, HudIconButtonVisual, HudProgressBarVisual, HudPanelVisual, HudTextSource, HudNumberSource, HudLayer, Transform, Anchor, AnchorSide, NinePatchConfig, NinePatchPerFrame, } from './scene/types.js';
32
32
  export { PROTOCOL_VERSION, type HelloMessage, type InitMessage, type RpcRequestMessage, type RpcResultOk, type RpcResultError, type HostToSdkMessage, type SdkToHostMessage, type RpcErrorPayload, type RpcMethod, type SavesGetParams, type SavesGetResult, type SavesSetParams, type SavesSetResult, type SavesDeleteParams, type SavesDeleteResult, type SavesListResult, type GameDataGetParams, type GameDataGetResult, type GameDataSetParams, type GameDataSetResult, type GameDataDeleteParams, type GameDataDeleteResult, type GameDataListResult, type RealtimeGetTokenParams, type RealtimeGetTokenResult, } from './protocol.js';
package/dist/index.js CHANGED
@@ -21,5 +21,5 @@ export { setupEditorModeListener, isEditMode } from './scene/EditorMode.js';
21
21
  export { setRenderScriptRegistry, getRenderScriptRegistry, resolveRenderScript, } from './scene/renderScripts.js';
22
22
  export { setupEditorBridge } from './editor/EditorBridge.js';
23
23
  export { EditorOverlayScene, EDITOR_OVERLAY_KEY } from './editor/EditorOverlayScene.js';
24
- export { SCHEMA_VERSION, } from './scene/types.js';
24
+ export { SCHEMA_VERSION, isPerFrameNinePatch, } from './scene/types.js';
25
25
  export { PROTOCOL_VERSION, } from './protocol.js';
@@ -1,5 +1,5 @@
1
1
  import Phaser from 'phaser';
2
- import { SCHEMA_VERSION, } from './types.js';
2
+ import { SCHEMA_VERSION, isPerFrameNinePatch, } from './types.js';
3
3
  import { attachEntityRegistry, getEntityRegistry, } from './EntityRegistry.js';
4
4
  import { parseColor } from './spawnEntity.js';
5
5
  import { applyPixelArtFilters, getManifest, SCENES_BASE } from './SceneLoader.js';
@@ -80,21 +80,44 @@ const LAYER_DEPTH = {
80
80
  modal: 300,
81
81
  };
82
82
  /**
83
- * Render an image asset at (x, y) sized to (width, height). When the asset
84
- * carries `ninePatch` metadata, uses `phaser3-rex-plugins`' NinePatch (corners
85
- * fixed, edges + center stretch); otherwise falls back to a regular Image
86
- * scaled with `setDisplaySize`. Caller owns origin/anchor both Image and
87
- * NinePatch (which extends RenderTexture) expose `setOrigin`. Returns the
88
- * created GameObject already added to the scene's display list.
83
+ * Render an image (or one cell of a uniform-grid spritesheet) at (x, y) sized
84
+ * to (width, height). When the asset carries `ninePatch` metadata, uses
85
+ * `phaser3-rex-plugins`' NinePatch (corners fixed, edges + center stretch);
86
+ * otherwise falls back to a regular Image scaled with `setDisplaySize`.
87
+ *
88
+ * Two ninePatch shapes accepted:
89
+ * - Single image (slice 7.5): `asset.ninePatch` is a `NinePatchConfig`. The
90
+ * whole texture slices into 9 regions.
91
+ * - Per-frame on a uniform-grid sheet (slice 7.6): `asset.ninePatch` is
92
+ * `{ perFrame: NinePatchConfig }`. The shared cuts apply to whichever
93
+ * frame the caller picks via `frame`. Common case: itch.io button packs
94
+ * where every cell is a different button color/state with the same
95
+ * corner radius.
96
+ *
97
+ * Caller owns origin/anchor — both Image and NinePatch (which extends
98
+ * RenderTexture) expose `setOrigin`. Returns the created GameObject already
99
+ * added to the scene's display list.
89
100
  */
90
- function createImageOrNinePatch(scene, x, y, width, height, asset) {
101
+ function createImageOrNinePatch(scene, x, y, width, height, asset, frame) {
91
102
  if (asset.ninePatch) {
92
103
  const np = asset.ninePatch;
104
+ const isPerFrame = isPerFrameNinePatch(np);
105
+ const cuts = isPerFrame ? np.perFrame : np;
93
106
  const factory = scene.add.rexNinePatch;
94
107
  if (factory) {
108
+ const columns = [cuts.leftWidth, undefined, cuts.rightWidth];
109
+ const rows = [cuts.topHeight, undefined, cuts.bottomHeight];
110
+ // Per-frame uses rex's 9-arg `(scene, x, y, w, h, key, baseFrame, cols, rows)`
111
+ // form — baseFrame scopes the slicing to that one cell of the spritesheet.
112
+ // rex types baseFrame as string; Phaser accepts either string or number
113
+ // for spritesheet frame access, but passing String() satisfies the typed
114
+ // overload on rex's side.
115
+ if (isPerFrame) {
116
+ return factory.call(scene.add, x, y, width, height, asset.textureKey, String(frame ?? 0), columns, rows);
117
+ }
95
118
  // Middle column/row left undefined → that segment stretches to fill
96
119
  // the remaining space between the two fixed-width edges.
97
- return factory.call(scene.add, x, y, width, height, asset.textureKey, [np.leftWidth, undefined, np.rightWidth], [np.topHeight, undefined, np.bottomHeight]);
120
+ return factory.call(scene.add, x, y, width, height, asset.textureKey, columns, rows);
98
121
  }
99
122
  // Plugin not registered — degrade to a stretched Image rather than crash
100
123
  // an entire HUD render. Symptom is "corners distort on resize", not a
@@ -103,7 +126,11 @@ function createImageOrNinePatch(scene, x, y, width, height, asset) {
103
126
  // eslint-disable-next-line no-console
104
127
  console.warn(`[unboxy/hud] rexNinePatch plugin not registered; falling back to stretched Image for asset '${asset.id}'`);
105
128
  }
106
- const image = scene.add.image(x, y, asset.textureKey);
129
+ // Fallback: plain Image. Frame index threaded through for spritesheet
130
+ // assets so per-cell rendering still works without 9-slice.
131
+ const image = frame !== undefined
132
+ ? scene.add.image(x, y, asset.textureKey, frame)
133
+ : scene.add.image(x, y, asset.textureKey);
107
134
  image.setDisplaySize(width, height);
108
135
  return image;
109
136
  }
@@ -261,7 +288,7 @@ function createIconButton(ctx, entity, pos) {
261
288
  if (v.backgroundAssetId) {
262
289
  try {
263
290
  const bgAsset = ctx.resolveAsset(v.backgroundAssetId);
264
- const np = createImageOrNinePatch(ctx.scene, 0, 0, w, h, bgAsset);
291
+ const np = createImageOrNinePatch(ctx.scene, 0, 0, w, h, bgAsset, v.backgroundFrame);
265
292
  np.setOrigin?.(0.5, 0.5);
266
293
  bg = np;
267
294
  texturedBg = true;
@@ -439,7 +466,7 @@ function createPanel(ctx, entity, pos) {
439
466
  if (v.backgroundAssetId) {
440
467
  try {
441
468
  const bgAsset = ctx.resolveAsset(v.backgroundAssetId);
442
- const np = createImageOrNinePatch(ctx.scene, 0, 0, w, h, bgAsset);
469
+ const np = createImageOrNinePatch(ctx.scene, 0, 0, w, h, bgAsset, v.backgroundFrame);
443
470
  np.setOrigin?.(0.5, 0.5);
444
471
  container.add(np);
445
472
  bgRendered = true;
@@ -996,6 +1023,10 @@ export function applyHudPatch(game, entityId, patch) {
996
1023
  v.backgroundAssetId = p.backgroundAssetId;
997
1024
  else if (p.backgroundAssetId === null)
998
1025
  v.backgroundAssetId = undefined;
1026
+ if (typeof p.backgroundFrame === 'number')
1027
+ v.backgroundFrame = p.backgroundFrame;
1028
+ else if (p.backgroundFrame === null)
1029
+ v.backgroundFrame = undefined;
999
1030
  // Patches that change the rendered visual deeply (label, colour, icon)
1000
1031
  // re-render by destroying + re-spawning the container in place.
1001
1032
  const reg2 = reg;
@@ -73,18 +73,22 @@ export interface AssetRecord {
73
73
  /** Default playback rate for `animations[]`. Defaults to 8. */
74
74
  fps?: number;
75
75
  /**
76
- * For `image`: 9-slice cut-line metadata. When present, HUD widgets that
77
- * render this asset (image / icon-button background / panel background)
78
- * scale via `phaser3-rex-plugins` NinePatch instead of plain stretched
79
- * Image — corners stay fixed, edges + center stretch. Pack-import vision
80
- * captures these on UI button/panel imports; users can edit in the Adjust
81
- * modal at import time.
76
+ * 9-slice cut-line metadata. Two shapes accepted:
82
77
  *
83
- * <p>Single-image only in v1 (slice 7.5). Per-frame 9-slice on uniform-grid
84
- * sheets (e.g. `Square Buttons 26x26.png`) is v2 see
85
- * `unboxy-design/features/visual-editor/07-asset-pack-import.md` §7.9.
78
+ * - **Single image** (slice 7.5) — `NinePatchConfig` directly. Used on
79
+ * `kind: 'image'` assets like a single button/panel background. All four
80
+ * corner widths apply to the image as a whole.
81
+ * - **Per-frame on a uniform grid** (slice 7.6) — `{ perFrame: NinePatchConfig }`.
82
+ * Used on `kind: 'spritesheet'` assets where every cell is its own
83
+ * stretchable button/panel sharing the same corner radius (typical
84
+ * itch.io UI button packs, e.g. `Square Buttons 26x26.png`). The HUD
85
+ * widget references which cell to render via `backgroundFrame`.
86
+ *
87
+ * Per-cell keyed config (`{ perFrame: Record<number, NinePatchConfig> }`)
88
+ * is deferred to v3 per design 07 §7.9.5 — promote only when a real pack
89
+ * needs different corner radii on different cells.
86
90
  */
87
- ninePatch?: NinePatchConfig;
91
+ ninePatch?: NinePatchConfig | NinePatchPerFrame;
88
92
  /**
89
93
  * Vision-detected pixel-art flag. When true, SDK applies
90
94
  * `Phaser.Textures.FilterMode.NEAREST` to the texture after load so it
@@ -101,6 +105,20 @@ export interface NinePatchConfig {
101
105
  topHeight: number;
102
106
  bottomHeight: number;
103
107
  }
108
+ /**
109
+ * Per-frame 9-slice config (slice 7.6). One shared `NinePatchConfig` applied
110
+ * to every frame of a uniform-grid spritesheet. Covers ~95% of itch.io UI
111
+ * button packs, where each cell is a different button color/state but all
112
+ * share the same corner radius.
113
+ */
114
+ export interface NinePatchPerFrame {
115
+ perFrame: NinePatchConfig;
116
+ }
117
+ /**
118
+ * Type guard — true when `ninePatch` is the per-frame variant. Lets call
119
+ * sites that only need to handle one shape stay narrow.
120
+ */
121
+ export declare function isPerFrameNinePatch(np: NinePatchConfig | NinePatchPerFrame | undefined): np is NinePatchPerFrame;
104
122
  export interface SheetAnimation {
105
123
  /** Phaser animation key — used as `sprite.play('<name>')`. */
106
124
  name: string;
@@ -380,8 +398,19 @@ export interface HudIconButtonVisual {
380
398
  * otherwise the image is stretched to the widget's size. When set, the
381
399
  * colored-rect bg below (fillColor / strokeColor / shape) is ignored, and
382
400
  * `pressedFillColor` no-ops (no fill swap on a textured bg in v1).
401
+ *
402
+ * For uniform-grid spritesheet assets with `ninePatch.perFrame` set
403
+ * (slice 7.6), pair this with `backgroundFrame` to pick which cell to
404
+ * render — common case is a button-pack sheet where every cell is a
405
+ * different button color/state.
383
406
  */
384
407
  backgroundAssetId?: string;
408
+ /**
409
+ * For uniform-grid spritesheet backgrounds (slice 7.6): index of the cell
410
+ * to render. Defaults to 0 when omitted. Ignored when the background asset
411
+ * is a single image.
412
+ */
413
+ backgroundFrame?: number;
385
414
  /** Button shape. Default `rounded-rect`. Ignored when backgroundAssetId is set. */
386
415
  shape?: 'rounded-rect' | 'circle';
387
416
  width?: number;
@@ -433,8 +462,18 @@ export interface HudPanelVisual {
433
462
  * otherwise the image is stretched to the widget's size. When set,
434
463
  * `backgroundColor` / `backgroundAlpha` / `borderColor` / `borderWidth` are
435
464
  * ignored — the asset image carries its own pixels.
465
+ *
466
+ * For uniform-grid spritesheet assets with `ninePatch.perFrame` set
467
+ * (slice 7.6), pair this with `backgroundFrame` to pick which cell to
468
+ * render.
436
469
  */
437
470
  backgroundAssetId?: string;
471
+ /**
472
+ * For uniform-grid spritesheet backgrounds (slice 7.6): index of the cell
473
+ * to render. Defaults to 0 when omitted. Ignored when the background asset
474
+ * is a single image.
475
+ */
476
+ backgroundFrame?: number;
438
477
  /** Solid fill colour. Falls back to a transparent panel if omitted. */
439
478
  backgroundColor?: string;
440
479
  /** Background alpha (independent of widget-level alpha). */
@@ -8,3 +8,10 @@
8
8
  * code-rendered visuals will land in later slices.
9
9
  */
10
10
  export const SCHEMA_VERSION = 1;
11
+ /**
12
+ * Type guard — true when `ninePatch` is the per-frame variant. Lets call
13
+ * sites that only need to handle one shape stay narrow.
14
+ */
15
+ export function isPerFrameNinePatch(np) {
16
+ return !!np && 'perFrame' in np;
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboxy/phaser-sdk",
3
- "version": "0.2.37",
3
+ "version": "0.2.38",
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",