@umicat/three-sdk 0.12.0 → 0.13.0

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/Input3D.d.ts CHANGED
@@ -27,9 +27,10 @@ export interface Input3DOptions {
27
27
  /** Where key events are read from. */
28
28
  target?: Window;
29
29
  /**
30
- * On-screen thumbstick + jump button. `'auto'` (the default) adds them when
30
+ * On-screen thumbstick and buttons. `'auto'` (the default) adds them when
31
31
  * the device reports coarse pointers and no fine one — i.e. a phone, not a
32
- * laptop with a touchscreen.
32
+ * laptop with a touchscreen. What the cluster CONTAINS is a separate
33
+ * question — see `jump` and `actions`.
33
34
  */
34
35
  touch?: boolean | 'auto';
35
36
  /**
@@ -52,6 +53,21 @@ export interface Input3DOptions {
52
53
  /** A shape for the JUMP button, in place of its `▲`. Same reasoning as
53
54
  * `Input3DAction.icon`, for the one button the platform owns outright. */
54
55
  jumpIcon?: string;
56
+ /**
57
+ * Whether this game HAS a jump. Default `true`.
58
+ *
59
+ * Jumping is not universal — a game played by walking around a board does
60
+ * not want a jump button, and it was getting one anyway because the button
61
+ * came bundled with the thumbstick under `touch`. Turning `touch` off to be
62
+ * rid of it takes the stick with it, which is not a trade anyone would make.
63
+ *
64
+ * `false` removes the on-screen button AND makes `jump` permanently false,
65
+ * which also hands **`Space` back to the game**. Those two belong together:
66
+ * Space is read here as jump, so a game with no jump that binds Space to
67
+ * something of its own would otherwise have the hero hop every time the
68
+ * player used it.
69
+ */
70
+ jump?: boolean;
55
71
  /**
56
72
  * How the thumbstick behaves on touch.
57
73
  *
@@ -111,6 +127,8 @@ export declare class Input3D {
111
127
  private touchJump;
112
128
  private actions;
113
129
  private jumpIcon?;
130
+ /** Whether this game has a jump at all — see `Input3DOptions.jump`. */
131
+ private readonly hasJump;
114
132
  /** The glyph inside each on-screen button, by action id (`'jump'` for the
115
133
  * platform's own). Kept so an icon can be CHANGED while the game runs —
116
134
  * which is the point of it for anything the player switches: an attack
package/dist/Input3D.js CHANGED
@@ -71,7 +71,7 @@ export class Input3D {
71
71
  this.onDown = (e) => {
72
72
  this.keysDown.add(e.code);
73
73
  if (!e.repeat) {
74
- if (e.code === 'Space')
74
+ if (e.code === 'Space' && this.hasJump)
75
75
  this.beginPress('jump');
76
76
  for (const a of this.actions)
77
77
  if (a.keys?.includes(e.code))
@@ -120,6 +120,7 @@ export class Input3D {
120
120
  this.target.addEventListener('blur', this.onBlur);
121
121
  this.actions = opts.actions ?? [];
122
122
  this.jumpIcon = opts.jumpIcon;
123
+ this.hasJump = opts.jump ?? true;
123
124
  this.stickMode = opts.stick ?? 'floating';
124
125
  this.wantLook = opts.look ?? true;
125
126
  this.topInset = opts.controlsTopInset ?? 'max(64px, 12%)';
@@ -206,6 +207,8 @@ export class Input3D {
206
207
  /** Whether the jump control is held right now. Pass it to the controller —
207
208
  * coyote time and buffering live there, not here. */
208
209
  get jump() {
210
+ if (!this.hasJump)
211
+ return false;
209
212
  return this.enabled && this.held_('jump', this.touchJump || this.isDown('Space'));
210
213
  }
211
214
  /**
@@ -278,7 +281,7 @@ export class Input3D {
278
281
  if (this.keysDown.has(code))
279
282
  return; // held, not re-pressed
280
283
  this.keysDown.add(code);
281
- if (code === 'Space')
284
+ if (code === 'Space' && this.hasJump)
282
285
  this.beginPress('jump');
283
286
  for (const a of this.actions)
284
287
  if (a.keys?.includes(code))
@@ -465,16 +468,29 @@ export class Input3D {
465
468
  return el;
466
469
  };
467
470
  this.glyphs.clear();
468
- const btn = makeButton('▲', this.jumpIcon);
469
- if (glyph)
470
- this.glyphs.set('jump', glyph);
471
- cluster.append(btn);
472
- for (const a of this.actions) {
473
- const el = makeButton(a.label ?? a.id.slice(0, 1).toUpperCase(), a.icon);
471
+ // ONE LIST, and the game decides what is in it.
472
+ //
473
+ // Jump used to be built here on its own, before the loop, which is what
474
+ // made it the one button a game could not decline — it arrived with the
475
+ // thumbstick whether the game had a jump or not. It is an entry in the
476
+ // list now, present unless `jump: false`, and everything after it is
477
+ // whatever the game declared. What this class owns is PLACING them.
478
+ const buttons = [
479
+ ...(this.hasJump ? [{ id: 'jump', label: '▲', icon: this.jumpIcon }] : []),
480
+ ...this.actions.map((a) => ({ id: a.id, label: a.label ?? a.id.slice(0, 1).toUpperCase(), icon: a.icon })),
481
+ ];
482
+ let jumpBtn = null;
483
+ for (const b of buttons) {
484
+ const el = makeButton(b.label, b.icon);
474
485
  if (glyph)
475
- this.glyphs.set(a.id, glyph);
486
+ this.glyphs.set(b.id, glyph);
476
487
  cluster.append(el);
477
- this.wireButton(el, a.id);
488
+ // Jump is the platform's own: its press is read through `get jump()`
489
+ // rather than through the action latch, so it is wired below instead.
490
+ if (b.id === 'jump')
491
+ jumpBtn = el;
492
+ else
493
+ this.wireButton(el, b.id);
478
494
  }
479
495
  root.append(zone, lookZone, pad, cluster);
480
496
  container.appendChild(root);
@@ -613,13 +629,16 @@ export class Input3D {
613
629
  on(grip, ev, (e) => { if (e.pointerId === stickId)
614
630
  reset(); });
615
631
  }
616
- on(btn, 'pointerdown', (e) => {
617
- btn.setPointerCapture(e.pointerId);
618
- this.touchJump = true;
619
- this.beginPress('jump');
620
- });
621
- for (const ev of ['pointerup', 'pointercancel', 'lostpointercapture']) {
622
- on(btn, ev, () => { this.touchJump = false; });
632
+ if (jumpBtn) {
633
+ const jb = jumpBtn;
634
+ on(jb, 'pointerdown', (e) => {
635
+ jb.setPointerCapture(e.pointerId);
636
+ this.touchJump = true;
637
+ this.beginPress('jump');
638
+ });
639
+ for (const ev of ['pointerup', 'pointercancel', 'lostpointercapture']) {
640
+ on(jb, ev, () => { this.touchJump = false; });
641
+ }
623
642
  }
624
643
  }
625
644
  wireButton(el, id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umicat/three-sdk",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Three.js runtime for Umicat games: the scene3d design format, its loader with physics, a kinematic character controller, and the Umicat platform via @umicat/platform-sdk.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",