@lagless/character-controller-3d 0.0.38

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.
@@ -0,0 +1,5 @@
1
+ export * from './lib/character-controller-config.js';
2
+ export * from './lib/character-controller-interfaces.js';
3
+ export * from './lib/character-controller-manager.js';
4
+ export * from './lib/character-controller-system.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sCAAsC,CAAC;AACrD,cAAc,0CAA0C,CAAC;AACzD,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from './lib/character-controller-config.js';
2
+ export * from './lib/character-controller-interfaces.js';
3
+ export * from './lib/character-controller-manager.js';
4
+ export * from './lib/character-controller-system.js';
5
+
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './lib/character-controller-config.js';\nexport * from './lib/character-controller-interfaces.js';\nexport * from './lib/character-controller-manager.js';\nexport * from './lib/character-controller-system.js';\n"],"names":[],"rangeMappings":";;;","mappings":"AAAA,cAAc,uCAAuC;AACrD,cAAc,2CAA2C;AACzD,cAAc,wCAAwC;AACtD,cAAc,uCAAuC"}
@@ -0,0 +1,36 @@
1
+ export interface CharacterControllerConfig {
2
+ /** Walking speed (units/sec). Default: 4 */
3
+ walkSpeed: number;
4
+ /** Running speed (units/sec). Default: 8 */
5
+ runSpeed: number;
6
+ /** Acceleration toward target speed (units/sec²). Default: 40 */
7
+ acceleration: number;
8
+ /** Deceleration when no input (units/sec²). Default: 60 */
9
+ deceleration: number;
10
+ /** Gravity acceleration (units/sec²). Default: 20 */
11
+ gravity: number;
12
+ /** Jump force (initial vertical velocity, units/sec). Default: 8 */
13
+ jumpForce: number;
14
+ /** Max number of jumps (1 = no double jump). Default: 1 */
15
+ maxJumps: number;
16
+ /** Max fall speed (units/sec). Default: 30 */
17
+ maxFallSpeed: number;
18
+ /** Capsule collider half-height. Default: 0.5 */
19
+ capsuleHalfHeight: number;
20
+ /** Capsule collider radius. Default: 0.3 */
21
+ capsuleRadius: number;
22
+ /** KCC skin offset. Default: 0.01 */
23
+ kccOffset: number;
24
+ /** Max slope climb angle in radians. Default: PI/4 (45 degrees) */
25
+ maxSlopeClimbAngle: number;
26
+ /** Min slope slide angle in radians. Default: PI/6 (30 degrees) */
27
+ minSlopeSlideAngle: number;
28
+ /** Autostep max height. 0 to disable. Default: 0.3 */
29
+ autostepMaxHeight: number;
30
+ /** Autostep min width. Default: 0.2 */
31
+ autostepMinWidth: number;
32
+ /** Snap to ground distance. 0 to disable. Default: 0.3 */
33
+ snapToGroundDistance: number;
34
+ }
35
+ export declare const DEFAULT_CHARACTER_CONTROLLER_CONFIG: CharacterControllerConfig;
36
+ //# sourceMappingURL=character-controller-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"character-controller-config.d.ts","sourceRoot":"","sources":["../../src/lib/character-controller-config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,yBAAyB;IACxC,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mEAAmE;IACnE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uCAAuC;IACvC,gBAAgB,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,mCAAmC,EAAE,yBAiBjD,CAAC"}
@@ -0,0 +1,20 @@
1
+ export const DEFAULT_CHARACTER_CONTROLLER_CONFIG = {
2
+ walkSpeed: 4,
3
+ runSpeed: 8,
4
+ acceleration: 40,
5
+ deceleration: 60,
6
+ gravity: 20,
7
+ jumpForce: 8,
8
+ maxJumps: 1,
9
+ maxFallSpeed: 30,
10
+ capsuleHalfHeight: 0.5,
11
+ capsuleRadius: 0.3,
12
+ kccOffset: 0.01,
13
+ maxSlopeClimbAngle: Math.PI / 4,
14
+ minSlopeSlideAngle: Math.PI / 6,
15
+ autostepMaxHeight: 0.3,
16
+ autostepMinWidth: 0.2,
17
+ snapToGroundDistance: 0.3
18
+ };
19
+
20
+ //# sourceMappingURL=character-controller-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/character-controller-config.ts"],"sourcesContent":["export interface CharacterControllerConfig {\n /** Walking speed (units/sec). Default: 4 */\n walkSpeed: number;\n /** Running speed (units/sec). Default: 8 */\n runSpeed: number;\n /** Acceleration toward target speed (units/sec²). Default: 40 */\n acceleration: number;\n /** Deceleration when no input (units/sec²). Default: 60 */\n deceleration: number;\n /** Gravity acceleration (units/sec²). Default: 20 */\n gravity: number;\n /** Jump force (initial vertical velocity, units/sec). Default: 8 */\n jumpForce: number;\n /** Max number of jumps (1 = no double jump). Default: 1 */\n maxJumps: number;\n /** Max fall speed (units/sec). Default: 30 */\n maxFallSpeed: number;\n /** Capsule collider half-height. Default: 0.5 */\n capsuleHalfHeight: number;\n /** Capsule collider radius. Default: 0.3 */\n capsuleRadius: number;\n /** KCC skin offset. Default: 0.01 */\n kccOffset: number;\n /** Max slope climb angle in radians. Default: PI/4 (45 degrees) */\n maxSlopeClimbAngle: number;\n /** Min slope slide angle in radians. Default: PI/6 (30 degrees) */\n minSlopeSlideAngle: number;\n /** Autostep max height. 0 to disable. Default: 0.3 */\n autostepMaxHeight: number;\n /** Autostep min width. Default: 0.2 */\n autostepMinWidth: number;\n /** Snap to ground distance. 0 to disable. Default: 0.3 */\n snapToGroundDistance: number;\n}\n\nexport const DEFAULT_CHARACTER_CONTROLLER_CONFIG: CharacterControllerConfig = {\n walkSpeed: 4,\n runSpeed: 8,\n acceleration: 40,\n deceleration: 60,\n gravity: 20,\n jumpForce: 8,\n maxJumps: 1,\n maxFallSpeed: 30,\n capsuleHalfHeight: 0.5,\n capsuleRadius: 0.3,\n kccOffset: 0.01,\n maxSlopeClimbAngle: Math.PI / 4,\n minSlopeSlideAngle: Math.PI / 6,\n autostepMaxHeight: 0.3,\n autostepMinWidth: 0.2,\n snapToGroundDistance: 0.3,\n};\n"],"names":["DEFAULT_CHARACTER_CONTROLLER_CONFIG","walkSpeed","runSpeed","acceleration","deceleration","gravity","jumpForce","maxJumps","maxFallSpeed","capsuleHalfHeight","capsuleRadius","kccOffset","maxSlopeClimbAngle","Math","PI","minSlopeSlideAngle","autostepMaxHeight","autostepMinWidth","snapToGroundDistance"],"rangeMappings":";;;;;;;;;;;;;;;;;","mappings":"AAmCA,OAAO,MAAMA,sCAAiE;IAC5EC,WAAW;IACXC,UAAU;IACVC,cAAc;IACdC,cAAc;IACdC,SAAS;IACTC,WAAW;IACXC,UAAU;IACVC,cAAc;IACdC,mBAAmB;IACnBC,eAAe;IACfC,WAAW;IACXC,oBAAoBC,KAAKC,EAAE,GAAG;IAC9BC,oBAAoBF,KAAKC,EAAE,GAAG;IAC9BE,mBAAmB;IACnBC,kBAAkB;IAClBC,sBAAsB;AACxB,EAAE"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Interface for a CharacterState component from codegen.
3
+ * The game's codegen produces a class with these fields.
4
+ * Each field has `get(entity): number` / `set(entity, value): void`.
5
+ */
6
+ export interface ICharacterStateComponent {
7
+ verticalVelocity: {
8
+ get(entity: number): number;
9
+ set(entity: number, value: number): void;
10
+ };
11
+ grounded: {
12
+ get(entity: number): number;
13
+ set(entity: number, value: number): void;
14
+ };
15
+ currentSpeed: {
16
+ get(entity: number): number;
17
+ set(entity: number, value: number): void;
18
+ };
19
+ jumpCount: {
20
+ get(entity: number): number;
21
+ set(entity: number, value: number): void;
22
+ };
23
+ moveInputX: {
24
+ get(entity: number): number;
25
+ set(entity: number, value: number): void;
26
+ };
27
+ moveInputZ: {
28
+ get(entity: number): number;
29
+ set(entity: number, value: number): void;
30
+ };
31
+ isSprinting: {
32
+ get(entity: number): number;
33
+ set(entity: number, value: number): void;
34
+ };
35
+ facingYaw: {
36
+ get(entity: number): number;
37
+ set(entity: number, value: number): void;
38
+ };
39
+ locomotionAngle: {
40
+ get(entity: number): number;
41
+ set(entity: number, value: number): void;
42
+ };
43
+ }
44
+ //# sourceMappingURL=character-controller-interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"character-controller-interfaces.d.ts","sourceRoot":"","sources":["../../src/lib/character-controller-interfaces.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,gBAAgB,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC5F,QAAQ,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACpF,YAAY,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACxF,SAAS,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACrF,UAAU,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtF,UAAU,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtF,WAAW,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACvF,SAAS,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACrF,eAAe,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC5F"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Interface for a CharacterState component from codegen.
3
+ * The game's codegen produces a class with these fields.
4
+ * Each field has `get(entity): number` / `set(entity, value): void`.
5
+ */ export { };
6
+
7
+ //# sourceMappingURL=character-controller-interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/character-controller-interfaces.ts"],"sourcesContent":["/**\n * Interface for a CharacterState component from codegen.\n * The game's codegen produces a class with these fields.\n * Each field has `get(entity): number` / `set(entity, value): void`.\n */\nexport interface ICharacterStateComponent {\n verticalVelocity: { get(entity: number): number; set(entity: number, value: number): void };\n grounded: { get(entity: number): number; set(entity: number, value: number): void };\n currentSpeed: { get(entity: number): number; set(entity: number, value: number): void };\n jumpCount: { get(entity: number): number; set(entity: number, value: number): void };\n moveInputX: { get(entity: number): number; set(entity: number, value: number): void };\n moveInputZ: { get(entity: number): number; set(entity: number, value: number): void };\n isSprinting: { get(entity: number): number; set(entity: number, value: number): void };\n facingYaw: { get(entity: number): number; set(entity: number, value: number): void };\n locomotionAngle: { get(entity: number): number; set(entity: number, value: number): void };\n}\n"],"names":[],"rangeMappings":";;;;","mappings":"AAAA;;;;CAIC,GACD,WAUC"}
@@ -0,0 +1,38 @@
1
+ import { type RapierKinematicCharacterController } from '@lagless/physics3d';
2
+ import { type PhysicsWorldManager3d } from '@lagless/physics3d';
3
+ import { CharacterControllerConfig } from './character-controller-config.js';
4
+ /**
5
+ * Manages Rapier KCC instances per entity.
6
+ * After rollback (Rapier world is freed and restored), call `recreateAll()`.
7
+ *
8
+ * Supports deferred init: construct with just config, then call `init(worldManager)` later.
9
+ * This is needed because the PhysicsWorldManager3d is created inside PhysicsRunner3d,
10
+ * but KCCManager must be registered in DI before systems are resolved.
11
+ */
12
+ export declare class CharacterControllerManager {
13
+ private readonly _config;
14
+ private readonly _controllers;
15
+ private _worldManager;
16
+ constructor(_config: CharacterControllerConfig);
17
+ /**
18
+ * Deferred initialization — set the world manager after construction.
19
+ */
20
+ init(worldManager: PhysicsWorldManager3d): void;
21
+ private get worldManager();
22
+ createForEntity(entity: number): RapierKinematicCharacterController;
23
+ getForEntity(entity: number): RapierKinematicCharacterController | undefined;
24
+ removeForEntity(entity: number): void;
25
+ /**
26
+ * After rollback: old KCCs are invalid (Rapier world was freed).
27
+ * Recreate all KCCs from scratch using current entity set.
28
+ */
29
+ recreateAll(): void;
30
+ /**
31
+ * After state transfer: recreate KCCs for the given entity set.
32
+ * Use this instead of recreateAll() when _controllers may not reflect
33
+ * the correct entity set (e.g. after late-join state transfer).
34
+ */
35
+ recreateFromEntities(entities: Iterable<number>): void;
36
+ dispose(): void;
37
+ }
38
+ //# sourceMappingURL=character-controller-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"character-controller-manager.d.ts","sourceRoot":"","sources":["../../src/lib/character-controller-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kCAAkC,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAE7E;;;;;;;GAOG;AACH,qBAAa,0BAA0B;IAIzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyD;IACtF,OAAO,CAAC,aAAa,CAAoC;gBAE5B,OAAO,EAAE,yBAAyB;IAE/D;;OAEG;IACI,IAAI,CAAC,YAAY,EAAE,qBAAqB,GAAG,IAAI;IAItD,OAAO,KAAK,YAAY,GAKvB;IAEM,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,kCAAkC;IAoBnE,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,kCAAkC,GAAG,SAAS;IAI5E,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ5C;;;OAGG;IACI,WAAW,IAAI,IAAI;IAS1B;;;;OAIG;IACI,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI;IAQtD,OAAO,IAAI,IAAI;CAMvB"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Manages Rapier KCC instances per entity.
3
+ * After rollback (Rapier world is freed and restored), call `recreateAll()`.
4
+ *
5
+ * Supports deferred init: construct with just config, then call `init(worldManager)` later.
6
+ * This is needed because the PhysicsWorldManager3d is created inside PhysicsRunner3d,
7
+ * but KCCManager must be registered in DI before systems are resolved.
8
+ */ export class CharacterControllerManager {
9
+ /**
10
+ * Deferred initialization — set the world manager after construction.
11
+ */ init(worldManager) {
12
+ this._worldManager = worldManager;
13
+ }
14
+ get worldManager() {
15
+ if (!this._worldManager) {
16
+ throw new Error('CharacterControllerManager not initialized — call init(worldManager) first');
17
+ }
18
+ return this._worldManager;
19
+ }
20
+ createForEntity(entity) {
21
+ if (this._controllers.has(entity)) {
22
+ this.removeForEntity(entity);
23
+ }
24
+ const kcc = this.worldManager.world.createCharacterController(this._config.kccOffset);
25
+ kcc.setUp({
26
+ x: 0,
27
+ y: 1,
28
+ z: 0
29
+ });
30
+ kcc.setMaxSlopeClimbAngle(this._config.maxSlopeClimbAngle);
31
+ kcc.setMinSlopeSlideAngle(this._config.minSlopeSlideAngle);
32
+ if (this._config.autostepMaxHeight > 0) {
33
+ kcc.enableAutostep(this._config.autostepMaxHeight, this._config.autostepMinWidth, true);
34
+ }
35
+ if (this._config.snapToGroundDistance > 0) {
36
+ kcc.enableSnapToGround(this._config.snapToGroundDistance);
37
+ }
38
+ kcc.setSlideEnabled(true);
39
+ kcc.setApplyImpulsesToDynamicBodies(true);
40
+ this._controllers.set(entity, kcc);
41
+ return kcc;
42
+ }
43
+ getForEntity(entity) {
44
+ return this._controllers.get(entity);
45
+ }
46
+ removeForEntity(entity) {
47
+ const kcc = this._controllers.get(entity);
48
+ if (kcc) {
49
+ kcc.free();
50
+ this._controllers.delete(entity);
51
+ }
52
+ }
53
+ /**
54
+ * After rollback: old KCCs are invalid (Rapier world was freed).
55
+ * Recreate all KCCs from scratch using current entity set.
56
+ */ recreateAll() {
57
+ const entities = Array.from(this._controllers.keys());
58
+ // Clear without freeing — old world already freed
59
+ this._controllers.clear();
60
+ for (const entity of entities){
61
+ this.createForEntity(entity);
62
+ }
63
+ }
64
+ /**
65
+ * After state transfer: recreate KCCs for the given entity set.
66
+ * Use this instead of recreateAll() when _controllers may not reflect
67
+ * the correct entity set (e.g. after late-join state transfer).
68
+ */ recreateFromEntities(entities) {
69
+ // Clear without freeing — old world already freed by restoreSnapshot
70
+ this._controllers.clear();
71
+ for (const entity of entities){
72
+ this.createForEntity(entity);
73
+ }
74
+ }
75
+ dispose() {
76
+ for (const kcc of this._controllers.values()){
77
+ kcc.free();
78
+ }
79
+ this._controllers.clear();
80
+ }
81
+ constructor(_config){
82
+ this._config = _config;
83
+ this._controllers = new Map();
84
+ }
85
+ }
86
+
87
+ //# sourceMappingURL=character-controller-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/character-controller-manager.ts"],"sourcesContent":["import { type RapierKinematicCharacterController } from '@lagless/physics3d';\nimport { type PhysicsWorldManager3d } from '@lagless/physics3d';\nimport { CharacterControllerConfig } from './character-controller-config.js';\n\n/**\n * Manages Rapier KCC instances per entity.\n * After rollback (Rapier world is freed and restored), call `recreateAll()`.\n *\n * Supports deferred init: construct with just config, then call `init(worldManager)` later.\n * This is needed because the PhysicsWorldManager3d is created inside PhysicsRunner3d,\n * but KCCManager must be registered in DI before systems are resolved.\n */\nexport class CharacterControllerManager {\n private readonly _controllers = new Map<number, RapierKinematicCharacterController>();\n private _worldManager: PhysicsWorldManager3d | undefined;\n\n constructor(private readonly _config: CharacterControllerConfig) {}\n\n /**\n * Deferred initialization — set the world manager after construction.\n */\n public init(worldManager: PhysicsWorldManager3d): void {\n this._worldManager = worldManager;\n }\n\n private get worldManager(): PhysicsWorldManager3d {\n if (!this._worldManager) {\n throw new Error('CharacterControllerManager not initialized — call init(worldManager) first');\n }\n return this._worldManager;\n }\n\n public createForEntity(entity: number): RapierKinematicCharacterController {\n if (this._controllers.has(entity)) {\n this.removeForEntity(entity);\n }\n const kcc = this.worldManager.world.createCharacterController(this._config.kccOffset);\n kcc.setUp({ x: 0, y: 1, z: 0 });\n kcc.setMaxSlopeClimbAngle(this._config.maxSlopeClimbAngle);\n kcc.setMinSlopeSlideAngle(this._config.minSlopeSlideAngle);\n if (this._config.autostepMaxHeight > 0) {\n kcc.enableAutostep(this._config.autostepMaxHeight, this._config.autostepMinWidth, true);\n }\n if (this._config.snapToGroundDistance > 0) {\n kcc.enableSnapToGround(this._config.snapToGroundDistance);\n }\n kcc.setSlideEnabled(true);\n kcc.setApplyImpulsesToDynamicBodies(true);\n this._controllers.set(entity, kcc);\n return kcc;\n }\n\n public getForEntity(entity: number): RapierKinematicCharacterController | undefined {\n return this._controllers.get(entity);\n }\n\n public removeForEntity(entity: number): void {\n const kcc = this._controllers.get(entity);\n if (kcc) {\n kcc.free();\n this._controllers.delete(entity);\n }\n }\n\n /**\n * After rollback: old KCCs are invalid (Rapier world was freed).\n * Recreate all KCCs from scratch using current entity set.\n */\n public recreateAll(): void {\n const entities = Array.from(this._controllers.keys());\n // Clear without freeing — old world already freed\n this._controllers.clear();\n for (const entity of entities) {\n this.createForEntity(entity);\n }\n }\n\n /**\n * After state transfer: recreate KCCs for the given entity set.\n * Use this instead of recreateAll() when _controllers may not reflect\n * the correct entity set (e.g. after late-join state transfer).\n */\n public recreateFromEntities(entities: Iterable<number>): void {\n // Clear without freeing — old world already freed by restoreSnapshot\n this._controllers.clear();\n for (const entity of entities) {\n this.createForEntity(entity);\n }\n }\n\n public dispose(): void {\n for (const kcc of this._controllers.values()) {\n kcc.free();\n }\n this._controllers.clear();\n }\n}\n"],"names":["CharacterControllerManager","init","worldManager","_worldManager","Error","createForEntity","entity","_controllers","has","removeForEntity","kcc","world","createCharacterController","_config","kccOffset","setUp","x","y","z","setMaxSlopeClimbAngle","maxSlopeClimbAngle","setMinSlopeSlideAngle","minSlopeSlideAngle","autostepMaxHeight","enableAutostep","autostepMinWidth","snapToGroundDistance","enableSnapToGround","setSlideEnabled","setApplyImpulsesToDynamicBodies","set","getForEntity","get","free","delete","recreateAll","entities","Array","from","keys","clear","recreateFromEntities","dispose","values","constructor","Map"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAIA;;;;;;;CAOC,GACD,OAAO,MAAMA;IAMX;;GAEC,GACD,AAAOC,KAAKC,YAAmC,EAAQ;QACrD,IAAI,CAACC,aAAa,GAAGD;IACvB;IAEA,IAAYA,eAAsC;QAChD,IAAI,CAAC,IAAI,CAACC,aAAa,EAAE;YACvB,MAAM,IAAIC,MAAM;QAClB;QACA,OAAO,IAAI,CAACD,aAAa;IAC3B;IAEOE,gBAAgBC,MAAc,EAAsC;QACzE,IAAI,IAAI,CAACC,YAAY,CAACC,GAAG,CAACF,SAAS;YACjC,IAAI,CAACG,eAAe,CAACH;QACvB;QACA,MAAMI,MAAM,IAAI,CAACR,YAAY,CAACS,KAAK,CAACC,yBAAyB,CAAC,IAAI,CAACC,OAAO,CAACC,SAAS;QACpFJ,IAAIK,KAAK,CAAC;YAAEC,GAAG;YAAGC,GAAG;YAAGC,GAAG;QAAE;QAC7BR,IAAIS,qBAAqB,CAAC,IAAI,CAACN,OAAO,CAACO,kBAAkB;QACzDV,IAAIW,qBAAqB,CAAC,IAAI,CAACR,OAAO,CAACS,kBAAkB;QACzD,IAAI,IAAI,CAACT,OAAO,CAACU,iBAAiB,GAAG,GAAG;YACtCb,IAAIc,cAAc,CAAC,IAAI,CAACX,OAAO,CAACU,iBAAiB,EAAE,IAAI,CAACV,OAAO,CAACY,gBAAgB,EAAE;QACpF;QACA,IAAI,IAAI,CAACZ,OAAO,CAACa,oBAAoB,GAAG,GAAG;YACzChB,IAAIiB,kBAAkB,CAAC,IAAI,CAACd,OAAO,CAACa,oBAAoB;QAC1D;QACAhB,IAAIkB,eAAe,CAAC;QACpBlB,IAAImB,+BAA+B,CAAC;QACpC,IAAI,CAACtB,YAAY,CAACuB,GAAG,CAACxB,QAAQI;QAC9B,OAAOA;IACT;IAEOqB,aAAazB,MAAc,EAAkD;QAClF,OAAO,IAAI,CAACC,YAAY,CAACyB,GAAG,CAAC1B;IAC/B;IAEOG,gBAAgBH,MAAc,EAAQ;QAC3C,MAAMI,MAAM,IAAI,CAACH,YAAY,CAACyB,GAAG,CAAC1B;QAClC,IAAII,KAAK;YACPA,IAAIuB,IAAI;YACR,IAAI,CAAC1B,YAAY,CAAC2B,MAAM,CAAC5B;QAC3B;IACF;IAEA;;;GAGC,GACD,AAAO6B,cAAoB;QACzB,MAAMC,WAAWC,MAAMC,IAAI,CAAC,IAAI,CAAC/B,YAAY,CAACgC,IAAI;QAClD,kDAAkD;QAClD,IAAI,CAAChC,YAAY,CAACiC,KAAK;QACvB,KAAK,MAAMlC,UAAU8B,SAAU;YAC7B,IAAI,CAAC/B,eAAe,CAACC;QACvB;IACF;IAEA;;;;GAIC,GACD,AAAOmC,qBAAqBL,QAA0B,EAAQ;QAC5D,qEAAqE;QACrE,IAAI,CAAC7B,YAAY,CAACiC,KAAK;QACvB,KAAK,MAAMlC,UAAU8B,SAAU;YAC7B,IAAI,CAAC/B,eAAe,CAACC;QACvB;IACF;IAEOoC,UAAgB;QACrB,KAAK,MAAMhC,OAAO,IAAI,CAACH,YAAY,CAACoC,MAAM,GAAI;YAC5CjC,IAAIuB,IAAI;QACV;QACA,IAAI,CAAC1B,YAAY,CAACiC,KAAK;IACzB;IA/EAI,YAAY,AAAiB/B,OAAkC,CAAE;aAApCA,UAAAA;aAHZN,eAAe,IAAIsC;IAG8B;AAgFpE"}
@@ -0,0 +1,31 @@
1
+ import type { IFilter, IPhysicsRefsComponent, ITransform3dComponent, PhysicsWorldManager3d } from '@lagless/physics3d';
2
+ import type { ECSConfig, IECSSystem } from '@lagless/core';
3
+ import { CharacterControllerConfig } from './character-controller-config.js';
4
+ import { CharacterControllerManager } from './character-controller-manager.js';
5
+ import { ICharacterStateComponent } from './character-controller-interfaces.js';
6
+ /**
7
+ * Abstract character controller system. Game extends this with codegen types.
8
+ *
9
+ * Handles: acceleration/deceleration, gravity, jump, Rapier KCC collision,
10
+ * Transform3d update, grounded detection, facing yaw, locomotion angle.
11
+ */
12
+ export declare abstract class AbstractCharacterControllerSystem implements IECSSystem {
13
+ protected readonly _config: CharacterControllerConfig;
14
+ protected readonly _ecsConfig: ECSConfig;
15
+ protected readonly _filter: IFilter;
16
+ protected readonly _transform: ITransform3dComponent;
17
+ protected readonly _physicsRefs: IPhysicsRefsComponent;
18
+ protected readonly _characterState: ICharacterStateComponent;
19
+ protected readonly _worldManager: PhysicsWorldManager3d;
20
+ protected readonly _kccManager: CharacterControllerManager;
21
+ protected readonly _frameLengthSec: number;
22
+ constructor(_config: CharacterControllerConfig, _ecsConfig: ECSConfig, _filter: IFilter, _transform: ITransform3dComponent, _physicsRefs: IPhysicsRefsComponent, _characterState: ICharacterStateComponent, _worldManager: PhysicsWorldManager3d, _kccManager: CharacterControllerManager);
23
+ update(_tick: number): void;
24
+ protected updateEntity(entity: number, dt: number): void;
25
+ /**
26
+ * Called from game systems (e.g. ApplyCharacterInputSystem) when jump is requested.
27
+ * Checks grounded + jumpCount, applies vertical velocity.
28
+ */
29
+ tryJump(entity: number): boolean;
30
+ }
31
+ //# sourceMappingURL=character-controller-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"character-controller-system.d.ts","sourceRoot":"","sources":["../../src/lib/character-controller-system.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACvH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAMhF;;;;;GAKG;AACH,8BAAsB,iCAAkC,YAAW,UAAU;IAIzE,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,yBAAyB;IACrD,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,SAAS;IACxC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO;IACnC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,qBAAqB;IACpD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,qBAAqB;IACtD,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,wBAAwB;IAC5D,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,qBAAqB;IACvD,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,0BAA0B;IAV5D,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;gBAGtB,OAAO,EAAE,yBAAyB,EAClC,UAAU,EAAE,SAAS,EACrB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,qBAAqB,EACjC,YAAY,EAAE,qBAAqB,EACnC,eAAe,EAAE,wBAAwB,EACzC,aAAa,EAAE,qBAAqB,EACpC,WAAW,EAAE,0BAA0B;IAKrD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQlC,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IA0GxD;;;OAGG;IACI,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CAYxC"}
@@ -0,0 +1,156 @@
1
+ import { MathOps } from '@lagless/math';
2
+ const _desiredVec3 = {
3
+ x: 0,
4
+ y: 0,
5
+ z: 0
6
+ };
7
+ const _posVec3 = {
8
+ x: 0,
9
+ y: 0,
10
+ z: 0
11
+ };
12
+ const _rotQuat = {
13
+ x: 0,
14
+ y: 0,
15
+ z: 0,
16
+ w: 1
17
+ };
18
+ /**
19
+ * Abstract character controller system. Game extends this with codegen types.
20
+ *
21
+ * Handles: acceleration/deceleration, gravity, jump, Rapier KCC collision,
22
+ * Transform3d update, grounded detection, facing yaw, locomotion angle.
23
+ */ export class AbstractCharacterControllerSystem {
24
+ update(_tick) {
25
+ const dt = this._frameLengthSec;
26
+ for(let i = 0; i < this._filter.length; i++){
27
+ const entity = this._filter.entities(i);
28
+ this.updateEntity(entity, dt);
29
+ }
30
+ }
31
+ updateEntity(entity, dt) {
32
+ const cs = this._characterState;
33
+ const moveX = cs.moveInputX.get(entity);
34
+ const moveZ = cs.moveInputZ.get(entity);
35
+ const isSprinting = cs.isSprinting.get(entity) !== 0;
36
+ const facingYaw = cs.facingYaw.get(entity);
37
+ // --- Target speed ---
38
+ const inputLengthSq = moveX * moveX + moveZ * moveZ;
39
+ const hasInput = inputLengthSq > 0.001;
40
+ const targetSpeed = hasInput ? isSprinting ? this._config.runSpeed : this._config.walkSpeed : 0;
41
+ // --- Accelerate/decelerate ---
42
+ let currentSpeed = cs.currentSpeed.get(entity);
43
+ if (targetSpeed > currentSpeed) {
44
+ currentSpeed = Math.min(currentSpeed + this._config.acceleration * dt, targetSpeed);
45
+ } else if (targetSpeed < currentSpeed) {
46
+ currentSpeed = Math.max(currentSpeed - this._config.deceleration * dt, targetSpeed);
47
+ }
48
+ cs.currentSpeed.set(entity, currentSpeed);
49
+ // --- Compute movement direction (world space, already in moveInputX/Z) ---
50
+ let dirX = 0, dirZ = 0;
51
+ if (hasInput && currentSpeed > 0.001) {
52
+ const invLen = 1 / MathOps.sqrt(inputLengthSq);
53
+ dirX = moveX * invLen;
54
+ dirZ = moveZ * invLen;
55
+ }
56
+ // --- Gravity ---
57
+ let vertVel = cs.verticalVelocity.get(entity);
58
+ const grounded = cs.grounded.get(entity) !== 0;
59
+ if (!grounded) {
60
+ vertVel -= this._config.gravity * dt;
61
+ if (vertVel < -this._config.maxFallSpeed) vertVel = -this._config.maxFallSpeed;
62
+ } else if (vertVel < 0) {
63
+ vertVel = 0;
64
+ }
65
+ cs.verticalVelocity.set(entity, vertVel);
66
+ // --- Desired displacement ---
67
+ const desiredX = dirX * currentSpeed * dt;
68
+ const desiredY = vertVel * dt;
69
+ const desiredZ = dirZ * currentSpeed * dt;
70
+ // --- KCC movement ---
71
+ const kcc = this._kccManager.getForEntity(entity);
72
+ if (!kcc) return;
73
+ const colliderHandle = this._physicsRefs.colliderHandle.get(entity);
74
+ const collider = this._worldManager.getCollider(colliderHandle);
75
+ if (!collider) return;
76
+ _desiredVec3.x = desiredX;
77
+ _desiredVec3.y = desiredY;
78
+ _desiredVec3.z = desiredZ;
79
+ kcc.computeColliderMovement(collider, _desiredVec3);
80
+ const computed = kcc.computedMovement();
81
+ const newGrounded = kcc.computedGrounded();
82
+ // --- Apply to Transform3d ---
83
+ const posX = this._transform.positionX.get(entity) + computed.x;
84
+ const posY = this._transform.positionY.get(entity) + computed.y;
85
+ const posZ = this._transform.positionZ.get(entity) + computed.z;
86
+ this._transform.positionX.set(entity, posX);
87
+ this._transform.positionY.set(entity, posY);
88
+ this._transform.positionZ.set(entity, posZ);
89
+ // --- Sync to Rapier body ---
90
+ const bodyHandle = this._physicsRefs.bodyHandle.get(entity);
91
+ const body = this._worldManager.getBody(bodyHandle);
92
+ if (body) {
93
+ _posVec3.x = posX;
94
+ _posVec3.y = posY;
95
+ _posVec3.z = posZ;
96
+ body.setNextKinematicTranslation(_posVec3);
97
+ }
98
+ // --- Update grounded ---
99
+ cs.grounded.set(entity, newGrounded ? 1 : 0);
100
+ if (newGrounded) {
101
+ cs.jumpCount.set(entity, 0);
102
+ if (vertVel < 0) {
103
+ cs.verticalVelocity.set(entity, 0);
104
+ }
105
+ }
106
+ // --- Character rotation = camera yaw ---
107
+ const halfYaw = facingYaw * 0.5;
108
+ const sinY = MathOps.sin(halfYaw);
109
+ const cosY = MathOps.cos(halfYaw);
110
+ this._transform.rotationX.set(entity, 0);
111
+ this._transform.rotationY.set(entity, sinY);
112
+ this._transform.rotationZ.set(entity, 0);
113
+ this._transform.rotationW.set(entity, cosY);
114
+ if (body) {
115
+ _rotQuat.x = 0;
116
+ _rotQuat.y = sinY;
117
+ _rotQuat.z = 0;
118
+ _rotQuat.w = cosY;
119
+ body.setNextKinematicRotation(_rotQuat);
120
+ }
121
+ // --- Locomotion angle (movement direction relative to facing, for animation) ---
122
+ if (hasInput && currentSpeed > 0.001) {
123
+ const moveAngle = MathOps.atan2(dirX, dirZ);
124
+ const locoAngle = MathOps.normalizeAngle(moveAngle - facingYaw);
125
+ cs.locomotionAngle.set(entity, locoAngle);
126
+ } else {
127
+ cs.locomotionAngle.set(entity, 0);
128
+ }
129
+ }
130
+ /**
131
+ * Called from game systems (e.g. ApplyCharacterInputSystem) when jump is requested.
132
+ * Checks grounded + jumpCount, applies vertical velocity.
133
+ */ tryJump(entity) {
134
+ const cs = this._characterState;
135
+ const grounded = cs.grounded.get(entity) !== 0;
136
+ const jumpCount = cs.jumpCount.get(entity);
137
+ if (!grounded && jumpCount >= this._config.maxJumps) return false;
138
+ cs.verticalVelocity.set(entity, this._config.jumpForce);
139
+ cs.jumpCount.set(entity, jumpCount + 1);
140
+ cs.grounded.set(entity, 0);
141
+ return true;
142
+ }
143
+ constructor(_config, _ecsConfig, _filter, _transform, _physicsRefs, _characterState, _worldManager, _kccManager){
144
+ this._config = _config;
145
+ this._ecsConfig = _ecsConfig;
146
+ this._filter = _filter;
147
+ this._transform = _transform;
148
+ this._physicsRefs = _physicsRefs;
149
+ this._characterState = _characterState;
150
+ this._worldManager = _worldManager;
151
+ this._kccManager = _kccManager;
152
+ this._frameLengthSec = this._ecsConfig.frameLength / 1000;
153
+ }
154
+ }
155
+
156
+ //# sourceMappingURL=character-controller-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/character-controller-system.ts"],"sourcesContent":["import { MathOps } from '@lagless/math';\nimport type { IFilter, IPhysicsRefsComponent, ITransform3dComponent, PhysicsWorldManager3d } from '@lagless/physics3d';\nimport type { ECSConfig, IECSSystem } from '@lagless/core';\nimport { CharacterControllerConfig } from './character-controller-config.js';\nimport { CharacterControllerManager } from './character-controller-manager.js';\nimport { ICharacterStateComponent } from './character-controller-interfaces.js';\n\nconst _desiredVec3 = { x: 0, y: 0, z: 0 };\nconst _posVec3 = { x: 0, y: 0, z: 0 };\nconst _rotQuat = { x: 0, y: 0, z: 0, w: 1 };\n\n/**\n * Abstract character controller system. Game extends this with codegen types.\n *\n * Handles: acceleration/deceleration, gravity, jump, Rapier KCC collision,\n * Transform3d update, grounded detection, facing yaw, locomotion angle.\n */\nexport abstract class AbstractCharacterControllerSystem implements IECSSystem {\n protected readonly _frameLengthSec: number;\n\n constructor(\n protected readonly _config: CharacterControllerConfig,\n protected readonly _ecsConfig: ECSConfig,\n protected readonly _filter: IFilter,\n protected readonly _transform: ITransform3dComponent,\n protected readonly _physicsRefs: IPhysicsRefsComponent,\n protected readonly _characterState: ICharacterStateComponent,\n protected readonly _worldManager: PhysicsWorldManager3d,\n protected readonly _kccManager: CharacterControllerManager,\n ) {\n this._frameLengthSec = this._ecsConfig.frameLength / 1000;\n }\n\n public update(_tick: number): void {\n const dt = this._frameLengthSec;\n for (let i = 0; i < this._filter.length; i++) {\n const entity = this._filter.entities(i);\n this.updateEntity(entity, dt);\n }\n }\n\n protected updateEntity(entity: number, dt: number): void {\n const cs = this._characterState;\n const moveX = cs.moveInputX.get(entity);\n const moveZ = cs.moveInputZ.get(entity);\n const isSprinting = cs.isSprinting.get(entity) !== 0;\n const facingYaw = cs.facingYaw.get(entity);\n\n // --- Target speed ---\n const inputLengthSq = moveX * moveX + moveZ * moveZ;\n const hasInput = inputLengthSq > 0.001;\n const targetSpeed = hasInput ? (isSprinting ? this._config.runSpeed : this._config.walkSpeed) : 0;\n\n // --- Accelerate/decelerate ---\n let currentSpeed = cs.currentSpeed.get(entity);\n if (targetSpeed > currentSpeed) {\n currentSpeed = Math.min(currentSpeed + this._config.acceleration * dt, targetSpeed);\n } else if (targetSpeed < currentSpeed) {\n currentSpeed = Math.max(currentSpeed - this._config.deceleration * dt, targetSpeed);\n }\n cs.currentSpeed.set(entity, currentSpeed);\n\n // --- Compute movement direction (world space, already in moveInputX/Z) ---\n let dirX = 0, dirZ = 0;\n if (hasInput && currentSpeed > 0.001) {\n const invLen = 1 / MathOps.sqrt(inputLengthSq);\n dirX = moveX * invLen;\n dirZ = moveZ * invLen;\n }\n\n // --- Gravity ---\n let vertVel = cs.verticalVelocity.get(entity);\n const grounded = cs.grounded.get(entity) !== 0;\n if (!grounded) {\n vertVel -= this._config.gravity * dt;\n if (vertVel < -this._config.maxFallSpeed) vertVel = -this._config.maxFallSpeed;\n } else if (vertVel < 0) {\n vertVel = 0;\n }\n cs.verticalVelocity.set(entity, vertVel);\n\n // --- Desired displacement ---\n const desiredX = dirX * currentSpeed * dt;\n const desiredY = vertVel * dt;\n const desiredZ = dirZ * currentSpeed * dt;\n\n // --- KCC movement ---\n const kcc = this._kccManager.getForEntity(entity);\n if (!kcc) return;\n\n const colliderHandle = this._physicsRefs.colliderHandle.get(entity);\n const collider = this._worldManager.getCollider(colliderHandle);\n if (!collider) return;\n\n _desiredVec3.x = desiredX; _desiredVec3.y = desiredY; _desiredVec3.z = desiredZ;\n kcc.computeColliderMovement(collider, _desiredVec3);\n const computed = kcc.computedMovement();\n const newGrounded = kcc.computedGrounded();\n\n // --- Apply to Transform3d ---\n const posX = this._transform.positionX.get(entity) + computed.x;\n const posY = this._transform.positionY.get(entity) + computed.y;\n const posZ = this._transform.positionZ.get(entity) + computed.z;\n this._transform.positionX.set(entity, posX);\n this._transform.positionY.set(entity, posY);\n this._transform.positionZ.set(entity, posZ);\n\n // --- Sync to Rapier body ---\n const bodyHandle = this._physicsRefs.bodyHandle.get(entity);\n const body = this._worldManager.getBody(bodyHandle);\n if (body) {\n _posVec3.x = posX; _posVec3.y = posY; _posVec3.z = posZ;\n body.setNextKinematicTranslation(_posVec3);\n }\n\n // --- Update grounded ---\n cs.grounded.set(entity, newGrounded ? 1 : 0);\n if (newGrounded) {\n cs.jumpCount.set(entity, 0);\n if (vertVel < 0) {\n cs.verticalVelocity.set(entity, 0);\n }\n }\n\n // --- Character rotation = camera yaw ---\n const halfYaw = facingYaw * 0.5;\n const sinY = MathOps.sin(halfYaw);\n const cosY = MathOps.cos(halfYaw);\n this._transform.rotationX.set(entity, 0);\n this._transform.rotationY.set(entity, sinY);\n this._transform.rotationZ.set(entity, 0);\n this._transform.rotationW.set(entity, cosY);\n if (body) {\n _rotQuat.x = 0; _rotQuat.y = sinY; _rotQuat.z = 0; _rotQuat.w = cosY;\n body.setNextKinematicRotation(_rotQuat);\n }\n\n // --- Locomotion angle (movement direction relative to facing, for animation) ---\n if (hasInput && currentSpeed > 0.001) {\n const moveAngle = MathOps.atan2(dirX, dirZ);\n const locoAngle = MathOps.normalizeAngle(moveAngle - facingYaw);\n cs.locomotionAngle.set(entity, locoAngle);\n } else {\n cs.locomotionAngle.set(entity, 0);\n }\n }\n\n /**\n * Called from game systems (e.g. ApplyCharacterInputSystem) when jump is requested.\n * Checks grounded + jumpCount, applies vertical velocity.\n */\n public tryJump(entity: number): boolean {\n const cs = this._characterState;\n const grounded = cs.grounded.get(entity) !== 0;\n const jumpCount = cs.jumpCount.get(entity);\n\n if (!grounded && jumpCount >= this._config.maxJumps) return false;\n\n cs.verticalVelocity.set(entity, this._config.jumpForce);\n cs.jumpCount.set(entity, jumpCount + 1);\n cs.grounded.set(entity, 0);\n return true;\n }\n}\n"],"names":["MathOps","_desiredVec3","x","y","z","_posVec3","_rotQuat","w","AbstractCharacterControllerSystem","update","_tick","dt","_frameLengthSec","i","_filter","length","entity","entities","updateEntity","cs","_characterState","moveX","moveInputX","get","moveZ","moveInputZ","isSprinting","facingYaw","inputLengthSq","hasInput","targetSpeed","_config","runSpeed","walkSpeed","currentSpeed","Math","min","acceleration","max","deceleration","set","dirX","dirZ","invLen","sqrt","vertVel","verticalVelocity","grounded","gravity","maxFallSpeed","desiredX","desiredY","desiredZ","kcc","_kccManager","getForEntity","colliderHandle","_physicsRefs","collider","_worldManager","getCollider","computeColliderMovement","computed","computedMovement","newGrounded","computedGrounded","posX","_transform","positionX","posY","positionY","posZ","positionZ","bodyHandle","body","getBody","setNextKinematicTranslation","jumpCount","halfYaw","sinY","sin","cosY","cos","rotationX","rotationY","rotationZ","rotationW","setNextKinematicRotation","moveAngle","atan2","locoAngle","normalizeAngle","locomotionAngle","tryJump","maxJumps","jumpForce","constructor","_ecsConfig","frameLength"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":"5.9.3"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@lagless/character-controller-3d",
3
+ "version": "0.0.38",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/GbGr/lagless",
8
+ "directory": "libs/character-controller-3d"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ "./package.json": "./package.json",
16
+ ".": {
17
+ "@lagless/source": "./src/index.ts",
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "default": "./dist/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md"
26
+ ],
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "nx": {
31
+ "sourceRoot": "libs/character-controller-3d/src",
32
+ "targets": {
33
+ "build": {
34
+ "executor": "@nx/js:swc",
35
+ "outputs": [
36
+ "{options.outputPath}"
37
+ ],
38
+ "options": {
39
+ "outputPath": "libs/character-controller-3d/dist",
40
+ "main": "libs/character-controller-3d/src/index.ts",
41
+ "tsConfig": "libs/character-controller-3d/tsconfig.lib.json",
42
+ "stripLeadingPaths": true
43
+ }
44
+ }
45
+ }
46
+ },
47
+ "dependencies": {
48
+ "@swc/helpers": "~0.5.11",
49
+ "@lagless/core": "0.0.38",
50
+ "@lagless/math": "0.0.38",
51
+ "@lagless/physics3d": "0.0.38"
52
+ }
53
+ }