@zylem/game-lib 0.6.3 → 0.6.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/actions.d.ts +5 -5
- package/dist/actions.js +196 -32
- package/dist/actions.js.map +1 -1
- package/dist/behavior/jumper-2d.d.ts +114 -0
- package/dist/behavior/jumper-2d.js +711 -0
- package/dist/behavior/jumper-2d.js.map +1 -0
- package/dist/behavior/platformer-3d.d.ts +14 -14
- package/dist/behavior/platformer-3d.js +347 -104
- package/dist/behavior/platformer-3d.js.map +1 -1
- package/dist/behavior/ricochet-2d.d.ts +4 -3
- package/dist/behavior/ricochet-2d.js +53 -22
- package/dist/behavior/ricochet-2d.js.map +1 -1
- package/dist/behavior/ricochet-3d.d.ts +117 -0
- package/dist/behavior/ricochet-3d.js +443 -0
- package/dist/behavior/ricochet-3d.js.map +1 -0
- package/dist/behavior/screen-visibility.d.ts +79 -0
- package/dist/behavior/screen-visibility.js +358 -0
- package/dist/behavior/screen-visibility.js.map +1 -0
- package/dist/behavior/screen-wrap.d.ts +4 -3
- package/dist/behavior/screen-wrap.js +100 -49
- package/dist/behavior/screen-wrap.js.map +1 -1
- package/dist/behavior/shooter-2d.d.ts +79 -0
- package/dist/behavior/shooter-2d.js +180 -0
- package/dist/behavior/shooter-2d.js.map +1 -0
- package/dist/behavior/thruster.d.ts +5 -4
- package/dist/behavior/thruster.js +133 -75
- package/dist/behavior/thruster.js.map +1 -1
- package/dist/behavior/top-down-movement.d.ts +56 -0
- package/dist/behavior/top-down-movement.js +125 -0
- package/dist/behavior/top-down-movement.js.map +1 -0
- package/dist/behavior/world-boundary-2d.d.ts +4 -3
- package/dist/behavior/world-boundary-2d.js +90 -36
- package/dist/behavior/world-boundary-2d.js.map +1 -1
- package/dist/behavior/world-boundary-3d.d.ts +76 -0
- package/dist/behavior/world-boundary-3d.js +274 -0
- package/dist/behavior/world-boundary-3d.js.map +1 -0
- package/dist/{behavior-descriptor-BWNWmIjv.d.ts → behavior-descriptor-BXnVR8Ki.d.ts} +22 -5
- package/dist/{blueprints-BWGz8fII.d.ts → blueprints-DmbK2dki.d.ts} +2 -2
- package/dist/camera-4XO5gbQH.d.ts +905 -0
- package/dist/camera.d.ts +1 -1
- package/dist/camera.js +876 -289
- package/dist/camera.js.map +1 -1
- package/dist/{composition-DrzFrbqI.d.ts → composition-BASvMKrW.d.ts} +1 -1
- package/dist/{core-DAkskq6Y.d.ts → core-CARRaS55.d.ts} +57 -14
- package/dist/core.d.ts +9 -8
- package/dist/core.js +4519 -1255
- package/dist/core.js.map +1 -1
- package/dist/{entities-DC9ce_vx.d.ts → entities-ChFirVL9.d.ts} +22 -28
- package/dist/entities.d.ts +4 -4
- package/dist/entities.js +1231 -314
- package/dist/entities.js.map +1 -1
- package/dist/{entity-BpbZqg19.d.ts → entity-vj-HTjzU.d.ts} +80 -11
- package/dist/{global-change-Dc8uCKi2.d.ts → global-change-2JvMaz44.d.ts} +1 -1
- package/dist/main.d.ts +718 -19
- package/dist/main.js +12129 -5959
- package/dist/main.js.map +1 -1
- package/dist/physics-pose-DCc4oE44.d.ts +25 -0
- package/dist/physics-protocol-BDD3P5W2.d.ts +200 -0
- package/dist/physics-worker.d.ts +21 -0
- package/dist/physics-worker.js +306 -0
- package/dist/physics-worker.js.map +1 -0
- package/dist/physics.d.ts +205 -0
- package/dist/physics.js +577 -0
- package/dist/physics.js.map +1 -0
- package/dist/{stage-types-BFsm3qsZ.d.ts → stage-types-C19IhuzA.d.ts} +253 -89
- package/dist/stage.d.ts +9 -8
- package/dist/stage.js +3782 -1041
- package/dist/stage.js.map +1 -1
- package/dist/sync-state-machine-CZyspBpj.d.ts +16 -0
- package/dist/{thruster-DhRaJnoL.d.ts → thruster-23lzoPZd.d.ts} +16 -8
- package/dist/world-DfgxoNMt.d.ts +105 -0
- package/package.json +25 -1
- package/dist/camera-B5e4c78l.d.ts +0 -468
- package/dist/world-Be5m1XC1.d.ts +0 -31
|
@@ -10,8 +10,9 @@ function createPlatformer3DMovementComponent(options = {}) {
|
|
|
10
10
|
coyoteTime: options.coyoteTime ?? 0.1,
|
|
11
11
|
jumpBufferTime: options.jumpBufferTime ?? 0.1,
|
|
12
12
|
jumpCutMultiplier: options.jumpCutMultiplier ?? 0.5,
|
|
13
|
-
multiJumpWindowTime: options.multiJumpWindowTime ?? 0.15
|
|
13
|
+
multiJumpWindowTime: options.multiJumpWindowTime ?? 0.15,
|
|
14
14
|
// 150ms default
|
|
15
|
+
debugGroundProbe: options.debugGroundProbe ?? false
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
18
|
function createPlatformer3DInputComponent() {
|
|
@@ -44,8 +45,46 @@ function createPlatformer3DStateComponent() {
|
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
// src/lib/core/utility/sync-state-machine.ts
|
|
49
|
+
import {
|
|
50
|
+
StateMachine as BaseStateMachine
|
|
51
|
+
} from "typescript-fsm";
|
|
52
|
+
import { t } from "typescript-fsm";
|
|
53
|
+
var SyncStateMachine = class extends BaseStateMachine {
|
|
54
|
+
constructor(init, transitions = [], logger = console) {
|
|
55
|
+
super(init, transitions, logger);
|
|
56
|
+
}
|
|
57
|
+
dispatch(_event, ..._args) {
|
|
58
|
+
throw new Error("SyncStateMachine does not support async dispatch.");
|
|
59
|
+
}
|
|
60
|
+
syncDispatch(event, ...args) {
|
|
61
|
+
const found = this.transitions.some((transition) => {
|
|
62
|
+
if (transition.fromState !== this._current || transition.event !== event) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const current = this._current;
|
|
66
|
+
this._current = transition.toState;
|
|
67
|
+
if (!transition.cb) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
transition.cb(...args);
|
|
72
|
+
return true;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
this._current = current;
|
|
75
|
+
this.logger.error("Exception in callback", error);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
if (!found) {
|
|
80
|
+
const errorMessage = this.formatErr(this._current, event);
|
|
81
|
+
this.logger.error(errorMessage);
|
|
82
|
+
}
|
|
83
|
+
return found;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
47
87
|
// src/lib/behaviors/platformer-3d/platformer-3d-fsm.ts
|
|
48
|
-
import { StateMachine, t } from "typescript-fsm";
|
|
49
88
|
var Platformer3DState = /* @__PURE__ */ ((Platformer3DState2) => {
|
|
50
89
|
Platformer3DState2["Idle"] = "idle";
|
|
51
90
|
Platformer3DState2["Walking"] = "walking";
|
|
@@ -67,7 +106,7 @@ var Platformer3DEvent = /* @__PURE__ */ ((Platformer3DEvent2) => {
|
|
|
67
106
|
var Platformer3DFSM = class {
|
|
68
107
|
constructor(ctx) {
|
|
69
108
|
this.ctx = ctx;
|
|
70
|
-
this.machine = new
|
|
109
|
+
this.machine = new SyncStateMachine(
|
|
71
110
|
"idle" /* Idle */,
|
|
72
111
|
[
|
|
73
112
|
// Idle transitions
|
|
@@ -113,7 +152,7 @@ var Platformer3DFSM = class {
|
|
|
113
152
|
*/
|
|
114
153
|
dispatch(event) {
|
|
115
154
|
if (this.machine.can(event)) {
|
|
116
|
-
this.machine.
|
|
155
|
+
this.machine.syncDispatch(event);
|
|
117
156
|
}
|
|
118
157
|
}
|
|
119
158
|
/**
|
|
@@ -171,97 +210,294 @@ var Platformer3DFSM = class {
|
|
|
171
210
|
}
|
|
172
211
|
};
|
|
173
212
|
|
|
174
|
-
// src/lib/behaviors/
|
|
175
|
-
import { Vector3, BufferGeometry, LineBasicMaterial, Line } from "three";
|
|
213
|
+
// src/lib/behaviors/shared/ground-probe-3d.ts
|
|
176
214
|
import { Ray } from "@dimforge/rapier3d-compat";
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
215
|
+
import { BufferGeometry, Line, LineBasicMaterial, Vector3 } from "three";
|
|
216
|
+
|
|
217
|
+
// src/lib/physics/serialize-descriptors.ts
|
|
218
|
+
import { RigidBodyType } from "@dimforge/rapier3d-compat";
|
|
219
|
+
function serializeColliderDesc(desc) {
|
|
220
|
+
const internal = desc;
|
|
221
|
+
const customShapeData = internal.__zylemShapeData;
|
|
222
|
+
if (customShapeData?.shape === "trimesh") {
|
|
223
|
+
const result2 = {
|
|
224
|
+
shape: "trimesh",
|
|
225
|
+
dimensions: [],
|
|
226
|
+
vertices: [...customShapeData.vertices],
|
|
227
|
+
indices: [...customShapeData.indices]
|
|
228
|
+
};
|
|
229
|
+
const translation = internal.translation;
|
|
230
|
+
if (translation && (translation.x !== 0 || translation.y !== 0 || translation.z !== 0)) {
|
|
231
|
+
result2.translation = [translation.x, translation.y, translation.z];
|
|
232
|
+
}
|
|
233
|
+
if (internal.isSensor) {
|
|
234
|
+
result2.sensor = true;
|
|
235
|
+
}
|
|
236
|
+
if (internal.collisionGroups !== void 0 && internal.collisionGroups !== 4294967295) {
|
|
237
|
+
result2.collisionGroups = internal.collisionGroups;
|
|
238
|
+
}
|
|
239
|
+
if (internal.activeCollisionTypes !== void 0) {
|
|
240
|
+
result2.activeCollisionTypes = internal.activeCollisionTypes;
|
|
241
|
+
}
|
|
242
|
+
return result2;
|
|
243
|
+
}
|
|
244
|
+
const shapeType = internal.shape?.type ?? internal.shapeType ?? 0;
|
|
245
|
+
const { shape, dimensions, heightfieldMeta } = extractShapeData(shapeType, internal);
|
|
246
|
+
const result = {
|
|
247
|
+
shape,
|
|
248
|
+
dimensions
|
|
249
|
+
};
|
|
250
|
+
const t2 = internal.translation;
|
|
251
|
+
if (t2 && (t2.x !== 0 || t2.y !== 0 || t2.z !== 0)) {
|
|
252
|
+
result.translation = [t2.x, t2.y, t2.z];
|
|
253
|
+
}
|
|
254
|
+
if (internal.isSensor) {
|
|
255
|
+
result.sensor = true;
|
|
256
|
+
}
|
|
257
|
+
if (internal.collisionGroups !== void 0 && internal.collisionGroups !== 4294967295) {
|
|
258
|
+
result.collisionGroups = internal.collisionGroups;
|
|
259
|
+
}
|
|
260
|
+
if (internal.activeCollisionTypes !== void 0) {
|
|
261
|
+
result.activeCollisionTypes = internal.activeCollisionTypes;
|
|
262
|
+
}
|
|
263
|
+
if (heightfieldMeta) {
|
|
264
|
+
result.heightfieldMeta = heightfieldMeta;
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
function extractShapeData(shapeType, internal) {
|
|
269
|
+
switch (shapeType) {
|
|
270
|
+
case 0:
|
|
271
|
+
return {
|
|
272
|
+
shape: "ball",
|
|
273
|
+
dimensions: [internal.shape?.radius ?? internal.halfExtents?.x ?? 1]
|
|
274
|
+
};
|
|
275
|
+
case 1:
|
|
276
|
+
return {
|
|
277
|
+
shape: "cuboid",
|
|
278
|
+
dimensions: [
|
|
279
|
+
internal.shape?.halfExtents?.x ?? internal.halfExtents?.x ?? 0.5,
|
|
280
|
+
internal.shape?.halfExtents?.y ?? internal.halfExtents?.y ?? 0.5,
|
|
281
|
+
internal.shape?.halfExtents?.z ?? internal.halfExtents?.z ?? 0.5
|
|
282
|
+
]
|
|
283
|
+
};
|
|
284
|
+
case 2:
|
|
285
|
+
return {
|
|
286
|
+
shape: "capsule",
|
|
287
|
+
dimensions: [
|
|
288
|
+
internal.shape?.halfHeight ?? 0.5,
|
|
289
|
+
internal.shape?.radius ?? 0.5
|
|
290
|
+
]
|
|
291
|
+
};
|
|
292
|
+
case 6:
|
|
293
|
+
return {
|
|
294
|
+
shape: "cone",
|
|
295
|
+
dimensions: [
|
|
296
|
+
internal.shape?.halfHeight ?? 1,
|
|
297
|
+
internal.shape?.radius ?? 1
|
|
298
|
+
]
|
|
299
|
+
};
|
|
300
|
+
case 7:
|
|
301
|
+
return {
|
|
302
|
+
shape: "cylinder",
|
|
303
|
+
dimensions: [
|
|
304
|
+
internal.shape?.halfHeight ?? 1,
|
|
305
|
+
internal.shape?.radius ?? 1
|
|
306
|
+
]
|
|
307
|
+
};
|
|
308
|
+
case 11: {
|
|
309
|
+
const nrows = internal.shape?.nrows ?? 10;
|
|
310
|
+
const ncols = internal.shape?.ncols ?? 10;
|
|
311
|
+
const heights = internal.shape?.heights;
|
|
312
|
+
return {
|
|
313
|
+
shape: "heightfield",
|
|
314
|
+
dimensions: heights ? Array.from(heights) : [],
|
|
315
|
+
heightfieldMeta: { nrows, ncols }
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
default:
|
|
319
|
+
return { shape: "cuboid", dimensions: [0.5, 0.5, 0.5] };
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/lib/behaviors/shared/ground-probe-3d.ts
|
|
324
|
+
var DEFAULT_OFFSETS = [
|
|
325
|
+
{ x: 0, z: 0 },
|
|
326
|
+
{ x: 0.4, z: 0.4 },
|
|
327
|
+
{ x: -0.4, z: 0.4 },
|
|
328
|
+
{ x: 0.4, z: -0.4 },
|
|
329
|
+
{ x: -0.4, z: -0.4 }
|
|
330
|
+
];
|
|
331
|
+
var GroundProbe3D = class {
|
|
332
|
+
constructor(world) {
|
|
184
333
|
this.world = world;
|
|
185
|
-
this.scene = scene;
|
|
186
334
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
detectGround(entity) {
|
|
194
|
-
if (!this.world?.world || !entity.body) return false;
|
|
335
|
+
rays = /* @__PURE__ */ new Map();
|
|
336
|
+
debugLines = /* @__PURE__ */ new Map();
|
|
337
|
+
probeSupport(entity, options) {
|
|
338
|
+
if (!this.world?.world || !entity.body) return null;
|
|
339
|
+
const mode = options.mode ?? "any";
|
|
340
|
+
const offsets = mode === "center" ? (options.offsets ?? DEFAULT_OFFSETS).slice(0, 1) : options.offsets ?? DEFAULT_OFFSETS;
|
|
195
341
|
const translation = entity.body.translation();
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
{ x: radius, z: -radius },
|
|
203
|
-
{ x: -radius, z: -radius }
|
|
204
|
-
];
|
|
205
|
-
let entityRays = this.rays.get(entity.uuid);
|
|
206
|
-
if (!entityRays) {
|
|
207
|
-
entityRays = offsets.map(() => new Ray({ x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }));
|
|
208
|
-
this.rays.set(entity.uuid, entityRays);
|
|
209
|
-
}
|
|
210
|
-
let grounded = false;
|
|
211
|
-
offsets.forEach((offset, i) => {
|
|
212
|
-
if (grounded) return;
|
|
213
|
-
const ray = entityRays[i];
|
|
342
|
+
const rays = this.getOrCreateRays(entity.uuid, offsets.length);
|
|
343
|
+
const originYOffset = options.originYOffset ?? 0;
|
|
344
|
+
let support = null;
|
|
345
|
+
for (let index = 0; index < offsets.length; index++) {
|
|
346
|
+
const offset = offsets[index];
|
|
347
|
+
const ray = rays[index];
|
|
214
348
|
ray.origin = {
|
|
215
349
|
x: translation.x + offset.x,
|
|
216
|
-
y: translation.y,
|
|
350
|
+
y: translation.y + originYOffset,
|
|
217
351
|
z: translation.z + offset.z
|
|
218
352
|
};
|
|
219
353
|
ray.dir = { x: 0, y: -1, z: 0 };
|
|
220
354
|
const hit = this.world.world.castRay(
|
|
221
355
|
ray,
|
|
222
|
-
rayLength,
|
|
356
|
+
options.rayLength,
|
|
223
357
|
true,
|
|
224
358
|
void 0,
|
|
225
359
|
void 0,
|
|
226
360
|
void 0,
|
|
227
|
-
|
|
228
|
-
(collider) => {
|
|
229
|
-
const ref = collider._parent?.userData?.uuid;
|
|
230
|
-
if (ref === entity.uuid) return false;
|
|
231
|
-
grounded = true;
|
|
232
|
-
return true;
|
|
233
|
-
}
|
|
361
|
+
entity.body
|
|
234
362
|
);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
363
|
+
if (!hit) continue;
|
|
364
|
+
const nextSupport = {
|
|
365
|
+
toi: hit.toi,
|
|
366
|
+
point: {
|
|
367
|
+
x: ray.origin.x + ray.dir.x * hit.toi,
|
|
368
|
+
y: ray.origin.y + ray.dir.y * hit.toi,
|
|
369
|
+
z: ray.origin.z + ray.dir.z * hit.toi
|
|
370
|
+
},
|
|
371
|
+
origin: {
|
|
372
|
+
x: ray.origin.x,
|
|
373
|
+
y: ray.origin.y,
|
|
374
|
+
z: ray.origin.z
|
|
375
|
+
},
|
|
376
|
+
rayIndex: index,
|
|
377
|
+
colliderUuid: hit.collider?._parent?.userData?.uuid
|
|
378
|
+
};
|
|
379
|
+
if (mode === "center") {
|
|
380
|
+
support = nextSupport;
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
if (!support || nextSupport.toi < support.toi) {
|
|
384
|
+
support = nextSupport;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (options.debug && options.scene) {
|
|
388
|
+
this.updateDebugLines(
|
|
389
|
+
entity.uuid,
|
|
390
|
+
rays,
|
|
391
|
+
Boolean(support),
|
|
392
|
+
options.rayLength,
|
|
393
|
+
options.scene
|
|
394
|
+
);
|
|
395
|
+
} else {
|
|
396
|
+
this.disposeDebugLines(entity.uuid);
|
|
397
|
+
}
|
|
398
|
+
return support;
|
|
399
|
+
}
|
|
400
|
+
detect(entity, options) {
|
|
401
|
+
return this.probeSupport(entity, options) != null;
|
|
402
|
+
}
|
|
403
|
+
destroyEntity(uuid) {
|
|
404
|
+
this.rays.delete(uuid);
|
|
405
|
+
this.disposeDebugLines(uuid);
|
|
406
|
+
}
|
|
407
|
+
destroy() {
|
|
408
|
+
this.rays.clear();
|
|
409
|
+
for (const uuid of this.debugLines.keys()) {
|
|
410
|
+
this.disposeDebugLines(uuid);
|
|
238
411
|
}
|
|
239
|
-
|
|
412
|
+
this.debugLines.clear();
|
|
240
413
|
}
|
|
241
|
-
|
|
242
|
-
let
|
|
414
|
+
getOrCreateRays(uuid, count) {
|
|
415
|
+
let rays = this.rays.get(uuid);
|
|
416
|
+
if (!rays || rays.length !== count) {
|
|
417
|
+
rays = Array.from(
|
|
418
|
+
{ length: count },
|
|
419
|
+
() => new Ray({ x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 })
|
|
420
|
+
);
|
|
421
|
+
this.rays.set(uuid, rays);
|
|
422
|
+
}
|
|
423
|
+
return rays;
|
|
424
|
+
}
|
|
425
|
+
updateDebugLines(uuid, rays, hasGround, length, scene) {
|
|
426
|
+
let lines = this.debugLines.get(uuid);
|
|
243
427
|
if (!lines) {
|
|
244
428
|
lines = rays.map(() => {
|
|
245
|
-
const geometry = new BufferGeometry().setFromPoints([
|
|
429
|
+
const geometry = new BufferGeometry().setFromPoints([
|
|
430
|
+
new Vector3(),
|
|
431
|
+
new Vector3()
|
|
432
|
+
]);
|
|
246
433
|
const material = new LineBasicMaterial({ color: 16711680 });
|
|
247
434
|
const line = new Line(geometry, material);
|
|
248
|
-
|
|
435
|
+
scene.add(line);
|
|
249
436
|
return line;
|
|
250
437
|
});
|
|
251
|
-
this.debugLines.set(
|
|
438
|
+
this.debugLines.set(uuid, lines);
|
|
252
439
|
}
|
|
253
|
-
rays.forEach((ray,
|
|
254
|
-
const line = lines[
|
|
440
|
+
rays.forEach((ray, index) => {
|
|
441
|
+
const line = lines[index];
|
|
255
442
|
const start = new Vector3(ray.origin.x, ray.origin.y, ray.origin.z);
|
|
256
443
|
const end = new Vector3(
|
|
257
444
|
ray.origin.x + ray.dir.x * length,
|
|
258
445
|
ray.origin.y + ray.dir.y * length,
|
|
259
446
|
ray.origin.z + ray.dir.z * length
|
|
260
447
|
);
|
|
448
|
+
line.visible = true;
|
|
261
449
|
line.geometry.setFromPoints([start, end]);
|
|
262
|
-
line.material.color.setHex(
|
|
450
|
+
line.material.color.setHex(
|
|
451
|
+
hasGround ? 65280 : 16711680
|
|
452
|
+
);
|
|
263
453
|
});
|
|
264
454
|
}
|
|
455
|
+
disposeDebugLines(uuid) {
|
|
456
|
+
const lines = this.debugLines.get(uuid);
|
|
457
|
+
if (!lines) return;
|
|
458
|
+
for (const line of lines) {
|
|
459
|
+
line.removeFromParent();
|
|
460
|
+
line.geometry.dispose();
|
|
461
|
+
line.material.dispose();
|
|
462
|
+
}
|
|
463
|
+
this.debugLines.delete(uuid);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
function getGroundAnchorOffsetY(entity) {
|
|
467
|
+
const runtimeColliderDesc = entity?.colliderDesc;
|
|
468
|
+
if (runtimeColliderDesc) {
|
|
469
|
+
const serialized = serializeColliderDesc(runtimeColliderDesc);
|
|
470
|
+
const centerY2 = serialized.translation?.[1] ?? 0;
|
|
471
|
+
if (serialized.shape === "capsule" && serialized.dimensions.length >= 2) {
|
|
472
|
+
const halfCylinder = serialized.dimensions[0] ?? 0;
|
|
473
|
+
const radius = serialized.dimensions[1] ?? 0;
|
|
474
|
+
return halfCylinder + radius - centerY2;
|
|
475
|
+
}
|
|
476
|
+
if (serialized.shape === "cuboid" && serialized.dimensions.length >= 2) {
|
|
477
|
+
const halfHeight = serialized.dimensions[1] ?? 0;
|
|
478
|
+
return halfHeight - centerY2;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const collisionSize = entity?.options?.collision?.size ?? entity?.options?.collisionSize ?? entity?.options?.size;
|
|
482
|
+
const height = collisionSize?.y ?? 0;
|
|
483
|
+
if (height <= 0) {
|
|
484
|
+
return 0;
|
|
485
|
+
}
|
|
486
|
+
const collisionPosition = entity?.options?.collision?.position ?? entity?.options?.collisionPosition;
|
|
487
|
+
const centerY = collisionPosition?.y ?? height / 2;
|
|
488
|
+
return height / 2 - centerY;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/lib/behaviors/platformer-3d/platformer-3d.behavior.ts
|
|
492
|
+
var Platformer3DBehavior = class {
|
|
493
|
+
world;
|
|
494
|
+
scene;
|
|
495
|
+
groundProbe;
|
|
496
|
+
constructor(world, scene) {
|
|
497
|
+
this.world = world;
|
|
498
|
+
this.scene = scene;
|
|
499
|
+
this.groundProbe = new GroundProbe3D(world);
|
|
500
|
+
}
|
|
265
501
|
/**
|
|
266
502
|
* Apply horizontal movement based on input
|
|
267
503
|
*/
|
|
@@ -322,22 +558,7 @@ var Platformer3DBehavior = class {
|
|
|
322
558
|
const buttonReleased = state.jumpReleasedSinceLastJump;
|
|
323
559
|
const inMultiJumpWindow = state.timeSinceJump >= movement.multiJumpWindowTime;
|
|
324
560
|
const canMultiJump = !state.grounded && hasJumpsRemaining && buttonReleased && inMultiJumpWindow;
|
|
325
|
-
console.log("[JUMP DEBUG] Attempting jump:", {
|
|
326
|
-
grounded: state.grounded,
|
|
327
|
-
jumpCount: state.jumpCount,
|
|
328
|
-
maxJumps: movement.maxJumps,
|
|
329
|
-
isFirstJump,
|
|
330
|
-
canMultiJump,
|
|
331
|
-
"--- Multi-jump conditions ---": "",
|
|
332
|
-
"!grounded": !state.grounded,
|
|
333
|
-
hasJumpsRemaining,
|
|
334
|
-
buttonReleased,
|
|
335
|
-
inMultiJumpWindow,
|
|
336
|
-
timeSinceJump: state.timeSinceJump.toFixed(3),
|
|
337
|
-
multiJumpWindowTime: movement.multiJumpWindowTime
|
|
338
|
-
});
|
|
339
561
|
if (isFirstJump || canMultiJump) {
|
|
340
|
-
console.log("[JUMP DEBUG] \u2705 EXECUTING JUMP #" + (state.jumpCount + 1));
|
|
341
562
|
state.jumpBuffered = false;
|
|
342
563
|
state.jumpCount++;
|
|
343
564
|
state.jumpReleasedSinceLastJump = false;
|
|
@@ -348,8 +569,6 @@ var Platformer3DBehavior = class {
|
|
|
348
569
|
state.jumpCutApplied = false;
|
|
349
570
|
entity.transformStore.velocity.y = movement.jumpForce;
|
|
350
571
|
entity.transformStore.dirty.velocity = true;
|
|
351
|
-
} else {
|
|
352
|
-
console.log("[JUMP DEBUG] \u274C JUMP BLOCKED - conditions not met");
|
|
353
572
|
}
|
|
354
573
|
}
|
|
355
574
|
/**
|
|
@@ -377,12 +596,26 @@ var Platformer3DBehavior = class {
|
|
|
377
596
|
let isGrounded = false;
|
|
378
597
|
const isAirborne = state.jumping || state.falling;
|
|
379
598
|
if (isAirborne) {
|
|
380
|
-
const
|
|
599
|
+
const probeOriginYOffset = 0.05 - getGroundAnchorOffsetY(entity);
|
|
600
|
+
const nearGround = this.groundProbe.detect(entity, {
|
|
601
|
+
rayLength: entity.platformer.groundRayLength,
|
|
602
|
+
mode: "any",
|
|
603
|
+
debug: entity.platformer.debugGroundProbe,
|
|
604
|
+
scene: this.scene,
|
|
605
|
+
originYOffset: probeOriginYOffset
|
|
606
|
+
});
|
|
381
607
|
const canLand = state.falling && !state.jumping;
|
|
382
608
|
const hasLanded = Math.abs(velocity.y) < 0.5;
|
|
383
609
|
isGrounded = nearGround && canLand && hasLanded;
|
|
384
610
|
} else {
|
|
385
|
-
const
|
|
611
|
+
const probeOriginYOffset = 0.05 - getGroundAnchorOffsetY(entity);
|
|
612
|
+
const nearGround = this.groundProbe.detect(entity, {
|
|
613
|
+
rayLength: entity.platformer.groundRayLength,
|
|
614
|
+
mode: "any",
|
|
615
|
+
debug: entity.platformer.debugGroundProbe,
|
|
616
|
+
scene: this.scene,
|
|
617
|
+
originYOffset: probeOriginYOffset
|
|
618
|
+
});
|
|
386
619
|
const notFallingFast = velocity.y > -2;
|
|
387
620
|
isGrounded = nearGround && notFallingFast;
|
|
388
621
|
}
|
|
@@ -409,26 +642,32 @@ var Platformer3DBehavior = class {
|
|
|
409
642
|
}
|
|
410
643
|
}
|
|
411
644
|
/**
|
|
412
|
-
* Update
|
|
645
|
+
* Update one platformer entity.
|
|
646
|
+
*/
|
|
647
|
+
updateEntity(entity, delta) {
|
|
648
|
+
if (!entity.platformer || !entity.$platformer || !entity.platformerState) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const platformerEntity = entity;
|
|
652
|
+
this.updateState(platformerEntity, delta);
|
|
653
|
+
this.applyMovement(platformerEntity, delta);
|
|
654
|
+
this.handleJump(platformerEntity, delta);
|
|
655
|
+
this.applyGravity(platformerEntity, delta);
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Update all platformer entities.
|
|
413
659
|
*/
|
|
414
660
|
update(delta) {
|
|
415
661
|
if (!this.world?.collisionMap) return;
|
|
416
662
|
for (const [, entity] of this.world.collisionMap) {
|
|
417
|
-
|
|
418
|
-
if (!platformerEntity.platformer || !platformerEntity.$platformer || !platformerEntity.platformerState) {
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
421
|
-
this.updateState(platformerEntity, delta);
|
|
422
|
-
this.applyMovement(platformerEntity, delta);
|
|
423
|
-
this.handleJump(platformerEntity, delta);
|
|
424
|
-
this.applyGravity(platformerEntity, delta);
|
|
663
|
+
this.updateEntity(entity, delta);
|
|
425
664
|
}
|
|
426
665
|
}
|
|
427
666
|
/**
|
|
428
667
|
* Cleanup
|
|
429
668
|
*/
|
|
430
669
|
destroy() {
|
|
431
|
-
this.
|
|
670
|
+
this.groundProbe.destroy();
|
|
432
671
|
}
|
|
433
672
|
};
|
|
434
673
|
|
|
@@ -449,25 +688,25 @@ var defaultOptions = {
|
|
|
449
688
|
jumpForce: 12,
|
|
450
689
|
maxJumps: 1,
|
|
451
690
|
gravity: 9.82,
|
|
452
|
-
groundRayLength: 1
|
|
691
|
+
groundRayLength: 1,
|
|
692
|
+
debugGroundProbe: false
|
|
453
693
|
};
|
|
694
|
+
var PLATFORMER_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for("zylem:behavior:platformer-3d");
|
|
454
695
|
var Platformer3DBehaviorSystem = class {
|
|
455
|
-
constructor(world, scene) {
|
|
696
|
+
constructor(world, scene, getBehaviorLinks) {
|
|
456
697
|
this.world = world;
|
|
457
698
|
this.scene = scene;
|
|
699
|
+
this.getBehaviorLinks = getBehaviorLinks;
|
|
458
700
|
this.movementBehavior = new Platformer3DBehavior(world, scene);
|
|
459
701
|
}
|
|
460
702
|
movementBehavior;
|
|
461
|
-
update(
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
(r) => r.descriptor.key === /* @__PURE__ */ Symbol.for("zylem:behavior:platformer-3d")
|
|
469
|
-
);
|
|
470
|
-
if (!platformerRef || !gameEntity.body) continue;
|
|
703
|
+
update(_ecs, delta) {
|
|
704
|
+
const links = this.getBehaviorLinks?.(PLATFORMER_BEHAVIOR_KEY);
|
|
705
|
+
if (!links) return;
|
|
706
|
+
for (const link of links) {
|
|
707
|
+
const gameEntity = link.entity;
|
|
708
|
+
const platformerRef = link.ref;
|
|
709
|
+
if (!gameEntity.body) continue;
|
|
471
710
|
const options = platformerRef.options;
|
|
472
711
|
if (!gameEntity.platformer) {
|
|
473
712
|
gameEntity.platformer = createPlatformer3DMovementComponent(options);
|
|
@@ -487,8 +726,8 @@ var Platformer3DBehaviorSystem = class {
|
|
|
487
726
|
if (platformerRef.fsm && gameEntity.$platformer && gameEntity.platformerState) {
|
|
488
727
|
platformerRef.fsm.update(gameEntity.$platformer, gameEntity.platformerState);
|
|
489
728
|
}
|
|
729
|
+
this.movementBehavior.updateEntity(gameEntity, delta);
|
|
490
730
|
}
|
|
491
|
-
this.movementBehavior.update(delta);
|
|
492
731
|
}
|
|
493
732
|
destroy(_ecs) {
|
|
494
733
|
this.movementBehavior.destroy();
|
|
@@ -497,7 +736,11 @@ var Platformer3DBehaviorSystem = class {
|
|
|
497
736
|
var Platformer3DBehavior2 = defineBehavior({
|
|
498
737
|
name: "platformer-3d",
|
|
499
738
|
defaultOptions,
|
|
500
|
-
systemFactory: (ctx) => new Platformer3DBehaviorSystem(
|
|
739
|
+
systemFactory: (ctx) => new Platformer3DBehaviorSystem(
|
|
740
|
+
ctx.world,
|
|
741
|
+
ctx.scene,
|
|
742
|
+
ctx.getBehaviorLinks
|
|
743
|
+
),
|
|
501
744
|
createHandle: (ref) => ({
|
|
502
745
|
getState: () => ref.fsm?.getState() ?? "idle" /* Idle */,
|
|
503
746
|
isGrounded: () => ref.fsm?.isGrounded() ?? false,
|