hytopia 0.1.55 → 0.1.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/boilerplate/index.ts +1 -1
- package/docs/server.baseentitycontroller.attach.md +53 -0
- package/docs/server.baseentitycontroller.despawn.md +53 -0
- package/docs/server.baseentitycontroller.detach.md +53 -0
- package/docs/server.baseentitycontroller.md +260 -0
- package/docs/server.baseentitycontroller.onattach.md +13 -0
- package/docs/server.baseentitycontroller.ondespawn.md +13 -0
- package/docs/server.baseentitycontroller.ondetach.md +13 -0
- package/docs/server.baseentitycontroller.onspawn.md +13 -0
- package/docs/server.baseentitycontroller.ontick.md +13 -0
- package/docs/server.baseentitycontroller.ontickwithplayerinput.md +13 -0
- package/docs/server.baseentitycontroller.spawn.md +53 -0
- package/docs/server.baseentitycontroller.tick.md +67 -0
- package/docs/{server.basecharactercontroller.tickwithplayerinput.md → server.baseentitycontroller.tickwithplayerinput.md} +20 -4
- package/docs/server.entity.controller.md +13 -0
- package/docs/server.entity.md +34 -18
- package/docs/server.entity.modelanimationsplaybackrate.md +13 -0
- package/docs/server.entity.setcontroller.md +53 -0
- package/docs/server.entity.setmodelanimationsplaybackrate.md +57 -0
- package/docs/server.entityeventpayload.md +9 -0
- package/docs/server.entityeventpayload.setmodelanimationsplaybackrate.entity.md +11 -0
- package/docs/server.entityeventpayload.setmodelanimationsplaybackrate.md +70 -0
- package/docs/server.entityeventpayload.setmodelanimationsplaybackrate.playbackrate.md +11 -0
- package/docs/server.entityeventtype.md +14 -0
- package/docs/server.entityoptions.controller.md +13 -0
- package/docs/server.entityoptions.md +22 -3
- package/docs/server.entityoptions.modelanimationsplaybackrate.md +13 -0
- package/docs/server.facecallback.md +1 -1
- package/docs/server.facecompletecallback.md +1 -1
- package/docs/server.faceoptions.md +1 -1
- package/docs/server.md +27 -27
- package/docs/server.movecallback.md +1 -1
- package/docs/server.movecompletecallback.md +1 -1
- package/docs/server.moveoptions.md +1 -1
- package/docs/server.playerentity.md +1 -1
- package/docs/server.playerentitycontroller._constructor_.md +49 -0
- package/docs/server.playerentitycontroller.attach.md +53 -0
- package/docs/server.playerentitycontroller.canjump.md +13 -0
- package/docs/server.playerentitycontroller.canrun.md +13 -0
- package/docs/server.playerentitycontroller.canwalk.md +13 -0
- package/docs/server.playerentitycontroller.isgrounded.md +13 -0
- package/docs/server.playerentitycontroller.isonplatform.md +13 -0
- package/docs/server.playerentitycontroller.jumpvelocity.md +13 -0
- package/docs/server.playerentitycontroller.md +331 -0
- package/docs/server.playerentitycontroller.platform.md +13 -0
- package/docs/server.playerentitycontroller.runvelocity.md +13 -0
- package/docs/server.playerentitycontroller.spawn.md +53 -0
- package/docs/{server.defaultcharactercontroller.tickwithplayerinput.md → server.playerentitycontroller.tickwithplayerinput.md} +20 -4
- package/docs/server.playerentitycontroller.walkvelocity.md +13 -0
- package/docs/server.playerentitycontrolleroptions.canjump.md +13 -0
- package/docs/server.playerentitycontrolleroptions.canrun.md +13 -0
- package/docs/server.playerentitycontrolleroptions.canwalk.md +13 -0
- package/docs/server.playerentitycontrolleroptions.jumpvelocity.md +13 -0
- package/docs/{server.defaultcharactercontrolleroptions.md → server.playerentitycontrolleroptions.md} +10 -10
- package/docs/server.playerentitycontrolleroptions.runvelocity.md +13 -0
- package/docs/server.playerentitycontrolleroptions.walkvelocity.md +13 -0
- package/docs/server.playerui.lockpointer.md +53 -0
- package/docs/server.playerui.md +14 -0
- package/docs/server.playeruieventpayload.lockpointer.lock.md +11 -0
- package/docs/server.playeruieventpayload.lockpointer.md +70 -0
- package/docs/server.playeruieventpayload.lockpointer.playerui.md +11 -0
- package/docs/server.playeruieventpayload.md +9 -0
- package/docs/server.playeruieventtype.md +14 -0
- package/docs/{server.simplecharactercontroller.face.md → server.simpleentitycontroller.face.md} +2 -2
- package/docs/{server.simplecharactercontroller.md → server.simpleentitycontroller.md} +11 -13
- package/docs/{server.simplecharactercontroller.move.md → server.simpleentitycontroller.move.md} +2 -2
- package/examples/block-entity/index.ts +7 -7
- package/examples/custom-ui/index.ts +1 -1
- package/examples/entity-controller/MyEntityController.ts +321 -0
- package/examples/entity-controller/README.md +4 -0
- package/examples/{character-controller → entity-controller}/index.ts +6 -11
- package/examples/{character-controller → entity-controller}/package.json +1 -1
- package/examples/payload-game/index.ts +18 -19
- package/package.json +1 -1
- package/server.api.json +4129 -3498
- package/server.d.ts +229 -167
- package/server.js +85 -85
- package/docs/server.basecharactercontroller._constructor_.md +0 -65
- package/docs/server.basecharactercontroller.createcolliders.md +0 -19
- package/docs/server.basecharactercontroller.entity.md +0 -13
- package/docs/server.basecharactercontroller.md +0 -197
- package/docs/server.basecharactercontroller.ontick.md +0 -13
- package/docs/server.basecharactercontroller.ontickwithplayerinput.md +0 -13
- package/docs/server.basecharactercontroller.tick.md +0 -53
- package/docs/server.defaultcharactercontroller._constructor_.md +0 -65
- package/docs/server.defaultcharactercontroller.canjump.md +0 -13
- package/docs/server.defaultcharactercontroller.canrun.md +0 -13
- package/docs/server.defaultcharactercontroller.canwalk.md +0 -13
- package/docs/server.defaultcharactercontroller.createcolliders.md +0 -19
- package/docs/server.defaultcharactercontroller.isgrounded.md +0 -13
- package/docs/server.defaultcharactercontroller.isonplatform.md +0 -13
- package/docs/server.defaultcharactercontroller.jumpvelocity.md +0 -13
- package/docs/server.defaultcharactercontroller.md +0 -319
- package/docs/server.defaultcharactercontroller.platform.md +0 -13
- package/docs/server.defaultcharactercontroller.runvelocity.md +0 -13
- package/docs/server.defaultcharactercontroller.walkvelocity.md +0 -13
- package/docs/server.defaultcharactercontrolleroptions.canjump.md +0 -13
- package/docs/server.defaultcharactercontrolleroptions.canrun.md +0 -13
- package/docs/server.defaultcharactercontrolleroptions.canwalk.md +0 -13
- package/docs/server.defaultcharactercontrolleroptions.jumpvelocity.md +0 -13
- package/docs/server.defaultcharactercontrolleroptions.runvelocity.md +0 -13
- package/docs/server.defaultcharactercontrolleroptions.walkvelocity.md +0 -13
- package/docs/server.entity.charactercontroller.md +0 -13
- package/docs/server.entity.createcustomcharactercontroller.md +0 -13
- package/docs/server.entity.setcharactercontroller.md +0 -53
- package/docs/server.entityoptions.createcustomcharactercontroller.md +0 -13
- package/examples/character-controller/MyCharacterController.ts +0 -250
- package/examples/character-controller/README.md +0 -4
- /package/examples/{character-controller → entity-controller}/assets/map.json +0 -0
@@ -0,0 +1,321 @@
|
|
1
|
+
import {
|
2
|
+
Audio,
|
3
|
+
BaseEntityController,
|
4
|
+
ColliderShape,
|
5
|
+
CoefficientCombineRule,
|
6
|
+
CollisionGroup,
|
7
|
+
Entity,
|
8
|
+
PlayerEntity,
|
9
|
+
BlockType,
|
10
|
+
} from 'hytopia';
|
11
|
+
|
12
|
+
import type {
|
13
|
+
PlayerInput,
|
14
|
+
PlayerCameraOrientation,
|
15
|
+
} from 'hytopia';
|
16
|
+
|
17
|
+
/** Options for creating a MyEntityController instance. @public */
|
18
|
+
export interface MyEntityControllerOptions {
|
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 entity controller implementation.
|
40
|
+
*
|
41
|
+
* @remarks
|
42
|
+
* This class extends {@link BaseEntityController}
|
43
|
+
* and implements the default movement logic for a
|
44
|
+
* entity.
|
45
|
+
*
|
46
|
+
* @public
|
47
|
+
*/
|
48
|
+
export default class MyEntityController extends BaseEntityController {
|
49
|
+
/** The upward velocity applied to the entity when it jumps. */
|
50
|
+
public jumpVelocity: number = 10;
|
51
|
+
|
52
|
+
/** The normalized horizontal velocity applied to the entity when it runs. */
|
53
|
+
public runVelocity: number = 8;
|
54
|
+
|
55
|
+
/** The normalized horizontal velocity applied to the entity when it walks. */
|
56
|
+
public walkVelocity: number = 4;
|
57
|
+
|
58
|
+
/**
|
59
|
+
* A function allowing custom logic to determine if the entity can walk.
|
60
|
+
* @param myEntityController - The entity controller instance.
|
61
|
+
* @returns Whether the entity of the entity controller can walk.
|
62
|
+
*/
|
63
|
+
public canWalk: (myEntityController: MyEntityController) => boolean = () => true;
|
64
|
+
|
65
|
+
/**
|
66
|
+
* A function allowing custom logic to determine if the entity can run.
|
67
|
+
* @param myEntityController - The entity controller instance.
|
68
|
+
* @returns Whether the entity of the entity controller can run.
|
69
|
+
*/
|
70
|
+
public canRun: (myEntityController: MyEntityController) => boolean = () => true;
|
71
|
+
|
72
|
+
/**
|
73
|
+
* A function allowing custom logic to determine if the entity can jump.
|
74
|
+
* @param myEntityController - The entity controller instance.
|
75
|
+
* @returns Whether the entity of the entity controller can jump.
|
76
|
+
*/
|
77
|
+
public canJump: (myEntityController: MyEntityController) => 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;
|
87
|
+
|
88
|
+
/**
|
89
|
+
* @param options - Options for the controller.
|
90
|
+
*/
|
91
|
+
public constructor(options: MyEntityControllerOptions = {}) {
|
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;
|
100
|
+
}
|
101
|
+
|
102
|
+
/** Whether the entity is grounded. */
|
103
|
+
public get isGrounded(): boolean { return this._groundContactCount > 0; }
|
104
|
+
|
105
|
+
/** Whether the entity is on a platform, a platform is any entity with a kinematic rigid body. */
|
106
|
+
public get isOnPlatform(): boolean { return !!this._platform; }
|
107
|
+
|
108
|
+
/** The platform the entity is on, if any. */
|
109
|
+
public get platform(): Entity | undefined { return this._platform; }
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Called when the controller is attached to an entity.
|
113
|
+
* @param entity - The entity to attach the controller to.
|
114
|
+
*/
|
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
|
+
};
|
125
|
+
|
126
|
+
/**
|
127
|
+
* Called when the controlled entity is spawned.
|
128
|
+
* In MyEntityController, 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('MyEntityController.createColliders(): Entity is not spawned!');
|
135
|
+
}
|
136
|
+
|
137
|
+
// Ground sensor
|
138
|
+
entity.createAndAddChildColliderToSimulation({
|
139
|
+
shape: ColliderShape.CYLINDER,
|
140
|
+
radius: 0.23,
|
141
|
+
halfHeight: 0.125,
|
142
|
+
collisionGroups: {
|
143
|
+
belongsTo: [ CollisionGroup.ENTITY_SENSOR ],
|
144
|
+
collidesWith: [ CollisionGroup.BLOCK, CollisionGroup.ENTITY ],
|
145
|
+
},
|
146
|
+
isSensor: true,
|
147
|
+
relativePosition: { x: 0, y: -0.75, z: 0 },
|
148
|
+
tag: 'groundSensor',
|
149
|
+
onCollision: (_other: BlockType | Entity, started: boolean) => {
|
150
|
+
// Ground contact
|
151
|
+
this._groundContactCount += started ? 1 : -1;
|
152
|
+
|
153
|
+
if (!this._groundContactCount) {
|
154
|
+
entity.startModelOneshotAnimations([ 'jump_loop' ]);
|
155
|
+
} else {
|
156
|
+
entity.stopModelAnimations([ 'jump_loop' ]);
|
157
|
+
}
|
158
|
+
|
159
|
+
// Platform contact
|
160
|
+
if (!(_other instanceof Entity) || !_other.isKinematic) return;
|
161
|
+
|
162
|
+
if (started) {
|
163
|
+
this._platform = _other;
|
164
|
+
} else if (_other === this._platform && !started) {
|
165
|
+
this._platform = undefined;
|
166
|
+
}
|
167
|
+
},
|
168
|
+
});
|
169
|
+
|
170
|
+
|
171
|
+
// Wall collider
|
172
|
+
entity.createAndAddChildColliderToSimulation({
|
173
|
+
shape: ColliderShape.CAPSULE,
|
174
|
+
halfHeight: 0.30,
|
175
|
+
radius: 0.37,
|
176
|
+
collisionGroups: {
|
177
|
+
belongsTo: [ CollisionGroup.ENTITY_SENSOR ],
|
178
|
+
collidesWith: [ CollisionGroup.BLOCK ],
|
179
|
+
},
|
180
|
+
friction: 0,
|
181
|
+
frictionCombineRule: CoefficientCombineRule.Min,
|
182
|
+
tag: 'wallCollider',
|
183
|
+
});
|
184
|
+
};
|
185
|
+
|
186
|
+
/**
|
187
|
+
* Ticks the player movement for the entity 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.
|
194
|
+
*/
|
195
|
+
public tickWithPlayerInput(entity: PlayerEntity, input: PlayerInput, cameraOrientation: PlayerCameraOrientation, deltaTimeMs: number) {
|
196
|
+
if (!entity.isSpawned || !entity.world) return;
|
197
|
+
|
198
|
+
super.tickWithPlayerInput(entity, input, cameraOrientation, deltaTimeMs);
|
199
|
+
|
200
|
+
const { w, a, s, d, sp, sh, ml } = input;
|
201
|
+
const { yaw } = cameraOrientation;
|
202
|
+
const currentVelocity = entity.linearVelocity;
|
203
|
+
const targetVelocities = { x: 0, y: 0, z: 0 };
|
204
|
+
const isRunning = sh;
|
205
|
+
|
206
|
+
// Temporary, animations
|
207
|
+
if (this.isGrounded && (w || a || s || d)) {
|
208
|
+
if (isRunning) {
|
209
|
+
entity.stopModelAnimations(Array.from(entity.modelLoopedAnimations).filter(v => v !== 'run'));
|
210
|
+
entity.startModelLoopedAnimations([ 'run' ]);
|
211
|
+
this._stepAudio?.setPlaybackRate(0.83);
|
212
|
+
} else {
|
213
|
+
entity.stopModelAnimations(Array.from(entity.modelLoopedAnimations).filter(v => v !== 'walk'));
|
214
|
+
entity.startModelLoopedAnimations([ 'walk' ]);
|
215
|
+
this._stepAudio?.setPlaybackRate(0.5);
|
216
|
+
}
|
217
|
+
|
218
|
+
this._stepAudio?.play(entity.world, !this._stepAudio?.isPlaying);
|
219
|
+
} else {
|
220
|
+
this._stepAudio?.pause();
|
221
|
+
entity.stopModelAnimations(Array.from(entity.modelLoopedAnimations).filter(v => v !== 'idle'));
|
222
|
+
entity.startModelLoopedAnimations([ 'idle' ]);
|
223
|
+
}
|
224
|
+
|
225
|
+
if (ml) {
|
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
|
+
}
|
240
|
+
|
241
|
+
input.ml = false;
|
242
|
+
}
|
243
|
+
|
244
|
+
// Calculate target horizontal velocities (run/walk)
|
245
|
+
if ((isRunning && this.canRun(this)) || (!isRunning && this.canWalk(this))) {
|
246
|
+
const velocity = isRunning ? this.runVelocity : this.walkVelocity;
|
247
|
+
|
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
|
+
}
|
267
|
+
|
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
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
// Calculate target vertical velocity (jump)
|
278
|
+
if (sp && this.canJump(this)) {
|
279
|
+
if (this.isGrounded && currentVelocity.y > -0.001 && currentVelocity.y <= 3) {
|
280
|
+
targetVelocities.y = this.jumpVelocity;
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
// Apply impulse relative to target velocities, taking platform velocity into account
|
285
|
+
const platformVelocity = this._platform ? this._platform.linearVelocity : { x: 0, y: 0, z: 0 };
|
286
|
+
const deltaVelocities = {
|
287
|
+
x: targetVelocities.x - currentVelocity.x + platformVelocity.x,
|
288
|
+
y: targetVelocities.y + platformVelocity.y,
|
289
|
+
z: targetVelocities.z - currentVelocity.z + platformVelocity.z,
|
290
|
+
};
|
291
|
+
|
292
|
+
const hasExternalVelocity =
|
293
|
+
Math.abs(currentVelocity.x) > this.runVelocity ||
|
294
|
+
Math.abs(currentVelocity.y) > this.jumpVelocity ||
|
295
|
+
Math.abs(currentVelocity.z) > this.runVelocity;
|
296
|
+
|
297
|
+
if (!hasExternalVelocity) { // allow external velocities to resolve, otherwise our deltas will cancel them out.
|
298
|
+
if (Object.values(deltaVelocities).some(v => v !== 0)) {
|
299
|
+
const mass = entity.mass;
|
300
|
+
|
301
|
+
entity.applyImpulse({ // multiply by mass for the impulse to result in applying the correct target velocity
|
302
|
+
x: deltaVelocities.x * mass,
|
303
|
+
y: deltaVelocities.y * mass,
|
304
|
+
z: deltaVelocities.z * mass,
|
305
|
+
});
|
306
|
+
}
|
307
|
+
}
|
308
|
+
|
309
|
+
// Apply rotation
|
310
|
+
if (yaw !== undefined) {
|
311
|
+
const halfYaw = yaw / 2;
|
312
|
+
|
313
|
+
entity.setRotation({
|
314
|
+
x: 0,
|
315
|
+
y: Math.fround(Math.sin(halfYaw)),
|
316
|
+
z: 0,
|
317
|
+
w: Math.fround(Math.cos(halfYaw)),
|
318
|
+
});
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
@@ -3,7 +3,7 @@ import {
|
|
3
3
|
PlayerEntity,
|
4
4
|
} from 'hytopia';
|
5
5
|
|
6
|
-
import
|
6
|
+
import MyEntityController from './MyEntityController';
|
7
7
|
|
8
8
|
import worldMap from './assets/map.json';
|
9
9
|
|
@@ -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
|
+
// entity 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
|
+
controller: new MyEntityController(), // attach our entity 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
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* payload-game is a simple game that encompasses a number of core HYTOPIA SDK systems.
|
3
3
|
* This example utilizes entities, spawning, rigid body, colliders and sensors,
|
4
|
-
* collision groups, audio,
|
4
|
+
* collision groups, audio, entity controller hooks, and more.
|
5
5
|
*
|
6
6
|
* This example is a quick and dirty implementation of an overwatch style push the payload
|
7
7
|
* in a multiplayer PvE style. Players start the game around the payload and must stay near it
|
@@ -22,11 +22,11 @@ import {
|
|
22
22
|
PlayerCameraMode,
|
23
23
|
ColliderShape,
|
24
24
|
CollisionGroup,
|
25
|
-
|
25
|
+
PlayerEntityController,
|
26
26
|
Entity,
|
27
27
|
PlayerEntity,
|
28
28
|
RigidBodyType,
|
29
|
-
|
29
|
+
SimpleEntityController,
|
30
30
|
Vector3,
|
31
31
|
World,
|
32
32
|
startServer,
|
@@ -120,7 +120,7 @@ startServer(world => { // Perform our game setup logic in the startServer init c
|
|
120
120
|
playerEntity.spawn(world, randomSpawnCoordinate);
|
121
121
|
|
122
122
|
// We need to do some custom logic for player inputs, so let's assign custom onTick handler to the default player controller.
|
123
|
-
playerEntity.
|
123
|
+
playerEntity.controller!.onTickWithPlayerInput = onTickWithPlayerInput;
|
124
124
|
|
125
125
|
// Set custom collision groups for the player entity, this is so we can reference the PLAYER collision group
|
126
126
|
// specifically in enemy collision sensors.
|
@@ -293,7 +293,7 @@ function spawnPayloadEntity(world: World) {
|
|
293
293
|
}
|
294
294
|
|
295
295
|
payloadEntity = new Entity({
|
296
|
-
|
296
|
+
controller: new SimpleEntityController(),
|
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
|
-
|
353
|
+
controller: new SimpleEntityController(),
|
354
354
|
name: 'Spider',
|
355
355
|
modelUri: 'models/spider.gltf',
|
356
356
|
modelLoopedAnimations: [ 'walk' ],
|
@@ -444,16 +444,16 @@ function onTickPathfindPayload(entity: Entity) { // Movement logic for the paylo
|
|
444
444
|
return console.warn('Payload destination reached!! Game won!!');
|
445
445
|
}
|
446
446
|
|
447
|
-
if (!(entity.
|
448
|
-
return console.warn('Payload entity does not have a
|
447
|
+
if (!(entity.controller instanceof SimpleEntityController)) { // type guard
|
448
|
+
return console.warn('Payload entity does not have a SimpleEntityController!');
|
449
449
|
}
|
450
450
|
|
451
|
-
entity.
|
451
|
+
entity.controller.move(targetWaypointCoordinate, speed, {
|
452
452
|
moveCompleteCallback: () => targetWaypointCoordinateIndex++,
|
453
453
|
moveIgnoreAxes: { y: true },
|
454
454
|
});
|
455
455
|
|
456
|
-
entity.
|
456
|
+
entity.controller.face(targetWaypointCoordinate, speed / 2);
|
457
457
|
}
|
458
458
|
|
459
459
|
function onTickPathfindEnemy(entity: Entity, targetPlayers: Set<PlayerEntity>, speed: number, _tickDeltaMs: number) {
|
@@ -483,24 +483,23 @@ function onTickPathfindEnemy(entity: Entity, targetPlayers: Set<PlayerEntity>, s
|
|
483
483
|
}
|
484
484
|
|
485
485
|
// Handle Movement
|
486
|
-
if (!(entity.
|
487
|
-
return console.warn('Enemy entity does not have a
|
486
|
+
if (!(entity.controller instanceof SimpleEntityController)) {
|
487
|
+
return console.warn('Enemy entity does not have a SimpleEntityController!');
|
488
488
|
}
|
489
489
|
|
490
490
|
const targetPosition = enemyPathfindingTargets[entityId];
|
491
|
-
entity.
|
492
|
-
entity.
|
491
|
+
entity.controller.move(targetPosition, speed, { moveIgnoreAxes: { y: true } });
|
492
|
+
entity.controller.face(targetPosition, speed / 2);
|
493
493
|
}
|
494
494
|
|
495
495
|
enemyPathfindAccumulators[entityId]++;
|
496
496
|
}
|
497
497
|
|
498
|
-
function onTickWithPlayerInput(this:
|
499
|
-
if (!
|
498
|
+
function onTickWithPlayerInput(this: PlayerEntityController, entity: PlayerEntity, input: PlayerInput, cameraOrientation: PlayerCameraOrientation, _deltaTimeMs: number) {
|
499
|
+
if (!entity.world) return;
|
500
500
|
|
501
501
|
if (input.ml) {
|
502
|
-
const world =
|
503
|
-
const entity = this.entity;
|
502
|
+
const world = entity.world;
|
504
503
|
const direction = Vector3.fromVector3Like(entity.directionFromRotation);
|
505
504
|
|
506
505
|
direction.y = Math.sin(cameraOrientation.pitch);
|
@@ -513,7 +512,7 @@ function onTickWithPlayerInput(this: DefaultCharacterController, input: PlayerIn
|
|
513
512
|
// Normalize the direction vector to unit length
|
514
513
|
direction.normalize();
|
515
514
|
|
516
|
-
|
515
|
+
entity.startModelOneshotAnimations([ 'shoot' ]);
|
517
516
|
|
518
517
|
// Adjust bullet origin roughly for camera offset so crosshair is accurate
|
519
518
|
const bulletOrigin = entity.position;
|