hytopia 0.1.54 → 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 (129) 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.collider.bounciness.md +13 -0
  15. package/docs/server.collider.bouncinesscombinerule.md +13 -0
  16. package/docs/server.collider.collisiongroups.md +13 -0
  17. package/docs/{server.collider.getfriction.md → server.collider.friction.md} +4 -10
  18. package/docs/server.collider.frictioncombinerule.md +13 -0
  19. package/docs/server.collider.md +112 -63
  20. package/docs/server.collider.relativeposition.md +13 -0
  21. package/docs/server.collider.relativerotation.md +13 -0
  22. package/docs/{server.collider.setrelativetranslation.md → server.collider.setrelativeposition.md} +6 -6
  23. package/docs/server.collideroptions.md +6 -6
  24. package/docs/server.collideroptions.relativeposition.md +13 -0
  25. package/docs/server.defaultcharactercontroller._constructor_.md +1 -17
  26. package/docs/server.defaultcharactercontroller.attach.md +53 -0
  27. package/docs/server.defaultcharactercontroller.md +18 -4
  28. package/docs/server.defaultcharactercontroller.spawn.md +53 -0
  29. package/docs/server.defaultcharactercontroller.tickwithplayerinput.md +17 -1
  30. package/docs/server.entity.md +0 -19
  31. package/docs/server.entity.setcharactercontroller.md +2 -2
  32. package/docs/server.entityeventpayload.md +2 -2
  33. package/docs/server.entityeventpayload.updateposition.entity.md +11 -0
  34. package/docs/{server.entityeventpayload.updatetranslation.md → server.entityeventpayload.updateposition.md} +5 -5
  35. package/docs/server.entityeventpayload.updateposition.position.md +11 -0
  36. package/docs/server.entityeventtype.md +4 -4
  37. package/docs/server.entityoptions.charactercontroller.md +13 -0
  38. package/docs/server.entityoptions.md +3 -3
  39. package/docs/server.md +1 -1
  40. package/docs/server.movecallback.md +2 -2
  41. package/docs/server.movecompletecallback.md +1 -1
  42. package/docs/server.rigidbody.additionalmass.md +13 -0
  43. package/docs/server.rigidbody.additionalsolveriterations.md +13 -0
  44. package/docs/server.rigidbody.angulardamping.md +13 -0
  45. package/docs/server.rigidbody.angularvelocity.md +13 -0
  46. package/docs/server.rigidbody.directionfromrotation.md +13 -0
  47. package/docs/server.rigidbody.dominancegroup.md +13 -0
  48. package/docs/server.rigidbody.effectiveangularinertia.md +13 -0
  49. package/docs/server.rigidbody.effectiveinversemass.md +13 -0
  50. package/docs/server.rigidbody.effectiveworldinverseprincipalangularinertiasqrt.md +13 -0
  51. package/docs/server.rigidbody.enabledpositions.md +13 -0
  52. package/docs/server.rigidbody.enabledrotations.md +13 -0
  53. package/docs/server.rigidbody.gravityscale.md +13 -0
  54. package/docs/server.rigidbody.inversemass.md +13 -0
  55. package/docs/server.rigidbody.inverseprincipalangularinertiasqrt.md +13 -0
  56. package/docs/server.rigidbody.lineardamping.md +13 -0
  57. package/docs/server.rigidbody.linearvelocity.md +13 -0
  58. package/docs/server.rigidbody.localcenterofmass.md +13 -0
  59. package/docs/server.rigidbody.lockallpositions.md +17 -0
  60. package/docs/{server.rigidbody.getmass.md → server.rigidbody.mass.md} +4 -10
  61. package/docs/server.rigidbody.md +339 -150
  62. package/docs/server.rigidbody.nextkinematicposition.md +13 -0
  63. package/docs/server.rigidbody.nextkinematicrotation.md +13 -0
  64. package/docs/server.rigidbody.position.md +13 -0
  65. package/docs/server.rigidbody.principalangularinertia.md +13 -0
  66. package/docs/server.rigidbody.principalangularinertialocalframe.md +13 -0
  67. package/docs/server.rigidbody.rotation.md +13 -0
  68. package/docs/{server.rigidbody.setenabledtranslations.md → server.rigidbody.setenabledpositions.md} +6 -6
  69. package/docs/{server.rigidbody.setnextkinematictranslation.md → server.rigidbody.setnextkinematicposition.md} +6 -6
  70. package/docs/{server.rigidbody.settranslation.md → server.rigidbody.setposition.md} +6 -6
  71. package/docs/server.rigidbody.softccdprediction.md +13 -0
  72. package/docs/server.rigidbody.type.md +13 -0
  73. package/docs/server.rigidbody.worldcenterofmass.md +13 -0
  74. package/docs/server.rigidbodyoptions.enabledpositions.md +13 -0
  75. package/docs/server.rigidbodyoptions.md +19 -19
  76. package/docs/server.rigidbodyoptions.position.md +13 -0
  77. package/examples/block-entity/index.ts +3 -3
  78. package/examples/character-controller/MyCharacterController.ts +190 -119
  79. package/examples/character-controller/index.ts +5 -10
  80. package/examples/custom-ui/index.ts +2 -2
  81. package/examples/payload-game/index.ts +17 -18
  82. package/package.json +1 -1
  83. package/server.api.json +1461 -1203
  84. package/server.d.ts +181 -252
  85. package/server.js +85 -85
  86. package/docs/server.basecharactercontroller._constructor_.md +0 -65
  87. package/docs/server.basecharactercontroller.createcolliders.md +0 -19
  88. package/docs/server.basecharactercontroller.entity.md +0 -13
  89. package/docs/server.collider.getbounciness.md +0 -19
  90. package/docs/server.collider.getbouncinesscombinerule.md +0 -19
  91. package/docs/server.collider.getcollisiongroups.md +0 -19
  92. package/docs/server.collider.getfrictioncombinerule.md +0 -19
  93. package/docs/server.collider.getrelativerotation.md +0 -19
  94. package/docs/server.collider.getrelativetranslation.md +0 -19
  95. package/docs/server.collideroptions.relativetranslation.md +0 -13
  96. package/docs/server.defaultcharactercontroller.createcolliders.md +0 -19
  97. package/docs/server.entity.createcustomcharactercontroller.md +0 -13
  98. package/docs/server.entityeventpayload.updatetranslation.entity.md +0 -11
  99. package/docs/server.entityeventpayload.updatetranslation.translation.md +0 -11
  100. package/docs/server.entityoptions.createcustomcharactercontroller.md +0 -13
  101. package/docs/server.rigidbody.getadditionalmass.md +0 -19
  102. package/docs/server.rigidbody.getadditionalsolveriterations.md +0 -19
  103. package/docs/server.rigidbody.getangulardamping.md +0 -19
  104. package/docs/server.rigidbody.getangularvelocity.md +0 -19
  105. package/docs/server.rigidbody.getdirectionfromrotation.md +0 -19
  106. package/docs/server.rigidbody.getdominancegroup.md +0 -19
  107. package/docs/server.rigidbody.geteffectiveangularinertia.md +0 -19
  108. package/docs/server.rigidbody.geteffectiveinversemass.md +0 -19
  109. package/docs/server.rigidbody.geteffectiveworldinverseprincipalangularinertiasqrt.md +0 -19
  110. package/docs/server.rigidbody.getenabledrotations.md +0 -19
  111. package/docs/server.rigidbody.getenabledtranslations.md +0 -19
  112. package/docs/server.rigidbody.getgravityscale.md +0 -19
  113. package/docs/server.rigidbody.getinversemass.md +0 -19
  114. package/docs/server.rigidbody.getinverseprincipalangularinertiasqrt.md +0 -19
  115. package/docs/server.rigidbody.getlineardamping.md +0 -19
  116. package/docs/server.rigidbody.getlinearvelocity.md +0 -19
  117. package/docs/server.rigidbody.getlocalcenterofmass.md +0 -19
  118. package/docs/server.rigidbody.getnextkinematicrotation.md +0 -19
  119. package/docs/server.rigidbody.getnextkinematictranslation.md +0 -19
  120. package/docs/server.rigidbody.getprincipalangularinertia.md +0 -19
  121. package/docs/server.rigidbody.getprincipalangularinertialocalframe.md +0 -19
  122. package/docs/server.rigidbody.getrotation.md +0 -19
  123. package/docs/server.rigidbody.getsoftccdprediction.md +0 -19
  124. package/docs/server.rigidbody.gettranslation.md +0 -19
  125. package/docs/server.rigidbody.gettype.md +0 -19
  126. package/docs/server.rigidbody.getworldcenterofmass.md +0 -19
  127. package/docs/server.rigidbody.lockalltranslations.md +0 -17
  128. package/docs/server.rigidbodyoptions.enabledtranslations.md +0 -13
  129. package/docs/server.rigidbodyoptions.translation.md +0 -13
@@ -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
  }
@@ -11,6 +11,10 @@ startServer(world => {
11
11
  // Uncomment this to visualize physics vertices, will cause noticable lag.
12
12
  // world.simulation.enableDebugRendering(true);
13
13
 
14
+ // Visualize raycasts, like block breaking for our
15
+ // character controller.
16
+ world.simulation.enableDebugRaycasting(true);
17
+
14
18
  world.loadMap(worldMap);
15
19
 
16
20
  world.onPlayerJoin = player => {
@@ -20,18 +24,9 @@ startServer(world => {
20
24
  modelUri: 'models/player.gltf',
21
25
  modelLoopedAnimations: [ 'idle' ],
22
26
  modelScale: 0.5,
27
+ characterController: new MyCharacterController(), // attach our character controller
23
28
  });
24
29
 
25
- // Assign a custom character controller factory to the player entity.
26
- // This must be assigned before spawning the entity.
27
- // createCustomCharacterController if set will be handled internally when
28
- // .spawn() is called.
29
- playerEntity.createCustomCharacterController = () => {
30
- console.log('Creating custom player entity character controller...');
31
-
32
- return new MyCharacterController(playerEntity);
33
- };
34
-
35
30
  playerEntity.spawn(world, { x: 0, y: 10, z: 0 });
36
31
  console.log('Spawned player entity!');
37
32
 
@@ -45,7 +45,7 @@ startServer(world => {
45
45
  const randomY = Math.random() * 13 + 2; // Random between 2 and 15
46
46
  const randomZ = Math.random() * 40 - 20; // Random between -20 and 20
47
47
 
48
- playerEntity.setTranslation({ x: randomX, y: randomY, z: randomZ });
48
+ playerEntity.setPosition({ x: randomX, y: randomY, z: randomZ });
49
49
  }
50
50
  };
51
51
  };
@@ -75,7 +75,7 @@ function updatePlayerList() {
75
75
  // For each player, return their username and current position
76
76
  return {
77
77
  username: player.username,
78
- position: entity.getTranslation(), // Gets x,y,z coordinate
78
+ position: entity.position, // Gets x,y,z coordinate
79
79
  };
80
80
  });
81
81
 
@@ -151,7 +151,7 @@ startServer(world => { // Perform our game setup logic in the startServer init c
151
151
  // physics engine we use where entities despawned to not trigger a collision
152
152
  // event for leaving a sensor. This is a workaround till a better solution is found.
153
153
  world.entityManager.getAllPlayerEntities(player).forEach(entity => {
154
- entity.setTranslation({ x: 0, y: 100, z: 0 });
154
+ entity.setPosition({ x: 0, y: 100, z: 0 });
155
155
  setTimeout(() => entity.despawn(), 50); // Despawn after a short delay so we step the physics after translating it so leaving the sensor registers.
156
156
  });
157
157
 
@@ -254,8 +254,8 @@ function spawnBullet(world: World, coordinate: Vector3Like, direction: Vector3Li
254
254
  // Apply knockback, the knockback effect is less if the spider is larger, and more if it is smaller
255
255
  // because of how the physics simulation applies forces relative to automatically calculated mass from the spider's
256
256
  // size
257
- const bulletDirection = bullet.getDirectionFromRotation();
258
- const mass = otherEntity.getMass();
257
+ const bulletDirection = bullet.directionFromRotation;
258
+ const mass = otherEntity.mass;
259
259
  const knockback = 14 * mass;
260
260
 
261
261
  otherEntity.applyImpulse({
@@ -266,7 +266,7 @@ function spawnBullet(world: World, coordinate: Vector3Like, direction: Vector3Li
266
266
 
267
267
  if (enemyHealth[otherEntity.id!] <= 0) {
268
268
  // YEET the spider before despawning it so it registers leaving the sensor
269
- otherEntity.setTranslation({ x: 0, y: 100, z: 0 });
269
+ otherEntity.setPosition({ x: 0, y: 100, z: 0 });
270
270
  setTimeout(() => { otherEntity.despawn(); }, 50); // Despawn after a short delay so we step the physics after translating it so leaving the sensor registers.
271
271
  }
272
272
 
@@ -293,7 +293,7 @@ function spawnPayloadEntity(world: World) {
293
293
  }
294
294
 
295
295
  payloadEntity = new Entity({
296
- createCustomCharacterController: (entity: Entity) => new SimpleCharacterController(entity),
296
+ characterController: new SimpleCharacterController(),
297
297
  name: 'Payload',
298
298
  modelUri: 'models/payload.gltf',
299
299
  modelScale: 0.7,
@@ -350,7 +350,7 @@ function spawnSpider(world: World, coordinate: Vector3Like) {
350
350
  const targetPlayers = new Set<PlayerEntity>();
351
351
 
352
352
  const spider = new Entity({
353
- createCustomCharacterController: (entity: Entity) => new SimpleCharacterController(entity),
353
+ characterController: new SimpleCharacterController(),
354
354
  name: 'Spider',
355
355
  modelUri: 'models/spider.gltf',
356
356
  modelLoopedAnimations: [ 'walk' ],
@@ -399,7 +399,7 @@ function spawnSpider(world: World, coordinate: Vector3Like) {
399
399
 
400
400
  spider.onEntityCollision = (spider: Entity, entity: Entity, started: boolean) => { // If the spider hits a player, deal damage and apply knockback
401
401
  if (started && entity instanceof PlayerEntity && entity.isSpawned) {
402
- const spiderDirection = spider.getDirectionFromRotation();
402
+ const spiderDirection = spider.directionFromRotation;
403
403
  const knockback = 4 * randomScaleMultiplier;
404
404
 
405
405
  entity.applyImpulse({
@@ -466,19 +466,19 @@ function onTickPathfindEnemy(entity: Entity, targetPlayers: Set<PlayerEntity>, s
466
466
  const targetPlayer = targetPlayers.values().next().value as PlayerEntity | undefined;
467
467
 
468
468
  enemyPathfindingTargets[entityId] = targetPlayer?.isSpawned
469
- ? targetPlayer.getTranslation()
470
- : payloadEntity.getTranslation();
469
+ ? targetPlayer.position
470
+ : payloadEntity.position;
471
471
 
472
472
  enemyPathfindAccumulators[entityId] -= PATHFIND_ACCUMULATOR_THRESHOLD;
473
473
 
474
474
  // Make the spider jump if its close to the target player
475
- const currentPosition = entity.getTranslation();
475
+ const currentPosition = entity.position;
476
476
  const dx = enemyPathfindingTargets[entityId].x - currentPosition.x;
477
477
  const dz = enemyPathfindingTargets[entityId].z - currentPosition.z;
478
478
  const distance = Math.sqrt(dx * dx + dz * dz);
479
479
 
480
480
  if (distance < 10) {
481
- const mass = entity.getMass();
481
+ const mass = entity.mass;
482
482
  entity.applyImpulse({ x: 0, y: (10 * Math.random() + 5) * mass, z: 0 });
483
483
  }
484
484
 
@@ -495,13 +495,12 @@ function onTickPathfindEnemy(entity: Entity, targetPlayers: Set<PlayerEntity>, s
495
495
  enemyPathfindAccumulators[entityId]++;
496
496
  }
497
497
 
498
- function onTickWithPlayerInput(this: DefaultCharacterController, input: PlayerInput, cameraOrientation: PlayerCameraOrientation, _deltaTimeMs: number) {
499
- if (!this.entity.world) return;
498
+ function onTickWithPlayerInput(this: DefaultCharacterController, entity: PlayerEntity, input: PlayerInput, cameraOrientation: PlayerCameraOrientation, _deltaTimeMs: number) {
499
+ if (!entity.world) return;
500
500
 
501
501
  if (input.ml) {
502
- const world = this.entity.world;
503
- const entity = this.entity;
504
- const direction = Vector3.fromVector3Like(entity.getDirectionFromRotation());
502
+ const world = entity.world;
503
+ const direction = Vector3.fromVector3Like(entity.directionFromRotation);
505
504
 
506
505
  direction.y = Math.sin(cameraOrientation.pitch);
507
506
 
@@ -513,10 +512,10 @@ function onTickWithPlayerInput(this: DefaultCharacterController, input: PlayerIn
513
512
  // Normalize the direction vector to unit length
514
513
  direction.normalize();
515
514
 
516
- this.entity.startModelOneshotAnimations([ 'shoot' ]);
515
+ entity.startModelOneshotAnimations([ 'shoot' ]);
517
516
 
518
517
  // Adjust bullet origin roughly for camera offset so crosshair is accurate
519
- const bulletOrigin = entity.getTranslation();
518
+ const bulletOrigin = entity.position;
520
519
  bulletOrigin.y += 0.65;
521
520
 
522
521
  const bullet = spawnBullet(world, bulletOrigin, direction);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hytopia",
3
- "version": "0.1.54",
3
+ "version": "0.1.56",
4
4
  "description": "The HYTOPIA SDK makes it easy for developers to create massively multiplayer games using JavaScript or TypeScript.",
5
5
  "main": "server.js",
6
6
  "bin": {