hytopia 0.1.55 → 0.1.56

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.
Files changed (37) hide show
  1. package/docs/server.basecharactercontroller.attach.md +53 -0
  2. package/docs/server.basecharactercontroller.despawn.md +53 -0
  3. package/docs/server.basecharactercontroller.detach.md +53 -0
  4. package/docs/server.basecharactercontroller.md +91 -28
  5. package/docs/server.basecharactercontroller.onattach.md +13 -0
  6. package/docs/server.basecharactercontroller.ondespawn.md +13 -0
  7. package/docs/server.basecharactercontroller.ondetach.md +13 -0
  8. package/docs/server.basecharactercontroller.onspawn.md +13 -0
  9. package/docs/server.basecharactercontroller.ontick.md +2 -2
  10. package/docs/server.basecharactercontroller.ontickwithplayerinput.md +2 -2
  11. package/docs/server.basecharactercontroller.spawn.md +53 -0
  12. package/docs/server.basecharactercontroller.tick.md +15 -1
  13. package/docs/server.basecharactercontroller.tickwithplayerinput.md +17 -1
  14. package/docs/server.defaultcharactercontroller._constructor_.md +1 -17
  15. package/docs/server.defaultcharactercontroller.attach.md +53 -0
  16. package/docs/server.defaultcharactercontroller.md +18 -4
  17. package/docs/server.defaultcharactercontroller.spawn.md +53 -0
  18. package/docs/server.defaultcharactercontroller.tickwithplayerinput.md +17 -1
  19. package/docs/server.entity.md +0 -19
  20. package/docs/server.entity.setcharactercontroller.md +2 -2
  21. package/docs/server.entityoptions.charactercontroller.md +13 -0
  22. package/docs/server.entityoptions.md +3 -3
  23. package/examples/block-entity/index.ts +1 -1
  24. package/examples/character-controller/MyCharacterController.ts +190 -119
  25. package/examples/character-controller/index.ts +5 -10
  26. package/examples/custom-ui/index.ts +1 -1
  27. package/examples/payload-game/index.ts +6 -7
  28. package/package.json +1 -1
  29. package/server.api.json +468 -176
  30. package/server.d.ts +71 -40
  31. package/server.js +81 -81
  32. package/docs/server.basecharactercontroller._constructor_.md +0 -65
  33. package/docs/server.basecharactercontroller.createcolliders.md +0 -19
  34. package/docs/server.basecharactercontroller.entity.md +0 -13
  35. package/docs/server.defaultcharactercontroller.createcolliders.md +0 -19
  36. package/docs/server.entity.createcustomcharactercontroller.md +0 -13
  37. package/docs/server.entityoptions.createcustomcharactercontroller.md +0 -13
@@ -54,7 +54,7 @@ Description
54
54
  </th></tr></thead>
55
55
  <tbody><tr><td>
56
56
 
57
- [(constructor)(entity, options)](./server.defaultcharactercontroller._constructor_.md)
57
+ [(constructor)(options)](./server.defaultcharactercontroller._constructor_.md)
58
58
 
59
59
 
60
60
  </td><td>
@@ -290,7 +290,7 @@ Description
290
290
  </th></tr></thead>
291
291
  <tbody><tr><td>
292
292
 
293
- [createColliders()](./server.defaultcharactercontroller.createcolliders.md)
293
+ [attach(entity)](./server.defaultcharactercontroller.attach.md)
294
294
 
295
295
 
296
296
  </td><td>
@@ -298,13 +298,27 @@ Description
298
298
 
299
299
  </td><td>
300
300
 
301
- Creates the colliders for the character controller, overriding the default implementation.
301
+ Called when the controller is attached to an entity.
302
302
 
303
303
 
304
304
  </td></tr>
305
305
  <tr><td>
306
306
 
307
- [tickWithPlayerInput(input, cameraOrientation, deltaTimeMs)](./server.defaultcharactercontroller.tickwithplayerinput.md)
307
+ [spawn(entity)](./server.defaultcharactercontroller.spawn.md)
308
+
309
+
310
+ </td><td>
311
+
312
+
313
+ </td><td>
314
+
315
+ Called when the controlled entity is spawned. In DefaultCharacterController, this function is used to create the colliders for the entity for wall and ground detection.
316
+
317
+
318
+ </td></tr>
319
+ <tr><td>
320
+
321
+ [tickWithPlayerInput(entity, input, cameraOrientation, deltaTimeMs)](./server.defaultcharactercontroller.tickwithplayerinput.md)
308
322
 
309
323
 
310
324
  </td><td>
@@ -0,0 +1,53 @@
1
+ <!-- Do not edit this file. It is automatically generated by API Documenter. -->
2
+
3
+ [Home](./index.md) &gt; [server](./server.md) &gt; [DefaultCharacterController](./server.defaultcharactercontroller.md) &gt; [spawn](./server.defaultcharactercontroller.spawn.md)
4
+
5
+ ## DefaultCharacterController.spawn() method
6
+
7
+ Called when the controlled entity is spawned. In DefaultCharacterController, this function is used to create the colliders for the entity for wall and ground detection.
8
+
9
+ **Signature:**
10
+
11
+ ```typescript
12
+ spawn(entity: Entity): void;
13
+ ```
14
+
15
+ ## Parameters
16
+
17
+ <table><thead><tr><th>
18
+
19
+ Parameter
20
+
21
+
22
+ </th><th>
23
+
24
+ Type
25
+
26
+
27
+ </th><th>
28
+
29
+ Description
30
+
31
+
32
+ </th></tr></thead>
33
+ <tbody><tr><td>
34
+
35
+ entity
36
+
37
+
38
+ </td><td>
39
+
40
+ [Entity](./server.entity.md)
41
+
42
+
43
+ </td><td>
44
+
45
+ The entity that is spawned.
46
+
47
+
48
+ </td></tr>
49
+ </tbody></table>
50
+ **Returns:**
51
+
52
+ void
53
+
@@ -9,7 +9,7 @@ Ticks the player movement for the character controller, overriding the default i
9
9
  **Signature:**
10
10
 
11
11
  ```typescript
12
- tickWithPlayerInput(input: PlayerInput, cameraOrientation: PlayerCameraOrientation, deltaTimeMs: number): void;
12
+ tickWithPlayerInput(entity: PlayerEntity, input: PlayerInput, cameraOrientation: PlayerCameraOrientation, deltaTimeMs: number): void;
13
13
  ```
14
14
 
15
15
  ## Parameters
@@ -32,6 +32,22 @@ Description
32
32
  </th></tr></thead>
33
33
  <tbody><tr><td>
34
34
 
35
+ entity
36
+
37
+
38
+ </td><td>
39
+
40
+ [PlayerEntity](./server.playerentity.md)
41
+
42
+
43
+ </td><td>
44
+
45
+ The entity to tick.
46
+
47
+
48
+ </td></tr>
49
+ <tr><td>
50
+
35
51
  input
36
52
 
37
53
 
@@ -164,25 +164,6 @@ The URI or path to the texture to be used, if this is set, the entity is a block
164
164
  The character controller for the entity.
165
165
 
166
166
 
167
- </td></tr>
168
- <tr><td>
169
-
170
- [createCustomCharacterController?](./server.entity.createcustomcharactercontroller.md)
171
-
172
-
173
- </td><td>
174
-
175
-
176
- </td><td>
177
-
178
- (entity: [Entity](./server.entity.md)<!-- -->) =&gt; [BaseCharacterController](./server.basecharactercontroller.md)
179
-
180
-
181
- </td><td>
182
-
183
- _(Optional)_ A function that creates a custom character controller for the entity when it spawns.
184
-
185
-
186
167
  </td></tr>
187
168
  <tr><td>
188
169
 
@@ -9,7 +9,7 @@ Sets the character controller for the entity.
9
9
  **Signature:**
10
10
 
11
11
  ```typescript
12
- setCharacterController(characterController: BaseCharacterController): void;
12
+ setCharacterController(characterController: BaseCharacterController | undefined): void;
13
13
  ```
14
14
 
15
15
  ## Parameters
@@ -37,7 +37,7 @@ characterController
37
37
 
38
38
  </td><td>
39
39
 
40
- [BaseCharacterController](./server.basecharactercontroller.md)
40
+ [BaseCharacterController](./server.basecharactercontroller.md) \| undefined
41
41
 
42
42
 
43
43
  </td><td>
@@ -0,0 +1,13 @@
1
+ <!-- Do not edit this file. It is automatically generated by API Documenter. -->
2
+
3
+ [Home](./index.md) &gt; [server](./server.md) &gt; [EntityOptions](./server.entityoptions.md) &gt; [characterController](./server.entityoptions.charactercontroller.md)
4
+
5
+ ## EntityOptions.characterController property
6
+
7
+ The character controller to use for the entity.
8
+
9
+ **Signature:**
10
+
11
+ ```typescript
12
+ characterController?: BaseCharacterController;
13
+ ```
@@ -75,7 +75,7 @@ _(Optional)_ The texture uri of a entity if the entity is a block entity, if set
75
75
  </td></tr>
76
76
  <tr><td>
77
77
 
78
- [createCustomCharacterController?](./server.entityoptions.createcustomcharactercontroller.md)
78
+ [characterController?](./server.entityoptions.charactercontroller.md)
79
79
 
80
80
 
81
81
  </td><td>
@@ -83,12 +83,12 @@ _(Optional)_ The texture uri of a entity if the entity is a block entity, if set
83
83
 
84
84
  </td><td>
85
85
 
86
- (entity: [Entity](./server.entity.md)<!-- -->) =&gt; [BaseCharacterController](./server.basecharactercontroller.md)
86
+ [BaseCharacterController](./server.basecharactercontroller.md)
87
87
 
88
88
 
89
89
  </td><td>
90
90
 
91
- _(Optional)_ A function that creates a custom character controller for the entity when it spawns.
91
+ _(Optional)_ The character controller to use for the entity.
92
92
 
93
93
 
94
94
  </td></tr>
@@ -142,7 +142,7 @@ startServer(world => {
142
142
  blockHalfExtents: { x: 0.5, y: 0.5, z: 0.5 },
143
143
  // attach a simple character controller so we can pathfind,
144
144
  // the character controller will be created and associated when we spawn the entity
145
- createCustomCharacterController: entity => new SimpleCharacterController(entity),
145
+ characterController: new SimpleCharacterController(),
146
146
  });
147
147
 
148
148
  blockPet.spawn(world, { x: 0, y: 10, z: -6 });
@@ -1,41 +1,102 @@
1
1
  import {
2
2
  Audio,
3
3
  BaseCharacterController,
4
- CollisionGroup,
5
- Collider,
6
- Entity,
7
4
  ColliderShape,
8
5
  CoefficientCombineRule,
6
+ CollisionGroup,
7
+ Entity,
8
+ PlayerEntity,
9
9
  BlockType,
10
10
  } from 'hytopia';
11
11
 
12
12
  import type {
13
13
  PlayerInput,
14
14
  PlayerCameraOrientation,
15
- Vector3,
16
15
  } from 'hytopia';
17
16
 
17
+ /** Options for creating a MyCharacterController instance. @public */
18
+ export interface MyCharacterControllerOptions {
19
+ /** The upward velocity applied to the entity when it jumps. */
20
+ jumpVelocity?: number;
21
+
22
+ /** The normalized horizontal velocity applied to the entity when it runs. */
23
+ runVelocity?: number;
24
+
25
+ /** The normalized horizontal velocity applied to the entity when it walks. */
26
+ walkVelocity?: number;
27
+
28
+ /** A function allowing custom logic to determine if the entity can jump. */
29
+ canJump?: () => boolean;
30
+
31
+ /** A function allowing custom logic to determine if the entity can walk. */
32
+ canWalk?: () => boolean;
33
+
34
+ /** A function allowing custom logic to determine if the entity can run. */
35
+ canRun?: () => boolean;
36
+ }
37
+
38
+ /**
39
+ * A custom character controller implementation.
40
+ *
41
+ * @remarks
42
+ * This class extends {@link BaseCharacterController}
43
+ * and implements the default movement logic for a
44
+ * character entity.
45
+ *
46
+ * @public
47
+ */
18
48
  export default class MyCharacterController extends BaseCharacterController {
49
+ /** The upward velocity applied to the entity when it jumps. */
19
50
  public jumpVelocity: number = 10;
51
+
52
+ /** The normalized horizontal velocity applied to the entity when it runs. */
20
53
  public runVelocity: number = 8;
54
+
55
+ /** The normalized horizontal velocity applied to the entity when it walks. */
21
56
  public walkVelocity: number = 4;
22
57
 
23
- private _stepAudio: Audio;
24
- private _groundContactCount: number = 0;
25
- private _platform: Entity | undefined;
58
+ /**
59
+ * A function allowing custom logic to determine if the entity can walk.
60
+ * @param myCharacterController - The character controller instance.
61
+ * @returns Whether the entity of the character controller can walk.
62
+ */
63
+ public canWalk: (myCharacterController: MyCharacterController) => boolean = () => true;
26
64
 
27
- public constructor(entity: Entity) {
28
- super(entity);
65
+ /**
66
+ * A function allowing custom logic to determine if the entity can run.
67
+ * @param myCharacterController - The character controller instance.
68
+ * @returns Whether the entity of the character controller can run.
69
+ */
70
+ public canRun: (myCharacterController: MyCharacterController) => boolean = () => true;
29
71
 
30
- // Setup any audio or dependencies in the constructor.
31
- this._stepAudio = new Audio({
32
- uri: 'audio/sfx/step.wav',
33
- loop: true,
34
- volume: 0.1,
35
- attachedToEntity: this.entity,
36
- });
72
+ /**
73
+ * A function allowing custom logic to determine if the entity can jump.
74
+ * @param myCharacterController - The character controller instance.
75
+ * @returns Whether the entity of the character controller can jump.
76
+ */
77
+ public canJump: (myCharacterController: MyCharacterController) => boolean = () => true;
78
+
79
+ /** @internal */
80
+ private _stepAudio: Audio | undefined;
81
+
82
+ /** @internal */
83
+ private _groundContactCount: number = 0;
84
+
85
+ /** @internal */
86
+ private _platform: Entity | undefined;
37
87
 
38
- this.entity.lockAllRotations(); // prevent physics from applying rotation to the entity.
88
+ /**
89
+ * @param options - Options for the controller.
90
+ */
91
+ public constructor(options: MyCharacterControllerOptions = {}) {
92
+ super();
93
+
94
+ this.jumpVelocity = options.jumpVelocity ?? this.jumpVelocity;
95
+ this.runVelocity = options.runVelocity ?? this.runVelocity;
96
+ this.walkVelocity = options.walkVelocity ?? this.walkVelocity;
97
+ this.canWalk = options.canWalk ?? this.canWalk;
98
+ this.canRun = options.canRun ?? this.canRun;
99
+ this.canJump = options.canJump ?? this.canJump;
39
100
  }
40
101
 
41
102
  /** Whether the entity is grounded. */
@@ -48,41 +109,51 @@ export default class MyCharacterController extends BaseCharacterController {
48
109
  public get platform(): Entity | undefined { return this._platform; }
49
110
 
50
111
  /**
51
- * Create the colliders for the character controller.
112
+ * Called when the controller is attached to an entity.
113
+ * @param entity - The entity to attach the controller to.
52
114
  */
53
- public createColliders(): Collider[] {
54
- if (!this.entity.isSpawned) {
55
- throw new Error('CharacterController.createSensorColliders(): Entity is not spawned!');
56
- }
115
+ public attach(entity: Entity) {
116
+ this._stepAudio = new Audio({
117
+ uri: 'audio/sfx/step.wav',
118
+ loop: true,
119
+ volume: 0.1,
120
+ attachedToEntity: entity,
121
+ });
122
+
123
+ entity.lockAllRotations(); // prevent physics from applying rotation to the entity, we can still explicitly set it.
124
+ };
57
125
 
58
- const colliders: Collider[] = [];
126
+ /**
127
+ * Called when the controlled entity is spawned.
128
+ * In MyCharacterController, this function is used to create
129
+ * the colliders for the entity for wall and ground detection.
130
+ * @param entity - The entity that is spawned.
131
+ */
132
+ public spawn(entity: Entity) {
133
+ if (!entity.isSpawned) {
134
+ throw new Error('CharacterController.createColliders(): Entity is not spawned!');
135
+ }
59
136
 
60
- /**
61
- * Our ground sensor detects when we're on the ground.
62
- * It assumes a cylinder shape and is positioned manually
63
- * relative to the default entity rigid body as defined
64
- * by the DEFAULT_ENTITY_RIGID_BODY_OPTIONS constant of
65
- * the hytopia package.
66
- */
67
- colliders.push(new Collider({
137
+ // Ground sensor
138
+ entity.createAndAddChildColliderToSimulation({
68
139
  shape: ColliderShape.CYLINDER,
69
- radius: 0.30,
140
+ radius: 0.23,
70
141
  halfHeight: 0.125,
71
142
  collisionGroups: {
72
143
  belongsTo: [ CollisionGroup.ENTITY_SENSOR ],
73
144
  collidesWith: [ CollisionGroup.BLOCK, CollisionGroup.ENTITY ],
74
145
  },
75
146
  isSensor: true,
76
- relativeTranslation: { x: 0, y: -0.75, z: 0 },
147
+ relativePosition: { x: 0, y: -0.75, z: 0 },
77
148
  tag: 'groundSensor',
78
149
  onCollision: (_other: BlockType | Entity, started: boolean) => {
79
150
  // Ground contact
80
151
  this._groundContactCount += started ? 1 : -1;
81
152
 
82
- if (!this._groundContactCount) { // Trigger animations
83
- this.entity.startModelOneshotAnimations([ 'jump_loop' ]);
153
+ if (!this._groundContactCount) {
154
+ entity.startModelOneshotAnimations([ 'jump_loop' ]);
84
155
  } else {
85
- this.entity.stopModelAnimations([ 'jump_loop' ]);
156
+ entity.stopModelAnimations([ 'jump_loop' ]);
86
157
  }
87
158
 
88
159
  // Platform contact
@@ -94,15 +165,11 @@ export default class MyCharacterController extends BaseCharacterController {
94
165
  this._platform = undefined;
95
166
  }
96
167
  },
97
- }));
98
-
99
- /**
100
- * A wall collider slightly larger than our player hitbox with
101
- * a collision group that only collides with blocks. This prevent
102
- * sticking and friction to blocks as a player moves, creating
103
- * a smooth slide effect on walls that we jump into, etc.
104
- */
105
- colliders.push(new Collider({
168
+ });
169
+
170
+
171
+ // Wall collider
172
+ entity.createAndAddChildColliderToSimulation({
106
173
  shape: ColliderShape.CAPSULE,
107
174
  halfHeight: 0.30,
108
175
  radius: 0.37,
@@ -113,101 +180,109 @@ export default class MyCharacterController extends BaseCharacterController {
113
180
  friction: 0,
114
181
  frictionCombineRule: CoefficientCombineRule.Min,
115
182
  tag: 'wallCollider',
116
- }));
117
-
118
- return colliders;
119
- }
183
+ });
184
+ };
120
185
 
121
186
  /**
122
- * Handles movement of the entity based on player's input
123
- * each tick. tickPlayerMovement is called internally if the entity
124
- * is of the PlayerEntity class.
187
+ * Ticks the player movement for the character controller,
188
+ * overriding the default implementation.
189
+ *
190
+ * @param entity - The entity to tick.
191
+ * @param input - The current input state of the player.
192
+ * @param cameraOrientation - The current camera orientation state of the player.
193
+ * @param deltaTimeMs - The delta time in milliseconds since the last tick.
125
194
  */
126
- public tickWithPlayerInput(input: PlayerInput, cameraOrientation: PlayerCameraOrientation, deltaTimeMs: number) {
127
- if (!this.entity.isSpawned || !this.entity.world) return; // type guard.
195
+ public tickWithPlayerInput(entity: PlayerEntity, input: PlayerInput, cameraOrientation: PlayerCameraOrientation, deltaTimeMs: number) {
196
+ if (!entity.isSpawned || !entity.world) return;
128
197
 
129
- super.tickWithPlayerInput(input, cameraOrientation, deltaTimeMs);
198
+ super.tickWithPlayerInput(entity, input, cameraOrientation, deltaTimeMs);
130
199
 
131
- const { w, a, s, d, sp, sh, ml, mr } = input; // See PlayerInput type for all possible inputs.
132
- const { yaw } = cameraOrientation; // Camera/perspectie orientation of player.
133
- const currentVelocity = this.entity.getLinearVelocity();
200
+ const { w, a, s, d, sp, sh, ml } = input;
201
+ const { yaw } = cameraOrientation;
202
+ const currentVelocity = entity.linearVelocity;
134
203
  const targetVelocities = { x: 0, y: 0, z: 0 };
135
204
  const isRunning = sh;
136
205
 
137
- // Handle movement animations if relevant.
138
- if (w || a || s || d) {
206
+ // Temporary, animations
207
+ if (this.isGrounded && (w || a || s || d)) {
139
208
  if (isRunning) {
140
- // We stop irrelevant animations to prevent blending issues.
141
- this.entity.stopModelAnimations(Array.from(this.entity.modelLoopedAnimations).filter(v => v !== 'run'));
142
- // If run is already playing, internally startModelLoopedAnimations will do nothing.
143
- this.entity.startModelLoopedAnimations([ 'run' ]);
144
- // Manually set a playback rate of our step audio to audibly sync to our walk speed.
145
- this._stepAudio.setPlaybackRate(0.83);
209
+ entity.stopModelAnimations(Array.from(entity.modelLoopedAnimations).filter(v => v !== 'run'));
210
+ entity.startModelLoopedAnimations([ 'run' ]);
211
+ this._stepAudio?.setPlaybackRate(0.83);
146
212
  } else {
147
- this.entity.stopModelAnimations(Array.from(this.entity.modelLoopedAnimations).filter(v => v !== 'walk'));
148
- this.entity.startModelLoopedAnimations([ 'walk' ]);
149
- this._stepAudio.setPlaybackRate(0.5);
213
+ entity.stopModelAnimations(Array.from(entity.modelLoopedAnimations).filter(v => v !== 'walk'));
214
+ entity.startModelLoopedAnimations([ 'walk' ]);
215
+ this._stepAudio?.setPlaybackRate(0.5);
150
216
  }
151
217
 
152
- this._stepAudio.play(this.entity.world, !this._stepAudio.isPlaying);
218
+ this._stepAudio?.play(entity.world, !this._stepAudio?.isPlaying);
153
219
  } else {
154
- this._stepAudio.pause();
155
- this.entity.stopModelAnimations(Array.from(this.entity.modelLoopedAnimations).filter(v => v !== 'idle'));
156
- this.entity.startModelLoopedAnimations([ 'idle' ]);
220
+ this._stepAudio?.pause();
221
+ entity.stopModelAnimations(Array.from(entity.modelLoopedAnimations).filter(v => v !== 'idle'));
222
+ entity.startModelLoopedAnimations([ 'idle' ]);
157
223
  }
158
224
 
159
- // Play a simple interact animation on left mouse click then clear the input.
160
225
  if (ml) {
161
- this.entity.startModelOneshotAnimations([ 'simple_interact' ]);
162
- input.ml = false;
163
- }
226
+ entity.startModelOneshotAnimations([ 'simple_interact' ]);
227
+
228
+ // break a block
229
+ const ray = entity.world.simulation.raycast(
230
+ entity.position,
231
+ entity.player.camera.facingDirection,
232
+ 10,
233
+ { filterExcludeRigidBody: entity.rawRigidBody },
234
+ );
235
+
236
+ if (ray?.hitBlock) {
237
+ // Remove the block
238
+ entity.world.chunkLattice.setBlock(ray.hitBlock.globalCoordinate, 0);
239
+ }
164
240
 
165
- // Rocket the player up on mouse right click.
166
- if (mr) {
167
- targetVelocities.y = 20;
168
- input.mr = false;
241
+ input.ml = false;
169
242
  }
170
243
 
171
244
  // Calculate target horizontal velocities (run/walk)
172
- const velocity = isRunning ? this.runVelocity : this.walkVelocity;
245
+ if ((isRunning && this.canRun(this)) || (!isRunning && this.canWalk(this))) {
246
+ const velocity = isRunning ? this.runVelocity : this.walkVelocity;
173
247
 
174
- if (w) {
175
- targetVelocities.x -= velocity * Math.sin(yaw);
176
- targetVelocities.z -= velocity * Math.cos(yaw);
177
- }
178
-
179
- if (s) {
180
- targetVelocities.x += velocity * Math.sin(yaw);
181
- targetVelocities.z += velocity * Math.cos(yaw);
182
- }
183
-
184
- if (a) {
185
- targetVelocities.x -= velocity * Math.cos(yaw);
186
- targetVelocities.z += velocity * Math.sin(yaw);
187
- }
188
-
189
- if (d) {
190
- targetVelocities.x += velocity * Math.cos(yaw);
191
- targetVelocities.z -= velocity * Math.sin(yaw);
192
- }
248
+ if (w) {
249
+ targetVelocities.x -= velocity * Math.sin(yaw);
250
+ targetVelocities.z -= velocity * Math.cos(yaw);
251
+ }
252
+
253
+ if (s) {
254
+ targetVelocities.x += velocity * Math.sin(yaw);
255
+ targetVelocities.z += velocity * Math.cos(yaw);
256
+ }
257
+
258
+ if (a) {
259
+ targetVelocities.x -= velocity * Math.cos(yaw);
260
+ targetVelocities.z += velocity * Math.sin(yaw);
261
+ }
262
+
263
+ if (d) {
264
+ targetVelocities.x += velocity * Math.cos(yaw);
265
+ targetVelocities.z -= velocity * Math.sin(yaw);
266
+ }
193
267
 
194
- // Normalize for diagonals
195
- const length = Math.sqrt(targetVelocities.x * targetVelocities.x + targetVelocities.z * targetVelocities.z);
196
- if (length > velocity) {
197
- const factor = velocity / length;
198
- targetVelocities.x *= factor;
199
- targetVelocities.z *= factor;
268
+ // Normalize for diagonals
269
+ const length = Math.sqrt(targetVelocities.x * targetVelocities.x + targetVelocities.z * targetVelocities.z);
270
+ if (length > velocity) {
271
+ const factor = velocity / length;
272
+ targetVelocities.x *= factor;
273
+ targetVelocities.z *= factor;
274
+ }
200
275
  }
201
276
 
202
277
  // Calculate target vertical velocity (jump)
203
- if (sp) {
204
- if (this.isGrounded && currentVelocity.y <= 3) {
278
+ if (sp && this.canJump(this)) {
279
+ if (this.isGrounded && currentVelocity.y > -0.001 && currentVelocity.y <= 3) {
205
280
  targetVelocities.y = this.jumpVelocity;
206
281
  }
207
282
  }
208
283
 
209
284
  // Apply impulse relative to target velocities, taking platform velocity into account
210
- const platformVelocity = this._platform ? this._platform.getLinearVelocity() : { x: 0, y: 0, z: 0 };
285
+ const platformVelocity = this._platform ? this._platform.linearVelocity : { x: 0, y: 0, z: 0 };
211
286
  const deltaVelocities = {
212
287
  x: targetVelocities.x - currentVelocity.x + platformVelocity.x,
213
288
  y: targetVelocities.y + platformVelocity.y,
@@ -219,11 +294,11 @@ export default class MyCharacterController extends BaseCharacterController {
219
294
  Math.abs(currentVelocity.y) > this.jumpVelocity ||
220
295
  Math.abs(currentVelocity.z) > this.runVelocity;
221
296
 
222
- if (!hasExternalVelocity) { // allow external velocities like impulses to resolve, otherwise our deltas will cancel them out.
297
+ if (!hasExternalVelocity) { // allow external velocities to resolve, otherwise our deltas will cancel them out.
223
298
  if (Object.values(deltaVelocities).some(v => v !== 0)) {
224
- const mass = this.entity.getMass();
299
+ const mass = entity.mass;
225
300
 
226
- this.entity.applyImpulse({ // multiply by mass for the impulse to result in applying the correct target velocity
301
+ entity.applyImpulse({ // multiply by mass for the impulse to result in applying the correct target velocity
227
302
  x: deltaVelocities.x * mass,
228
303
  y: deltaVelocities.y * mass,
229
304
  z: deltaVelocities.z * mass,
@@ -231,11 +306,11 @@ export default class MyCharacterController extends BaseCharacterController {
231
306
  }
232
307
  }
233
308
 
234
- // Apply rotation relative to camera orientation.
309
+ // Apply rotation
235
310
  if (yaw !== undefined) {
236
311
  const halfYaw = yaw / 2;
237
312
 
238
- this.entity.setRotation({
313
+ entity.setRotation({
239
314
  x: 0,
240
315
  y: Math.fround(Math.sin(halfYaw)),
241
316
  z: 0,
@@ -243,8 +318,4 @@ export default class MyCharacterController extends BaseCharacterController {
243
318
  });
244
319
  }
245
320
  }
246
-
247
- public tickPathfindingMovement(_destination: Vector3, _deltaTimeMs: number) {
248
- console.log('Non-player pathfinding not implemented!');
249
- }
250
321
  }