2023-06-18 10:23:43 +00:00
|
|
|
import {
|
|
|
|
EngineComponent,
|
|
|
|
World,
|
|
|
|
instanceCharacterObject,
|
|
|
|
getCharacterController,
|
|
|
|
Humanoid,
|
2023-06-25 11:51:16 +00:00
|
|
|
GameSocket,
|
|
|
|
EventEmitter,
|
|
|
|
Renderer,
|
|
|
|
randomUUID,
|
2023-06-18 10:23:43 +00:00
|
|
|
} from '@freeblox/engine';
|
|
|
|
|
2023-06-25 11:51:16 +00:00
|
|
|
import { Quaternion, Vector3 } from 'three';
|
2023-06-18 11:22:38 +00:00
|
|
|
import { ThirdPersonCamera } from './camera';
|
2023-06-25 11:51:16 +00:00
|
|
|
import { GameEvents, SpawnEvent } from '../types/events';
|
2023-06-18 10:23:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Gameplay manager.
|
|
|
|
*/
|
|
|
|
export class GameplayComponent extends EngineComponent {
|
|
|
|
public name = GameplayComponent.name;
|
|
|
|
public world!: World;
|
|
|
|
public cleanUpEvents?: () => void;
|
|
|
|
|
2023-06-18 11:22:38 +00:00
|
|
|
public controls!: ThirdPersonCamera;
|
2023-06-18 10:23:43 +00:00
|
|
|
public character!: Humanoid;
|
|
|
|
public movementSpeed = 16;
|
|
|
|
public movement = {
|
|
|
|
forward: 0,
|
|
|
|
backward: 0,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
};
|
2023-06-19 20:41:20 +00:00
|
|
|
public jump = false;
|
2023-06-18 10:23:43 +00:00
|
|
|
|
2023-06-25 15:18:48 +00:00
|
|
|
private prevVelocity = new Vector3();
|
|
|
|
private prevJump = false;
|
|
|
|
|
2023-06-18 11:22:38 +00:00
|
|
|
private move = new Vector3();
|
|
|
|
private look = new Vector3();
|
2023-06-25 11:51:16 +00:00
|
|
|
private server = new GameSocket(this.events);
|
|
|
|
|
|
|
|
public uuid = randomUUID();
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
protected renderer: Renderer,
|
|
|
|
protected events: EventEmitter<GameEvents>
|
|
|
|
) {
|
|
|
|
super(renderer, events);
|
|
|
|
}
|
2023-06-18 11:22:38 +00:00
|
|
|
|
2023-06-18 10:23:43 +00:00
|
|
|
override initialize(): void {
|
|
|
|
this.world = this.renderer.scene.getObjectByName('World') as World;
|
|
|
|
this.cleanUpEvents = this.bindEvents();
|
2023-06-25 11:51:16 +00:00
|
|
|
this.server.track(this.world);
|
|
|
|
this.server.connect('ws://localhost:8256', this.uuid, 'testing');
|
2023-06-18 10:23:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override update(delta: number): void {
|
2023-06-18 11:22:38 +00:00
|
|
|
this.controls?.update(delta);
|
|
|
|
|
|
|
|
this.move.set(0, 0, 0);
|
|
|
|
this.look.setFromMatrixColumn(this.renderer.camera.matrix, 0);
|
|
|
|
this.look.multiplyScalar(this.movement.forward + -this.movement.backward);
|
|
|
|
this.look.crossVectors(this.renderer.camera.up, this.look);
|
|
|
|
this.move.add(this.look);
|
|
|
|
|
|
|
|
this.look.setFromMatrixColumn(this.renderer.camera.matrix, 0);
|
|
|
|
this.look.multiplyScalar(this.movement.right + -this.movement.left);
|
|
|
|
this.move.add(this.look);
|
|
|
|
|
|
|
|
this.look.copy(this.move).normalize();
|
|
|
|
this.character?.localToWorld(this.look);
|
|
|
|
|
|
|
|
this.character?.setVelocity(this.move);
|
2023-06-25 15:18:48 +00:00
|
|
|
|
2023-06-25 11:51:16 +00:00
|
|
|
const look = this.move.clone().normalize();
|
2023-06-18 11:22:38 +00:00
|
|
|
if (this.move.length()) {
|
2023-06-25 11:51:16 +00:00
|
|
|
this.character?.setLook(look);
|
2023-06-18 11:22:38 +00:00
|
|
|
}
|
2023-06-25 15:18:48 +00:00
|
|
|
|
|
|
|
let jump = false;
|
2023-06-19 20:41:20 +00:00
|
|
|
if (this.jump) {
|
2023-06-25 15:18:48 +00:00
|
|
|
jump = true;
|
2023-06-19 20:41:20 +00:00
|
|
|
this.jump = false;
|
|
|
|
this.character?.jump();
|
|
|
|
}
|
2023-06-25 15:18:48 +00:00
|
|
|
|
|
|
|
if (!this.move.equals(this.prevVelocity) || jump !== this.prevJump) {
|
|
|
|
this.events.emit('sendPlayer', {
|
|
|
|
playerId: this.uuid,
|
|
|
|
velocity: this.move,
|
|
|
|
lookAt: look,
|
|
|
|
jump,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
this.prevVelocity.copy(this.move);
|
|
|
|
this.prevJump = jump;
|
2023-06-18 10:23:43 +00:00
|
|
|
}
|
|
|
|
|
2023-06-18 15:51:09 +00:00
|
|
|
override dispose(): void {
|
2023-06-18 10:23:43 +00:00
|
|
|
this.cleanUpEvents?.();
|
|
|
|
}
|
|
|
|
|
2023-06-25 11:51:16 +00:00
|
|
|
public async loadCharacter(
|
|
|
|
name: string,
|
|
|
|
pos?: Vector3,
|
|
|
|
rot?: Quaternion,
|
2023-06-25 15:18:48 +00:00
|
|
|
uuid?: string,
|
|
|
|
objectUUID?: string
|
2023-06-25 11:51:16 +00:00
|
|
|
) {
|
2023-06-25 15:18:48 +00:00
|
|
|
const char = await instanceCharacterObject(name, pos);
|
2023-06-18 10:23:43 +00:00
|
|
|
const ctrl = getCharacterController(char);
|
2023-06-25 15:18:48 +00:00
|
|
|
|
|
|
|
if (uuid) ctrl.uuid = uuid;
|
|
|
|
if (objectUUID) char.uuid = objectUUID;
|
|
|
|
|
2023-06-18 10:23:43 +00:00
|
|
|
this.world.add(char);
|
2023-06-25 11:51:16 +00:00
|
|
|
|
|
|
|
if (rot) char.quaternion.copy(rot);
|
|
|
|
|
|
|
|
this.events.emit('sceneJoin', char);
|
|
|
|
|
|
|
|
if (uuid !== this.uuid) return;
|
2023-06-18 10:23:43 +00:00
|
|
|
this.character = ctrl;
|
2023-06-18 11:22:38 +00:00
|
|
|
|
|
|
|
this.controls = new ThirdPersonCamera(
|
|
|
|
this.renderer.camera,
|
|
|
|
char,
|
|
|
|
this.renderer.renderer.domElement
|
|
|
|
);
|
|
|
|
this.controls.initialize();
|
2023-06-18 10:23:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private bindEvents() {
|
|
|
|
const keyDownEvent = (event: KeyboardEvent) => {
|
|
|
|
switch (event.code) {
|
|
|
|
case 'KeyW':
|
|
|
|
this.movement.forward = this.movementSpeed;
|
|
|
|
break;
|
|
|
|
case 'KeyS':
|
|
|
|
this.movement.backward = this.movementSpeed;
|
|
|
|
break;
|
|
|
|
case 'KeyA':
|
|
|
|
this.movement.left = this.movementSpeed;
|
|
|
|
break;
|
|
|
|
case 'KeyD':
|
|
|
|
this.movement.right = this.movementSpeed;
|
|
|
|
break;
|
2023-06-19 20:41:20 +00:00
|
|
|
case 'Space':
|
|
|
|
this.jump = true;
|
|
|
|
break;
|
2023-06-18 10:23:43 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
const keyUpEvent = (event: KeyboardEvent) => {
|
|
|
|
switch (event.code) {
|
|
|
|
case 'KeyW':
|
|
|
|
this.movement.forward = 0;
|
|
|
|
break;
|
|
|
|
case 'KeyS':
|
|
|
|
this.movement.backward = 0;
|
|
|
|
break;
|
|
|
|
case 'KeyA':
|
|
|
|
this.movement.left = 0;
|
|
|
|
break;
|
|
|
|
case 'KeyD':
|
|
|
|
this.movement.right = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-06-25 11:51:16 +00:00
|
|
|
const createCharacterEvent = (event: SpawnEvent) => {
|
|
|
|
this.loadCharacter(
|
|
|
|
event.playerName,
|
|
|
|
event.position,
|
|
|
|
event.rotation,
|
2023-06-25 15:18:48 +00:00
|
|
|
event.playerId,
|
|
|
|
event.objectUUID
|
2023-06-25 11:51:16 +00:00
|
|
|
);
|
2023-06-18 15:51:09 +00:00
|
|
|
};
|
|
|
|
|
2023-06-24 18:33:01 +00:00
|
|
|
const physicsLoadedEvent = () => {
|
|
|
|
this.events.emit('initialized');
|
|
|
|
};
|
|
|
|
|
2023-06-18 10:23:43 +00:00
|
|
|
window.addEventListener('keydown', keyDownEvent);
|
|
|
|
window.addEventListener('keyup', keyUpEvent);
|
2023-06-25 11:51:16 +00:00
|
|
|
this.events.addListener('spawnCharacter', createCharacterEvent);
|
2023-06-24 18:33:01 +00:00
|
|
|
this.events.addListener('physicsComplete', physicsLoadedEvent);
|
2023-06-18 10:23:43 +00:00
|
|
|
return () => {
|
|
|
|
window.removeEventListener('keydown', keyDownEvent);
|
|
|
|
window.removeEventListener('keyup', keyUpEvent);
|
2023-06-25 11:51:16 +00:00
|
|
|
this.events.removeEventListener('spawnCharacter', createCharacterEvent);
|
2023-06-24 18:33:01 +00:00
|
|
|
this.events.removeEventListener('physicsComplete', physicsLoadedEvent);
|
2023-06-18 10:23:43 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|