@unboxy/phaser-sdk 0.2.22 → 0.2.23

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.
@@ -209,15 +209,15 @@ function hitTest(game, worldX, worldY) {
209
209
  let topmost = null;
210
210
  let topmostDepth = -Infinity;
211
211
  for (const go of registry.all()) {
212
- const withBounds = go;
213
- if (typeof withBounds.getBounds !== 'function')
212
+ const r = entityHitRect(go);
213
+ if (!r)
214
214
  continue;
215
- const r = withBounds.getBounds();
216
215
  if (worldX < r.x || worldX > r.x + r.width)
217
216
  continue;
218
217
  if (worldY < r.y || worldY > r.y + r.height)
219
218
  continue;
220
- const depth = typeof withBounds.depth === 'number' ? withBounds.depth : 0;
219
+ const withDepth = go;
220
+ const depth = typeof withDepth.depth === 'number' ? withDepth.depth : 0;
221
221
  if (depth >= topmostDepth) {
222
222
  topmostDepth = depth;
223
223
  topmost = go;
@@ -225,6 +225,37 @@ function hitTest(game, worldX, worldY) {
225
225
  }
226
226
  return topmost;
227
227
  }
228
+ /**
229
+ * Compute a hit-test rectangle for a spawned entity in world coords.
230
+ *
231
+ * 1. If the entity has `editorHitWidth` / `editorHitHeight` set in data
232
+ * (code-rendered entities — Graphics has no intrinsic bounds), use
233
+ * those centered on the entity's x/y.
234
+ * 2. Otherwise fall back to Phaser's `getBounds()` (works for Sprite,
235
+ * Rectangle, Arc, Container — anything with a Size component).
236
+ *
237
+ * Returns null if neither path yields a usable rect.
238
+ */
239
+ function entityHitRect(go) {
240
+ const hitW = go.getData('editorHitWidth');
241
+ const hitH = go.getData('editorHitHeight');
242
+ if (typeof hitW === 'number' && typeof hitH === 'number') {
243
+ const positioned = go;
244
+ return {
245
+ x: positioned.x - hitW / 2,
246
+ y: positioned.y - hitH / 2,
247
+ width: hitW,
248
+ height: hitH,
249
+ };
250
+ }
251
+ const withBounds = go;
252
+ if (typeof withBounds.getBounds !== 'function')
253
+ return null;
254
+ const r = withBounds.getBounds();
255
+ if (r.width === 0 && r.height === 0)
256
+ return null;
257
+ return { x: r.x, y: r.y, width: r.width, height: r.height };
258
+ }
228
259
  function findRegistry(game) {
229
260
  for (const scene of game.scene.getScenes(false)) {
230
261
  const reg = getEntityRegistry(scene);
@@ -190,10 +190,24 @@ export class EditorOverlayScene extends Phaser.Scene {
190
190
  }
191
191
  }
192
192
  function computeBounds(go) {
193
- // Most game objects implement getBounds; Container does too.
193
+ // Code-rendered entities stash hit dimensions on data because Phaser
194
+ // Graphics has no intrinsic bounds. Use those when present.
195
+ const hitW = go.getData('editorHitWidth');
196
+ const hitH = go.getData('editorHitHeight');
197
+ if (typeof hitW === 'number' && typeof hitH === 'number') {
198
+ const positioned = go;
199
+ return {
200
+ x: positioned.x - hitW / 2,
201
+ y: positioned.y - hitH / 2,
202
+ width: hitW,
203
+ height: hitH,
204
+ };
205
+ }
194
206
  const withBounds = go;
195
207
  if (typeof withBounds.getBounds !== 'function')
196
208
  return null;
197
209
  const r = withBounds.getBounds();
210
+ if (r.width === 0 && r.height === 0)
211
+ return null;
198
212
  return { x: r.x, y: r.y, width: r.width, height: r.height };
199
213
  }
@@ -94,6 +94,8 @@ function createGroup(ctx, entity) {
94
94
  return container;
95
95
  }
96
96
  function createCodeRendered(ctx, entity) {
97
+ const w = entity.visual.width ?? 64;
98
+ const h = entity.visual.height ?? 64;
97
99
  const render = ctx.resolveRenderScript?.(entity.visual.script);
98
100
  if (!render) {
99
101
  // Slice 3.5: render scripts have a registry now, but a missing entry
@@ -102,21 +104,35 @@ function createCodeRendered(ctx, entity) {
102
104
  // the user sees the entity exists but the visual is unresolved.
103
105
  const ph = ctx.scene.add.graphics();
104
106
  ph.fillStyle(0x666666, 0.4);
105
- ph.fillRect(-16, -16, 32, 32);
107
+ ph.fillRect(-w / 2, -h / 2, w, h);
106
108
  ph.lineStyle(2, 0xff8800, 0.9);
107
- ph.strokeRect(-16, -16, 32, 32);
108
- // Annotate so editor / debug overlays can identify.
109
+ ph.strokeRect(-w / 2, -h / 2, w, h);
110
+ sizeForHitTest(ph, w, h);
109
111
  ph.setData('renderScriptMissing', entity.visual.script);
110
112
  return ph;
111
113
  }
112
114
  const g = ctx.scene.add.graphics();
113
115
  const params = entity.visual.params ?? {};
114
116
  render(g, params);
117
+ sizeForHitTest(g, w, h);
115
118
  // Stash data needed for live re-render from EditorBridge.applyEdit.
116
119
  g.setData('renderScriptPath', entity.visual.script);
117
120
  g.setData('renderScriptParams', params);
118
121
  return g;
119
122
  }
123
+ /**
124
+ * Phaser Graphics doesn't track its drawn area — `getBounds()` returns
125
+ * a 0×0 rect because the Graphics class doesn't include the Size/Origin
126
+ * mixins. Rather than fight Phaser's class hierarchy with method casts,
127
+ * stash the editor hit area on the GameObject's data manager. The editor
128
+ * overlay reads these before falling back to `getBounds()`. The implicit
129
+ * convention: hit area is centered on the GameObject's x/y, matching how
130
+ * render scripts conventionally draw around (0, 0).
131
+ */
132
+ function sizeForHitTest(g, width, height) {
133
+ g.setData('editorHitWidth', width);
134
+ g.setData('editorHitHeight', height);
135
+ }
120
136
  function applyTransform(go, t) {
121
137
  // Most game objects implement Transform; Container does too. Cast through
122
138
  // a permissive shape so this works for sprite/rect/circle/container alike.
@@ -116,6 +116,14 @@ export interface CodeRenderedVisual {
116
116
  /** Path to render script, e.g. `"src/visuals/boss-renderer.ts"`. */
117
117
  script: string;
118
118
  params?: Record<string, unknown>;
119
+ /**
120
+ * Optional bounds used for editor hit-testing (click + drag) and the
121
+ * selection rectangle. Phaser Graphics has no intrinsic size, so we set
122
+ * it explicitly. Defaults to 64×64 centered on the entity. The agent can
123
+ * override per-entity when the script draws something larger or smaller.
124
+ */
125
+ width?: number;
126
+ height?: number;
119
127
  }
120
128
  export type WorldVisual = SpriteVisual | PrimitiveVisual | CodeRenderedVisual;
121
129
  export type WorldEntityKind = 'sprite' | 'primitive' | 'code-rendered' | 'group' | 'tilemap' | 'trigger';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboxy/phaser-sdk",
3
- "version": "0.2.22",
3
+ "version": "0.2.23",
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",