@unboxy/phaser-sdk 0.2.33 → 0.2.34

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.
@@ -140,7 +140,27 @@ function createText(ctx, entity, pos) {
140
140
  return text;
141
141
  }
142
142
  function createImage(ctx, entity, pos) {
143
- const asset = ctx.resolveAsset(entity.visual.assetId);
143
+ let asset;
144
+ try {
145
+ asset = ctx.resolveAsset(entity.visual.assetId);
146
+ }
147
+ catch {
148
+ // Soft-fail when the assetId isn't in the manifest. Mirrors the world
149
+ // scene's createSprite missing-asset path. Wraps a Graphics in a
150
+ // Container so the editor's bounds-based hit-test has a sized target.
151
+ const w = entity.visual.width ?? 64;
152
+ const h = entity.visual.height ?? 64;
153
+ const g = ctx.scene.add.graphics();
154
+ g.fillStyle(0x4d2244, 0.4);
155
+ g.fillRect(-w / 2, -h / 2, w, h);
156
+ g.lineStyle(2, 0xff00ff, 0.9);
157
+ g.strokeRect(-w / 2, -h / 2, w, h);
158
+ g.lineBetween(-w / 2, -h / 2, w / 2, h / 2);
159
+ g.lineBetween(w / 2, -h / 2, -w / 2, h / 2);
160
+ const container = ctx.scene.add.container(pos.x, pos.y, [g]);
161
+ container.setSize(w, h);
162
+ return container;
163
+ }
144
164
  const image = entity.visual.frame !== undefined
145
165
  ? ctx.scene.add.image(pos.x, pos.y, asset.textureKey, entity.visual.frame)
146
166
  : ctx.scene.add.image(pos.x, pos.y, asset.textureKey);
@@ -478,7 +498,12 @@ export function preloadHudAssets(scene, hudScene, manifest) {
478
498
  for (const id of ids) {
479
499
  const asset = manifest.assets?.find((a) => a.id === id);
480
500
  if (!asset) {
481
- throw new Error(`[unboxy/hud] HUD scene '${hudScene.id}' references assetId '${id}' but the manifest has no such asset`);
501
+ // Soft-fail: warn + continue. createImage / createIconButton render
502
+ // a placeholder when the asset is unresolvable. Mirrors the world
503
+ // scene's preloadSceneAssets soft-fail (SceneLoader.ts).
504
+ // eslint-disable-next-line no-console
505
+ console.warn(`[unboxy/hud] HUD scene '${hudScene.id}' references assetId '${id}' but the manifest has no such asset; widget will render as a placeholder`);
506
+ continue;
482
507
  }
483
508
  if (scene.textures.exists(asset.textureKey))
484
509
  continue;
@@ -84,7 +84,15 @@ export function preloadSceneAssets(scene, sceneFile, manifest) {
84
84
  continue;
85
85
  const asset = state.assetsById.get(id);
86
86
  if (!asset) {
87
- throw new Error(`[unboxy/scene] scene '${sceneFile.id}' references assetId '${id}' but the manifest has no such asset`);
87
+ // Soft-fail: warn, continue. The downstream spawn path renders a
88
+ // magenta "?" placeholder for the offending entity so the user sees
89
+ // *where* a missing asset would have been, without losing every
90
+ // other entity in the scene. Common cause is the editor adding a
91
+ // sprite that references an asset whose manifest entry never got
92
+ // synced (e.g. AssetShelf drag without manifest update).
93
+ // eslint-disable-next-line no-console
94
+ console.warn(`[unboxy/scene] scene '${sceneFile.id}' references assetId '${id}' but the manifest has no such asset; entity will render as a placeholder`);
95
+ continue;
88
96
  }
89
97
  queueAssetLoad(scene, asset);
90
98
  state.requestedAssetIds.add(id);
@@ -36,7 +36,18 @@ export function spawnEntity(ctx, entity) {
36
36
  }
37
37
  function createSprite(ctx, entity) {
38
38
  const v = entity.visual;
39
- const asset = ctx.resolveAsset(v.assetId);
39
+ let asset;
40
+ try {
41
+ asset = ctx.resolveAsset(v.assetId);
42
+ }
43
+ catch (e) {
44
+ // Soft-fail when the assetId isn't in the manifest. SceneLoader's
45
+ // preloadSceneAssets logged the warning; here we render a clear
46
+ // magenta-bordered "?" placeholder so the entity is still visible
47
+ // and selectable in the editor. Same pattern as createCodeRendered's
48
+ // missing-render-script path.
49
+ return missingAssetPlaceholder(ctx, v.assetId);
50
+ }
40
51
  // Phaser's add.sprite handles both plain images and atlas/spritesheet
41
52
  // textures uniformly when given (key, frame). For plain images, frame
42
53
  // is undefined and Phaser uses the default frame.
@@ -51,6 +62,29 @@ function createSprite(ctx, entity) {
51
62
  sprite.setFlipY(true);
52
63
  return sprite;
53
64
  }
65
+ /**
66
+ * Magenta "?" placeholder for an entity whose assetId isn't in the manifest.
67
+ * Drawn as a Graphics so the editor's hit-test (which uses getBounds) lands
68
+ * on the visible square. Mirrors createCodeRendered's missing-script path.
69
+ */
70
+ function missingAssetPlaceholder(ctx, assetId) {
71
+ const w = 64;
72
+ const h = 64;
73
+ const g = ctx.scene.add.graphics();
74
+ g.fillStyle(0x4d2244, 0.4);
75
+ g.fillRect(-w / 2, -h / 2, w, h);
76
+ g.lineStyle(2, 0xff00ff, 0.9);
77
+ g.strokeRect(-w / 2, -h / 2, w, h);
78
+ // Diagonal "no entry" lines + a "?" text so it's obvious from a glance.
79
+ g.lineBetween(-w / 2, -h / 2, w / 2, h / 2);
80
+ g.lineBetween(w / 2, -h / 2, -w / 2, h / 2);
81
+ // Phaser Graphics has no intrinsic size — sizeForHitTest sets bounds so
82
+ // the editor's hit-test (which uses getBounds) can find this entity.
83
+ sizeForHitTest(g, w, h);
84
+ g.setDataEnabled();
85
+ g.setData('unboxyMissingAssetId', assetId);
86
+ return g;
87
+ }
54
88
  function createPrimitive(ctx, entity) {
55
89
  const v = entity.visual;
56
90
  if (v.kind === 'rect')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboxy/phaser-sdk",
3
- "version": "0.2.33",
3
+ "version": "0.2.34",
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",