@umicat/three-sdk 0.8.2 → 0.8.4

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
@@ -55,6 +55,17 @@ export interface Input3DOptions {
55
55
  /** Radians of camera rotation per screen-width dragged. Same number governs
56
56
  * touch drags and desktop right-drags, so there is one knob to turn. */
57
57
  lookSensitivity?: number;
58
+ /**
59
+ * How much of the top of the screen the move and look zones leave alone.
60
+ *
61
+ * A HUD lives at the top — score, hearts, a mute button — and the zones are
62
+ * full-screen and sit ABOVE the game's DOM, so without this every button up
63
+ * there is unreachable on a phone while looking perfectly fine. Nobody
64
+ * drives a thumbstick from the top edge, so the trade is free.
65
+ *
66
+ * A CSS length; the default is `max(64px, 12%)`.
67
+ */
68
+ controlsTopInset?: string;
58
69
  }
59
70
  /**
60
71
  * Movement and jump, from a keyboard or a thumb, behind one interface.
@@ -82,8 +93,10 @@ export declare class Input3D {
82
93
  private actions;
83
94
  private stickMode;
84
95
  private wantLook;
96
+ private topInset;
85
97
  private lookSens;
86
98
  private readonly lookDelta;
99
+ private enabled;
87
100
  private readonly touchHeld;
88
101
  private readonly pressedAt;
89
102
  private readonly unconsumed;
@@ -91,6 +104,21 @@ export declare class Input3D {
91
104
  private readonly cleanups;
92
105
  constructor(options?: Input3DOptions | Window);
93
106
  isDown(...codes: string[]): boolean;
107
+ /**
108
+ * Stop accepting input, and take the on-screen controls off the screen.
109
+ *
110
+ * For the moment a game puts a modal up — a game-over screen, a shop, a
111
+ * dialogue box. Two things go wrong otherwise, and the second is worse: the
112
+ * character keeps walking behind the dialog, and the controls' full-screen
113
+ * layer sits ABOVE the game's own DOM at z-index 10, so the button in that
114
+ * modal cannot be tapped at all. "Play Again" that does nothing is this bug.
115
+ *
116
+ * Disabling clears what was held, so a thumb that was pushing the stick when
117
+ * the modal opened does not resume walking when it closes.
118
+ */
119
+ setEnabled(on: boolean): void;
120
+ /** Whether input is currently being accepted. */
121
+ get isEnabled(): boolean;
94
122
  /**
95
123
  * Which way to walk. Keyboard and stick are merged, so both work on a device
96
124
  * with both.
package/dist/Input3D.js CHANGED
@@ -69,8 +69,10 @@ export class Input3D {
69
69
  this.actions = [];
70
70
  this.stickMode = 'floating';
71
71
  this.wantLook = true;
72
+ this.topInset = 'max(64px, 12%)';
72
73
  this.lookSens = 4.6;
73
74
  this.lookDelta = { x: 0, y: 0 };
75
+ this.enabled = true;
74
76
  this.touchHeld = new Set();
75
77
  this.pressedAt = new Map();
76
78
  this.unconsumed = new Set();
@@ -87,6 +89,7 @@ export class Input3D {
87
89
  this.actions = opts.actions ?? [];
88
90
  this.stickMode = opts.stick ?? 'floating';
89
91
  this.wantLook = opts.look ?? true;
92
+ this.topInset = opts.controlsTopInset ?? 'max(64px, 12%)';
90
93
  this.lookSens = opts.lookSensitivity ?? 4.6;
91
94
  if (this.wantLook && typeof document !== 'undefined') {
92
95
  this.mountMouseLook(opts.container?.ownerDocument ?? document);
@@ -96,7 +99,43 @@ export class Input3D {
96
99
  this.mountTouch(opts.container ?? document.body);
97
100
  }
98
101
  }
99
- isDown(...codes) { return codes.some((c) => this.keysDown.has(c)); }
102
+ isDown(...codes) {
103
+ return this.enabled && codes.some((c) => this.keysDown.has(c));
104
+ }
105
+ /**
106
+ * Stop accepting input, and take the on-screen controls off the screen.
107
+ *
108
+ * For the moment a game puts a modal up — a game-over screen, a shop, a
109
+ * dialogue box. Two things go wrong otherwise, and the second is worse: the
110
+ * character keeps walking behind the dialog, and the controls' full-screen
111
+ * layer sits ABOVE the game's own DOM at z-index 10, so the button in that
112
+ * modal cannot be tapped at all. "Play Again" that does nothing is this bug.
113
+ *
114
+ * Disabling clears what was held, so a thumb that was pushing the stick when
115
+ * the modal opened does not resume walking when it closes.
116
+ */
117
+ setEnabled(on) {
118
+ this.enabled = on;
119
+ if (!on) {
120
+ this.keysDown.clear();
121
+ this.touchHeld.clear();
122
+ this.pressedAt.clear();
123
+ this.unconsumed.clear();
124
+ this.stick.x = 0;
125
+ this.stick.z = 0;
126
+ this.touchJump = false;
127
+ this.lookDelta.x = 0;
128
+ this.lookDelta.y = 0;
129
+ }
130
+ if (this.root) {
131
+ this.root.style.display = on ? '' : 'none';
132
+ // display:none alone would be enough for hit-testing, but a game that
133
+ // fades its modal in wants the controls gone, not just inert.
134
+ this.root.style.pointerEvents = 'none';
135
+ }
136
+ }
137
+ /** Whether input is currently being accepted. */
138
+ get isEnabled() { return this.enabled; }
100
139
  /**
101
140
  * Which way to walk. Keyboard and stick are merged, so both work on a device
102
141
  * with both.
@@ -109,6 +148,8 @@ export class Input3D {
109
148
  * behaviour, which is correct for a camera that never moves.
110
149
  */
111
150
  direction(cameraYaw = 0) {
151
+ if (!this.enabled)
152
+ return { x: 0, z: 0 };
112
153
  let x = this.stick.x, z = this.stick.z;
113
154
  if (this.isDown('KeyW', 'ArrowUp'))
114
155
  z -= 1;
@@ -131,9 +172,13 @@ export class Input3D {
131
172
  }
132
173
  /** Whether the jump control is held right now. Pass it to the controller —
133
174
  * coyote time and buffering live there, not here. */
134
- get jump() { return this.held_('jump', this.touchJump || this.isDown('Space')); }
175
+ get jump() {
176
+ return this.enabled && this.held_('jump', this.touchJump || this.isDown('Space'));
177
+ }
135
178
  /** Whether an action is held right now — button or bound key, same answer. */
136
179
  held(id) {
180
+ if (!this.enabled)
181
+ return false;
137
182
  const a = this.actions.find((x) => x.id === id);
138
183
  return this.held_(id, this.touchHeld.has(id) || (a?.keys ? this.isDown(...a.keys) : false));
139
184
  }
@@ -146,6 +191,8 @@ export class Input3D {
146
191
  * it twice must not apply it twice. Pass straight to `LoadedScene3D.orbit`.
147
192
  */
148
193
  look() {
194
+ if (!this.enabled)
195
+ return { x: 0, y: 0 };
149
196
  const out = { x: this.lookDelta.x, y: this.lookDelta.y };
150
197
  this.lookDelta.x = 0;
151
198
  this.lookDelta.y = 0;
@@ -157,7 +204,7 @@ export class Input3D {
157
204
  * to last frame's, which means a press that begins and ends between two
158
205
  * frames never happened. This latches at the event, so it cannot be missed
159
206
  * — and it clears on read, so holding the button does not chain. */
160
- consume(id) { return this.unconsumed.delete(id); }
207
+ consume(id) { return this.enabled && this.unconsumed.delete(id); }
161
208
  held_(id, physical) {
162
209
  if (physical)
163
210
  return true;
@@ -269,7 +316,7 @@ export class Input3D {
269
316
  // anything. It deliberately stops short of the buttons on the right.
270
317
  const zone = document.createElement('div');
271
318
  Object.assign(zone.style, {
272
- position: 'absolute', left: '0', top: '0', width: '50%', height: '100%',
319
+ position: 'absolute', left: '0', top: this.topInset, width: '50%', bottom: '0',
273
320
  pointerEvents: floating ? 'auto' : 'none',
274
321
  });
275
322
  Object.assign(zone.style, NO_SELECTION);
@@ -278,7 +325,7 @@ export class Input3D {
278
325
  // can be the whole right half rather than an awkward cut-out around them.
279
326
  const lookZone = document.createElement('div');
280
327
  Object.assign(lookZone.style, {
281
- position: 'absolute', right: '0', top: '0', width: '50%', height: '100%',
328
+ position: 'absolute', right: '0', top: this.topInset, width: '50%', bottom: '0',
282
329
  pointerEvents: this.wantLook ? 'auto' : 'none',
283
330
  });
284
331
  Object.assign(lookZone.style, NO_SELECTION);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umicat/three-sdk",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
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",