Improve physics
This commit is contained in:
parent
8b87b68dae
commit
ec0b700313
14 changed files with 181 additions and 71 deletions
12
backend/src/helper/delta-time-calculator.ts
Normal file
12
backend/src/helper/delta-time-calculator.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
export class DeltaTimeCalculator {
|
||||||
|
private previousTime: [number, number] = process.hrtime();
|
||||||
|
|
||||||
|
public getNextDeltaTimeInMilliseconds(): number {
|
||||||
|
const deltaTime = process.hrtime(this.previousTime);
|
||||||
|
this.previousTime = process.hrtime();
|
||||||
|
|
||||||
|
const [seconds, nanoSeconds] = deltaTime;
|
||||||
|
|
||||||
|
return seconds * 1000 + nanoSeconds / 1000 / 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,12 +2,19 @@ import ioserver, { Socket } from 'socket.io';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { Server } from 'http';
|
import { Server } from 'http';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import { applyArrayPlugins, Random, TransportEvents, deserializeCommand } from 'shared';
|
import {
|
||||||
|
applyArrayPlugins,
|
||||||
|
Random,
|
||||||
|
TransportEvents,
|
||||||
|
deserializeCommand,
|
||||||
|
StepCommand,
|
||||||
|
} from 'shared';
|
||||||
import './index.html';
|
import './index.html';
|
||||||
import { Player } from './players/player';
|
import { Player } from './players/player';
|
||||||
import { PhysicalContainer } from './physics/containers/physical-container';
|
import { PhysicalContainer } from './physics/containers/physical-container';
|
||||||
import { createDungeon } from './map/create-dungeon';
|
import { createDungeon } from './map/create-dungeon';
|
||||||
import { glMatrix } from 'gl-matrix';
|
import { glMatrix } from 'gl-matrix';
|
||||||
|
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
||||||
|
|
||||||
glMatrix.setMatrixArrayType(Array);
|
glMatrix.setMatrixArrayType(Array);
|
||||||
|
|
||||||
|
|
@ -26,6 +33,8 @@ objects.initialize();
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
const deltaTimeCalculator = new DeltaTimeCalculator();
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
cors({
|
cors({
|
||||||
origin: (origin, callback) => {
|
origin: (origin, callback) => {
|
||||||
|
|
@ -50,7 +59,6 @@ app.get('/', function (req, res) {
|
||||||
io.on('connection', (socket: SocketIO.Socket) => {
|
io.on('connection', (socket: SocketIO.Socket) => {
|
||||||
socket.on(TransportEvents.PlayerJoining, () => {
|
socket.on(TransportEvents.PlayerJoining, () => {
|
||||||
const player = new Player(objects, socket);
|
const player = new Player(objects, socket);
|
||||||
|
|
||||||
socket.on(TransportEvents.PlayerToServer, (text: string) => {
|
socket.on(TransportEvents.PlayerToServer, (text: string) => {
|
||||||
const command = deserializeCommand(JSON.parse(text));
|
const command = deserializeCommand(JSON.parse(text));
|
||||||
player.sendCommand(command);
|
player.sendCommand(command);
|
||||||
|
|
@ -71,3 +79,14 @@ io.on('connection', (socket: SocketIO.Socket) => {
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
console.log(`server started at http://localhost:${port}`);
|
console.log(`server started at http://localhost:${port}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handlePhysics = () => {
|
||||||
|
const delta = deltaTimeCalculator.getNextDeltaTimeInMilliseconds();
|
||||||
|
const step = new StepCommand(delta);
|
||||||
|
|
||||||
|
objects.sendCommand(step);
|
||||||
|
|
||||||
|
setTimeout(handlePhysics, 5);
|
||||||
|
};
|
||||||
|
|
||||||
|
handlePhysics();
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,64 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { id, CharacterBase } from 'shared';
|
import {
|
||||||
|
id,
|
||||||
|
CharacterBase,
|
||||||
|
StepCommand,
|
||||||
|
settings,
|
||||||
|
CommandExecutors,
|
||||||
|
MoveActionCommand,
|
||||||
|
} from 'shared';
|
||||||
|
|
||||||
import { ImmutableBoundingBox } from '../physics/bounding-boxes/immutable-bounding-box';
|
import { ImmutableBoundingBox } from '../physics/bounding-boxes/immutable-bounding-box';
|
||||||
|
|
||||||
import { CirclePhysical } from './circle-physical';
|
import { CirclePhysical } from './circle-physical';
|
||||||
import { Physical } from '../physics/physical';
|
import { Physical } from '../physics/physical';
|
||||||
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
|
|
||||||
export class CharacterPhysical extends CharacterBase implements Physical {
|
export class CharacterPhysical extends CharacterBase implements Physical {
|
||||||
public readonly canCollide = true;
|
public readonly canCollide = true;
|
||||||
public readonly isInverted = false;
|
public readonly isInverted = false;
|
||||||
public readonly canMove = true;
|
public readonly canMove = true;
|
||||||
|
|
||||||
|
public head: CirclePhysical;
|
||||||
|
public leftFoot: CirclePhysical;
|
||||||
|
public rightFoot: CirclePhysical;
|
||||||
|
|
||||||
|
private movementActions: Array<MoveActionCommand> = [];
|
||||||
|
|
||||||
|
protected commandExecutors: CommandExecutors = {
|
||||||
|
[StepCommand.type]: this.step.bind(this),
|
||||||
|
[MoveActionCommand.type]: (c: MoveActionCommand) => this.movementActions.push(c),
|
||||||
|
};
|
||||||
|
|
||||||
private static readonly headOffset = vec2.fromValues(0, 40);
|
private static readonly headOffset = vec2.fromValues(0, 40);
|
||||||
private static readonly leftFootOffset = vec2.fromValues(-20, -10);
|
private static readonly leftFootOffset = vec2.fromValues(-20, -10);
|
||||||
private static readonly rightFootOffset = vec2.fromValues(20, -10);
|
private static readonly rightFootOffset = vec2.fromValues(20, -10);
|
||||||
|
|
||||||
constructor() {
|
constructor(container: PhysicalContainer) {
|
||||||
super(
|
super(
|
||||||
id(),
|
id(),
|
||||||
new CirclePhysical(vec2.clone(CharacterPhysical.headOffset), 50, null),
|
new CirclePhysical(vec2.clone(CharacterPhysical.headOffset), 50, null, container),
|
||||||
new CirclePhysical(vec2.clone(CharacterPhysical.leftFootOffset), 20, null),
|
new CirclePhysical(
|
||||||
new CirclePhysical(vec2.clone(CharacterPhysical.rightFootOffset), 20, null)
|
vec2.clone(CharacterPhysical.leftFootOffset),
|
||||||
|
20,
|
||||||
|
null,
|
||||||
|
container
|
||||||
|
),
|
||||||
|
new CirclePhysical(
|
||||||
|
vec2.clone(CharacterPhysical.rightFootOffset),
|
||||||
|
20,
|
||||||
|
null,
|
||||||
|
container
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.head.owner = this;
|
||||||
|
this.leftFoot.owner = this;
|
||||||
|
this.rightFoot.owner = this;
|
||||||
|
|
||||||
|
container.addObject(this.head);
|
||||||
|
container.addObject(this.leftFoot);
|
||||||
|
container.addObject(this.rightFoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _boundingBox?: ImmutableBoundingBox;
|
private _boundingBox?: ImmutableBoundingBox;
|
||||||
|
|
@ -42,16 +79,51 @@ export class CharacterPhysical extends CharacterBase implements Physical {
|
||||||
public distance(_: vec2): number {
|
public distance(_: vec2): number {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
public step() {
|
private sumAndResetMovementActions(): vec2 {
|
||||||
const movementForce = vec2.fromValues(right - left, up - down);
|
if (this.movementActions.length === 0) {
|
||||||
|
return vec2.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
const movementForce = this.movementActions.reduce(
|
||||||
|
(sum, current) => vec2.add(sum, sum, current.delta),
|
||||||
|
vec2.create()
|
||||||
|
);
|
||||||
|
|
||||||
|
vec2.scale(movementForce, movementForce, 1 / this.movementActions.length);
|
||||||
|
|
||||||
|
this.movementActions = [];
|
||||||
|
|
||||||
|
return movementForce;
|
||||||
|
}
|
||||||
|
|
||||||
|
public step(c: StepCommand) {
|
||||||
|
const deltaTime = c.deltaTimeInMiliseconds;
|
||||||
|
|
||||||
|
const movementForce = this.sumAndResetMovementActions();
|
||||||
|
if (!this.leftFoot.isAirborne || !this.rightFoot.isAirborne) {
|
||||||
|
movementForce.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
this.head.applyForce(movementForce, deltaTime);
|
this.head.applyForce(movementForce, deltaTime);
|
||||||
this.head.applyForce(settings.gravitationalForce, deltaTime);
|
this.head.applyForce(settings.gravitationalForce, deltaTime);
|
||||||
|
|
||||||
const bodyCenter = vec2.sub(vec2.create(), this.head.center, this.headOffset);
|
const bodyCenter = vec2.sub(
|
||||||
|
vec2.create(),
|
||||||
|
this.head.center,
|
||||||
|
CharacterPhysical.headOffset
|
||||||
|
);
|
||||||
|
|
||||||
const leftFootPositon = vec2.add(vec2.create(), bodyCenter, this.leftFootOffset);
|
const leftFootPositon = vec2.add(
|
||||||
const rightFootPositon = vec2.add(vec2.create(), bodyCenter, this.rightFootOffset);
|
vec2.create(),
|
||||||
|
bodyCenter,
|
||||||
|
CharacterPhysical.leftFootOffset
|
||||||
|
);
|
||||||
|
const rightFootPositon = vec2.add(
|
||||||
|
vec2.create(),
|
||||||
|
bodyCenter,
|
||||||
|
CharacterPhysical.rightFootOffset
|
||||||
|
);
|
||||||
|
|
||||||
const leftFootDelta = vec2.sub(vec2.create(), this.leftFoot.center, leftFootPositon);
|
const leftFootDelta = vec2.sub(vec2.create(), this.leftFoot.center, leftFootPositon);
|
||||||
const rightFootDelta = vec2.sub(
|
const rightFootDelta = vec2.sub(
|
||||||
|
|
@ -80,13 +152,13 @@ export class CharacterPhysical extends CharacterBase implements Physical {
|
||||||
this.leftFoot.step(deltaTime);
|
this.leftFoot.step(deltaTime);
|
||||||
this.rightFoot.step(deltaTime);
|
this.rightFoot.step(deltaTime);
|
||||||
|
|
||||||
this.flashlight.center = vec2.add(
|
/*this.flashlight.center = vec2.add(
|
||||||
vec2.create(),
|
vec2.create(),
|
||||||
this.head.center,
|
this.head.center,
|
||||||
vec2.fromValues(0, 0)
|
vec2.fromValues(0, 0)
|
||||||
);
|
);*/
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
public toJSON(): any {
|
public toJSON(): any {
|
||||||
const { type, id, head, leftFoot, rightFoot } = this;
|
const { type, id, head, leftFoot, rightFoot } = this;
|
||||||
return [type, id, head, leftFoot, rightFoot];
|
return [type, id, head, leftFoot, rightFoot];
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,18 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { Circle, clamp, GameObject, settings } from 'shared';
|
import {
|
||||||
|
Circle,
|
||||||
|
clamp,
|
||||||
|
CommandExecutors,
|
||||||
|
GameObject,
|
||||||
|
settings,
|
||||||
|
StepCommand,
|
||||||
|
} from 'shared';
|
||||||
import { Physical } from '../physics/physical';
|
import { Physical } from '../physics/physical';
|
||||||
|
|
||||||
import { BoundingBox } from '../physics/bounding-boxes/bounding-box';
|
import { BoundingBox } from '../physics/bounding-boxes/bounding-box';
|
||||||
import { BoundingBoxBase } from '../physics/bounding-boxes/bounding-box-base';
|
import { BoundingBoxBase } from '../physics/bounding-boxes/bounding-box-base';
|
||||||
|
import { moveCircle } from '../physics/move-circle';
|
||||||
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
|
|
||||||
export class CirclePhysical implements Circle, Physical {
|
export class CirclePhysical implements Circle, Physical {
|
||||||
readonly isInverted = false;
|
readonly isInverted = false;
|
||||||
|
|
@ -18,9 +27,18 @@ export class CirclePhysical implements Circle, Physical {
|
||||||
return this._isAirborne;
|
return this._isAirborne;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected commandExecutors: CommandExecutors = {
|
||||||
|
[StepCommand.type]: this.step.bind(this),
|
||||||
|
};
|
||||||
|
|
||||||
private _boundingBox: BoundingBox;
|
private _boundingBox: BoundingBox;
|
||||||
|
|
||||||
constructor(private _center: vec2, private _radius: number, private owner: GameObject) {
|
constructor(
|
||||||
|
private _center: vec2,
|
||||||
|
private _radius: number,
|
||||||
|
public owner: GameObject,
|
||||||
|
private readonly container: PhysicalContainer
|
||||||
|
) {
|
||||||
this._boundingBox = new BoundingBox(null);
|
this._boundingBox = new BoundingBox(null);
|
||||||
this.recalculateBoundingBox();
|
this.recalculateBoundingBox();
|
||||||
}
|
}
|
||||||
|
|
@ -59,11 +77,11 @@ export class CirclePhysical implements Circle, Physical {
|
||||||
return vec2.distance(target.center, this.center) - this.radius - target.radius;
|
return vec2.distance(target.center, this.center) - this.radius - target.radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
public areIntersecting(other: CirclePhysical): boolean {
|
public areIntersecting(other: Physical): boolean {
|
||||||
return other.distance(this.center) < this.radius;
|
return other.distance(this.center) < this.radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isInside(other: CirclePhysical): boolean {
|
public isInside(other: Physical): boolean {
|
||||||
return other.distance(this.center) < -this.radius;
|
return other.distance(this.center) < -this.radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,12 +123,13 @@ export class CirclePhysical implements Circle, Physical {
|
||||||
this.velocity = vec2.create();
|
this.velocity = vec2.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*public step(timeInMilliseconds: number): boolean {
|
public step(timeInMilliseconds: number): boolean {
|
||||||
vec2.scale(
|
vec2.scale(
|
||||||
this.velocity,
|
this.velocity,
|
||||||
this.velocity,
|
this.velocity,
|
||||||
Math.pow(settings.velocityAttenuation, timeInMilliseconds)
|
Math.pow(settings.velocityAttenuation, timeInMilliseconds)
|
||||||
);
|
);
|
||||||
|
|
||||||
const distance = vec2.scale(vec2.create(), this.velocity, timeInMilliseconds);
|
const distance = vec2.scale(vec2.create(), this.velocity, timeInMilliseconds);
|
||||||
const distanceLength = vec2.length(distance);
|
const distanceLength = vec2.length(distance);
|
||||||
const stepCount = Math.ceil(distanceLength / settings.physicsMaxStep);
|
const stepCount = Math.ceil(distanceLength / settings.physicsMaxStep);
|
||||||
|
|
@ -119,22 +138,21 @@ export class CirclePhysical implements Circle, Physical {
|
||||||
let wasHit = false;
|
let wasHit = false;
|
||||||
|
|
||||||
for (let i = 0; i < stepCount; i++) {
|
for (let i = 0; i < stepCount; i++) {
|
||||||
const { normal, tangent, hitSurface } = this.physics.tryMovingDynamicCircle(
|
const { normal, tangent, hitSurface } = moveCircle(
|
||||||
this,
|
this,
|
||||||
distance
|
distance,
|
||||||
|
this.container.findIntersecting(this.boundingBox)
|
||||||
);
|
);
|
||||||
if (hitSurface) {
|
if (hitSurface) {
|
||||||
vec2.scale(this.velocity, tangent, vec2.dot(tangent, this.velocity));
|
vec2.scale(this.velocity, tangent, vec2.dot(tangent, this.velocity));
|
||||||
wasHit = true;
|
wasHit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._isAirborne = !(
|
this._isAirborne = hitSurface;
|
||||||
hitSurface && vec2.dot(normal, settings.gravitationalForce) < 0
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wasHit;
|
return wasHit;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
public toJSON(): any {
|
public toJSON(): any {
|
||||||
const { center, radius } = this;
|
const { center, radius } = this;
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ export class PhysicalContainer {
|
||||||
|
|
||||||
private createGroupForCommand(commandType: string) {
|
private createGroupForCommand(commandType: string) {
|
||||||
const objectsReactingToCommand = [];
|
const objectsReactingToCommand = [];
|
||||||
|
console.log(commandType);
|
||||||
this.objects.forEach((o, _) => {
|
this.objects.forEach((o, _) => {
|
||||||
if (o.reactsToCommand(commandType)) {
|
if (o.reactsToCommand(commandType)) {
|
||||||
objectsReactingToCommand.push(o);
|
objectsReactingToCommand.push(o);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
/*import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { rotate90Deg } from 'shared';
|
import { rotate90Deg, settings } from 'shared';
|
||||||
import { CirclePhysics } from './bounds/circle-physics';
|
import { CirclePhysical } from '../objects/circle-physical';
|
||||||
import { PhysicalGameObject } from './physical-game-object';
|
import { Physical } from './physical';
|
||||||
|
|
||||||
export const moveCircle = (
|
export const moveCircle = (
|
||||||
circle: CirclePhysics,
|
circle: CirclePhysical,
|
||||||
delta: vec2,
|
delta: vec2,
|
||||||
possibleIntersectors: Array<PhysicalGameObject>
|
possibleIntersectors: Array<Physical>
|
||||||
): {
|
): {
|
||||||
realDelta: vec2;
|
realDelta: vec2;
|
||||||
hitSurface: boolean;
|
hitSurface: boolean;
|
||||||
|
|
@ -16,7 +16,7 @@ export const moveCircle = (
|
||||||
circle.center = vec2.add(circle.center, circle.center, delta);
|
circle.center = vec2.add(circle.center, circle.center, delta);
|
||||||
|
|
||||||
const intersecting = possibleIntersectors.filter(
|
const intersecting = possibleIntersectors.filter(
|
||||||
(b) => b !== circle && circle.areIntersecting(b) && b.canCollide
|
(b) => b.gameObject !== circle.gameObject && circle.areIntersecting(b) && b.canCollide
|
||||||
);
|
);
|
||||||
|
|
||||||
if (intersecting.length === 0) {
|
if (intersecting.length === 0) {
|
||||||
|
|
@ -34,7 +34,7 @@ export const moveCircle = (
|
||||||
closest: intersecting
|
closest: intersecting
|
||||||
.map((i) => ({
|
.map((i) => ({
|
||||||
inverted: i.isInverted,
|
inverted: i.isInverted,
|
||||||
distance: i.owner.distance(point),
|
distance: i.distance(point),
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => a.distance - b.distance)[0],
|
.sort((a, b) => a.distance - b.distance)[0],
|
||||||
}))
|
}))
|
||||||
|
|
@ -79,4 +79,3 @@ export const moveCircle = (
|
||||||
tangent: rotate90Deg(approxNormal),
|
tangent: rotate90Deg(approxNormal),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
*/
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { jsonSerialize } from '../serialize';
|
||||||
export class Player extends CommandReceiver {
|
export class Player extends CommandReceiver {
|
||||||
public isActive = true;
|
public isActive = true;
|
||||||
|
|
||||||
private character: CharacterPhysical = new CharacterPhysical();
|
private character: CharacterPhysical;
|
||||||
|
|
||||||
private objectsPreviouslyInViewArea: Array<Physical> = [];
|
private objectsPreviouslyInViewArea: Array<Physical> = [];
|
||||||
private objectsInViewArea: Array<Physical> = [];
|
private objectsInViewArea: Array<Physical> = [];
|
||||||
|
|
@ -30,12 +30,8 @@ export class Player extends CommandReceiver {
|
||||||
[SetViewAreaActionCommand.type]: this.setViewArea.bind(this),
|
[SetViewAreaActionCommand.type]: this.setViewArea.bind(this),
|
||||||
[MoveActionCommand.type]: (c: MoveActionCommand) => {
|
[MoveActionCommand.type]: (c: MoveActionCommand) => {
|
||||||
vec2.normalize(c.delta, c.delta);
|
vec2.normalize(c.delta, c.delta);
|
||||||
vec2.scale(c.delta, c.delta, 40);
|
vec2.scale(c.delta, c.delta, 15);
|
||||||
this.character.head.center = vec2.add(
|
this.character.sendCommand(c);
|
||||||
this.character.head.center,
|
|
||||||
this.character.head.center,
|
|
||||||
c.delta
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -46,6 +42,7 @@ export class Player extends CommandReceiver {
|
||||||
private readonly socket: SocketIO.Socket
|
private readonly socket: SocketIO.Socket
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
this.character = new CharacterPhysical(objects);
|
||||||
this.objectsPreviouslyInViewArea.push(this.character);
|
this.objectsPreviouslyInViewArea.push(this.character);
|
||||||
this.objectsInViewArea.push(this.character);
|
this.objectsInViewArea.push(this.character);
|
||||||
|
|
||||||
|
|
@ -82,7 +79,9 @@ export class Player extends CommandReceiver {
|
||||||
this.socket.emit(
|
this.socket.emit(
|
||||||
TransportEvents.ServerToPlayer,
|
TransportEvents.ServerToPlayer,
|
||||||
jsonSerialize(
|
jsonSerialize(
|
||||||
new DeleteObjectsCommand(noLongerIntersecting.map((p) => p.gameObject.id))
|
new DeleteObjectsCommand([
|
||||||
|
...new Set(noLongerIntersecting.map((p) => p.gameObject.id)),
|
||||||
|
])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +91,7 @@ export class Player extends CommandReceiver {
|
||||||
TransportEvents.ServerToPlayer,
|
TransportEvents.ServerToPlayer,
|
||||||
jsonSerialize(
|
jsonSerialize(
|
||||||
new CreateObjectsCommand(
|
new CreateObjectsCommand(
|
||||||
jsonSerialize(newlyIntersecting.map((p) => p.gameObject))
|
jsonSerialize([...new Set(newlyIntersecting.map((p) => p.gameObject))])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
@ -102,7 +101,11 @@ export class Player extends CommandReceiver {
|
||||||
TransportEvents.ServerToPlayer,
|
TransportEvents.ServerToPlayer,
|
||||||
jsonSerialize(
|
jsonSerialize(
|
||||||
new UpdateObjectsCommand(
|
new UpdateObjectsCommand(
|
||||||
jsonSerialize(this.objectsInViewArea.filter((o) => o.canMove))
|
jsonSerialize([
|
||||||
|
...new Set(
|
||||||
|
this.objectsInViewArea.filter((p) => p.canMove).map((p) => p.gameObject)
|
||||||
|
),
|
||||||
|
])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
prettyPrint,
|
prettyPrint,
|
||||||
settings,
|
settings,
|
||||||
SetViewAreaActionCommand,
|
SetViewAreaActionCommand,
|
||||||
|
StepCommand,
|
||||||
TransportEvents,
|
TransportEvents,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import io from 'socket.io-client';
|
import io from 'socket.io-client';
|
||||||
|
|
@ -23,7 +24,6 @@ import { MouseListener } from './commands/generators/mouse-listener';
|
||||||
import { TouchListener } from './commands/generators/touch-listener';
|
import { TouchListener } from './commands/generators/touch-listener';
|
||||||
import { CommandReceiverSocket } from './commands/receivers/command-receiver-socket';
|
import { CommandReceiverSocket } from './commands/receivers/command-receiver-socket';
|
||||||
import { RenderCommand } from './commands/types/render';
|
import { RenderCommand } from './commands/types/render';
|
||||||
import { StepCommand } from './commands/types/step';
|
|
||||||
import { Configuration } from './config/configuration';
|
import { Configuration } from './config/configuration';
|
||||||
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
||||||
import { rgb } from './helper/rgb';
|
import { rgb } from './helper/rgb';
|
||||||
|
|
@ -142,7 +142,7 @@ export class Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
private gameLoop(time: DOMHighResTimeStamp) {
|
private gameLoop(time: DOMHighResTimeStamp) {
|
||||||
const deltaTime = this.deltaTimeCalculator.getNextDeltaTime(time);
|
const deltaTime = this.deltaTimeCalculator.getNextDeltaTimeInMilliseconds(time);
|
||||||
this.keyboardListener.generateCommands();
|
this.keyboardListener.generateCommands();
|
||||||
|
|
||||||
if (this.gameObjects.camera) {
|
if (this.gameObjects.camera) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ export class DeltaTimeCalculator {
|
||||||
document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
|
document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNextDeltaTime(currentTime: DOMHighResTimeStamp): DOMHighResTimeStamp {
|
public getNextDeltaTimeInMilliseconds(
|
||||||
|
currentTime: DOMHighResTimeStamp
|
||||||
|
): DOMHighResTimeStamp {
|
||||||
if (this.previousTime === null) {
|
if (this.previousTime === null) {
|
||||||
this.previousTime = currentTime;
|
this.previousTime = currentTime;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import {
|
||||||
DeleteObjectsCommand,
|
DeleteObjectsCommand,
|
||||||
GameObject,
|
GameObject,
|
||||||
Id,
|
Id,
|
||||||
|
StepCommand,
|
||||||
UpdateObjectsCommand,
|
UpdateObjectsCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { StepCommand } from '../commands/types/step';
|
|
||||||
import { deserialize, deserializeJsonArray } from '../transport/deserialize';
|
import { deserialize, deserializeJsonArray } from '../transport/deserialize';
|
||||||
import { Camera } from './camera';
|
import { Camera } from './camera';
|
||||||
import { CharacterView } from './character-view';
|
import { CharacterView } from './character-view';
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"hosting": {
|
|
||||||
"public": "dist",
|
|
||||||
"ignore": [
|
|
||||||
"firebase.json",
|
|
||||||
"**/.*",
|
|
||||||
"**/node_modules/**"
|
|
||||||
],
|
|
||||||
"rewrites": [
|
|
||||||
{
|
|
||||||
"source": "**",
|
|
||||||
"destination": "/index.html"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Command } from 'shared';
|
import { Command } from '../command';
|
||||||
|
|
||||||
export class StepCommand extends Command {
|
export class StepCommand extends Command {
|
||||||
public static readonly type = 'StepCommand';
|
public static readonly type = 'StepCommand';
|
||||||
|
|
@ -3,6 +3,7 @@ export * from './commands/types/create-objects';
|
||||||
export * from './commands/types/create-player';
|
export * from './commands/types/create-player';
|
||||||
export * from './commands/types/delete-objects';
|
export * from './commands/types/delete-objects';
|
||||||
export * from './commands/types/update-objects';
|
export * from './commands/types/update-objects';
|
||||||
|
export * from './commands/types/step';
|
||||||
export * from './commands/types/ternary-action';
|
export * from './commands/types/ternary-action';
|
||||||
export * from './commands/types/move-action';
|
export * from './commands/types/move-action';
|
||||||
export * from './commands/types/set-view-area-action';
|
export * from './commands/types/set-view-area-action';
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ export const settings = {
|
||||||
hitDetectionCirclePointCount: 32,
|
hitDetectionCirclePointCount: 32,
|
||||||
hitDetectionMaxOverlap: 0.01,
|
hitDetectionMaxOverlap: 0.01,
|
||||||
physicsMaxStep: 5,
|
physicsMaxStep: 5,
|
||||||
gravitationalForce: vec2.fromValues(0, -0.015),
|
gravitationalForce: vec2.fromValues(0, -0.005),
|
||||||
maxVelocityX: 1.5,
|
maxVelocityX: 1.5,
|
||||||
maxVelocityY: 8,
|
maxVelocityY: 20,
|
||||||
velocityAttenuation: 0.99,
|
velocityAttenuation: 0.98,
|
||||||
inViewAreaSize: 1920 * 1080 * 3,
|
inViewAreaSize: 1920 * 1080 * 3,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue