142 lines
3.6 KiB
TypeScript
142 lines
3.6 KiB
TypeScript
import { IcyNetUser } from '../../common/types/user';
|
|
import { Socket } from 'socket.io-client';
|
|
import { PonyEntity } from './pony';
|
|
import { Scene, Vector2, Vector3 } from 'three';
|
|
|
|
export class Player extends PonyEntity {
|
|
public keydownMap: { [x: string]: boolean } = {};
|
|
private _prevKeydownMap: { [x: string]: boolean } = {};
|
|
|
|
/**
|
|
* Normal vector of the currently faced direction
|
|
*/
|
|
private _lookVector = new Vector3();
|
|
|
|
/**
|
|
* Direction of the current movement.
|
|
* X axis controls angular velocity, Y axis controls linear velocity direction by world coordinates.
|
|
*/
|
|
private _direction = new Vector2();
|
|
|
|
/**
|
|
* Joystick or other external movement usage.
|
|
*/
|
|
private _externalDirection = new Vector2();
|
|
|
|
/**
|
|
* Was external movement used this tick
|
|
*/
|
|
private _externalForce = false;
|
|
|
|
/**
|
|
* Was moving last tick
|
|
*/
|
|
private _wasMoving = false;
|
|
|
|
/**
|
|
* Was turning last tick
|
|
*/
|
|
private _wasTurning = false;
|
|
|
|
constructor(public user: IcyNetUser) {
|
|
super();
|
|
}
|
|
|
|
public static fromUser(user: IcyNetUser, scene: Scene): Player {
|
|
const entity = new Player(user);
|
|
entity.initialize();
|
|
scene.add(entity.container);
|
|
return entity;
|
|
}
|
|
|
|
initialize(): void {
|
|
super.initialize();
|
|
|
|
window.addEventListener('keydown', (e) => {
|
|
this.keydownMap[e.key.toLowerCase()] = true;
|
|
});
|
|
|
|
window.addEventListener('keyup', (e) => {
|
|
this.keydownMap[e.key.toLowerCase()] = false;
|
|
});
|
|
}
|
|
|
|
public createPacket(socket: Socket): void {
|
|
if (Object.keys(this.changes).length) {
|
|
socket.emit('move', this.changes);
|
|
this.changes = {};
|
|
}
|
|
}
|
|
|
|
public applyForce(vec: Vector2) {
|
|
this._externalDirection.copy(vec);
|
|
this._externalForce = true;
|
|
}
|
|
|
|
public moveCharacter(dt: number) {
|
|
const vector = new Vector2();
|
|
const wasExternalForce = this._externalForce;
|
|
this._externalForce = false;
|
|
|
|
if (wasExternalForce) {
|
|
vector.copy(this._externalDirection);
|
|
} else {
|
|
vector.copy(this._direction);
|
|
}
|
|
|
|
if (vector.x !== 0) {
|
|
this.angularVelocity.set(0, Math.PI * vector.x, 0);
|
|
this.changes.angular = this.angularVelocity.toArray();
|
|
|
|
this._wasTurning = true;
|
|
} else if (this._wasTurning && !wasExternalForce) {
|
|
this._wasTurning = false;
|
|
this.angularVelocity.set(0, 0, 0);
|
|
this.changes.angular = this.angularVelocity.toArray();
|
|
this.changes.rotation = this.container.rotation.toArray();
|
|
}
|
|
|
|
if (vector.y !== 0) {
|
|
this.velocity.copy(this._lookVector.clone().multiplyScalar(vector.y));
|
|
this.changes.velocity = this.velocity.toArray();
|
|
|
|
this._wasMoving = true;
|
|
this.setWalkAnimationState(1);
|
|
} else if (this._wasMoving && !wasExternalForce) {
|
|
this._wasMoving = false;
|
|
this.velocity.set(0, 0, 0);
|
|
this.changes.velocity = this.velocity.toArray();
|
|
this.changes.position = this.container.position.toArray();
|
|
this.setWalkAnimationState(0);
|
|
}
|
|
|
|
this.changes.rotation = this.container.rotation.toArray();
|
|
this.changes.position = this.container.position.toArray();
|
|
}
|
|
|
|
update(dt: number): void {
|
|
this.container.getWorldDirection(this._lookVector);
|
|
|
|
if (this.keydownMap['w']) {
|
|
this._direction.y = 1;
|
|
} else if (this.keydownMap['s']) {
|
|
this._direction.y = -1;
|
|
} else {
|
|
this._direction.y = 0;
|
|
}
|
|
|
|
if (this.keydownMap['a']) {
|
|
this._direction.x = 1;
|
|
} else if (this.keydownMap['d']) {
|
|
this._direction.x = -1;
|
|
} else {
|
|
this._direction.x = 0;
|
|
}
|
|
|
|
this.moveCharacter(dt);
|
|
super.update(dt);
|
|
|
|
this._prevKeydownMap = { ...this.keydownMap };
|
|
}
|
|
}
|