WIP
This commit is contained in:
parent
37954e2ef1
commit
cd75bdbfde
20 changed files with 253 additions and 131 deletions
|
|
@ -25,9 +25,6 @@ Random.seed = 42;
|
||||||
|
|
||||||
const objects = new PhysicalContainer();
|
const objects = new PhysicalContainer();
|
||||||
createDungeon(objects);
|
createDungeon(objects);
|
||||||
createDungeon(objects);
|
|
||||||
createDungeon(objects);
|
|
||||||
createDungeon(objects);
|
|
||||||
|
|
||||||
objects.initialize();
|
objects.initialize();
|
||||||
|
|
||||||
|
|
@ -99,15 +96,6 @@ const handlePhysics = () => {
|
||||||
console.log(players.map((p) => p.latency));
|
console.log(players.map((p) => p.latency));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deltas.length > 100) {
|
|
||||||
deltas.sort((a, b) => a - b);
|
|
||||||
console.log(`Median physics time: ${deltas[50].toFixed(2)} ms`);
|
|
||||||
console.log(
|
|
||||||
`Memory used: ${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)} MB`,
|
|
||||||
);
|
|
||||||
deltas = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
objects.sendCommand(step);
|
objects.sendCommand(step);
|
||||||
players.forEach((p) => p.sendCommand(step));
|
players.forEach((p) => p.sendCommand(step));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,28 @@
|
||||||
import { vec2, vec3 } from 'gl-matrix';
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
import { Random } from 'shared';
|
import { Random } from 'shared';
|
||||||
import { LampPhysical } from '../objects/lamp-physical';
|
import { LampPhysical } from '../objects/lamp-physical';
|
||||||
|
|
||||||
import { TunnelPhysical } from '../objects/tunnel-physical';
|
import { TunnelPhysical } from '../objects/tunnel-physical';
|
||||||
import { PhysicalContainer } from '../physics/containers/physical-container';
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
|
|
||||||
export const createDungeon = (objects: PhysicalContainer) => {
|
export const createDungeon = (objects: PhysicalContainer) => {
|
||||||
let previousRadius = 350;
|
const lightPositions: Array<vec2> = [];
|
||||||
let previousEnd = vec2.create();
|
|
||||||
|
|
||||||
let tunnelsCountSinceLastLight = 0;
|
for (let j = 0; j < 6; j++) {
|
||||||
|
let previousRadius = 500;
|
||||||
|
let previousEnd = vec2.fromValues(
|
||||||
|
j === 0 ? 0 : Random.getRandomInRange(-1000, 1000),
|
||||||
|
j === 0 ? 0 : Random.getRandomInRange(-1000, 1000),
|
||||||
|
);
|
||||||
|
for (let i = 0; i < 500; i++) {
|
||||||
|
const delta = vec2.fromValues(j % 2 ? 1 : -1, Random.getRandomInRange(-1, 1));
|
||||||
|
|
||||||
for (let i = 0; i < 50000; i += 500) {
|
vec2.normalize(delta, delta);
|
||||||
const deltaHeight = (Random.getRandom() - 0.5) * 500;
|
vec2.scale(delta, delta, 500);
|
||||||
const height = previousEnd.y + deltaHeight;
|
|
||||||
const currentEnd = vec2.fromValues(i, height);
|
const currentEnd = vec2.add(delta, delta, previousEnd);
|
||||||
const currentToRadius = Random.getRandom() * 300 + 150;
|
|
||||||
|
const currentToRadius = Random.getRandom() * 250 + 150;
|
||||||
|
|
||||||
const tunnel = new TunnelPhysical(
|
const tunnel = new TunnelPhysical(
|
||||||
previousEnd,
|
previousEnd,
|
||||||
|
|
@ -25,21 +33,29 @@ export const createDungeon = (objects: PhysicalContainer) => {
|
||||||
|
|
||||||
objects.addObject(tunnel);
|
objects.addObject(tunnel);
|
||||||
|
|
||||||
if (++tunnelsCountSinceLastLight > 3 && Random.getRandom() > 0.7) {
|
if (Random.getRandom() > 0.7) {
|
||||||
|
const position = currentEnd;
|
||||||
|
if (!lightPositions.find((p) => vec2.dist(p, position) < 2000)) {
|
||||||
|
lightPositions.push(position);
|
||||||
objects.addObject(
|
objects.addObject(
|
||||||
new LampPhysical(
|
new LampPhysical(
|
||||||
currentEnd,
|
currentEnd,
|
||||||
vec3.normalize(
|
vec3.normalize(
|
||||||
vec3.create(),
|
vec3.create(),
|
||||||
vec3.fromValues(Random.getRandom(), 0, Random.getRandom()),
|
vec3.fromValues(
|
||||||
|
Random.getRandomInRange(0.5, 1),
|
||||||
|
0,
|
||||||
|
Random.getRandomInRange(0.5, 1),
|
||||||
),
|
),
|
||||||
0.5,
|
),
|
||||||
|
Random.getRandomInRange(0.5, 1),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
tunnelsCountSinceLastLight = 0;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
previousEnd = currentEnd;
|
previousEnd = currentEnd;
|
||||||
previousRadius = currentToRadius;
|
previousRadius = currentToRadius;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -44,19 +44,22 @@ export class CharacterPhysical extends CharacterBase implements Physical {
|
||||||
constructor(private readonly container: PhysicalContainer) {
|
constructor(private readonly container: PhysicalContainer) {
|
||||||
super(id());
|
super(id());
|
||||||
this.head = new CirclePhysical(
|
this.head = new CirclePhysical(
|
||||||
vec2.clone(CharacterPhysical.headOffset),
|
//vec2.clone(CharacterPhysical.headOffset),
|
||||||
|
[-2952.911, 215.241],
|
||||||
50,
|
50,
|
||||||
this,
|
this,
|
||||||
container,
|
container,
|
||||||
);
|
);
|
||||||
this.leftFoot = new CirclePhysical(
|
this.leftFoot = new CirclePhysical(
|
||||||
vec2.clone(CharacterPhysical.leftFootOffset),
|
// vec2.clone(CharacterPhysical.leftFootOffset),
|
||||||
|
[-2930.603, 162.542],
|
||||||
20,
|
20,
|
||||||
this,
|
this,
|
||||||
container,
|
container,
|
||||||
);
|
);
|
||||||
this.rightFoot = new CirclePhysical(
|
this.rightFoot = new CirclePhysical(
|
||||||
vec2.clone(CharacterPhysical.rightFootOffset),
|
//vec2.clone(CharacterPhysical.rightFootOffset),
|
||||||
|
[-2973.152, 167.921],
|
||||||
20,
|
20,
|
||||||
this,
|
this,
|
||||||
container,
|
container,
|
||||||
|
|
|
||||||
|
|
@ -136,12 +136,6 @@ export class CirclePhysical implements Circle, Physical {
|
||||||
|
|
||||||
if (hitSurface) {
|
if (hitSurface) {
|
||||||
vec2.scale(this.velocity, tangent!, vec2.dot(tangent!, this.velocity));
|
vec2.scale(this.velocity, tangent!, vec2.dot(tangent!, this.velocity));
|
||||||
if (
|
|
||||||
vec2.length(this.velocity) <
|
|
||||||
settings.frictionMinVelocity * deltaTimeInSeconds
|
|
||||||
) {
|
|
||||||
this.velocity = vec2.create();
|
|
||||||
}
|
|
||||||
wasHit = true;
|
wasHit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
61
backend/src/objects/projectile-physical.ts
Normal file
61
backend/src/objects/projectile-physical.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import {
|
||||||
|
id,
|
||||||
|
StepCommand,
|
||||||
|
settings,
|
||||||
|
CommandExecutors,
|
||||||
|
serializesTo,
|
||||||
|
ProjectileBase,
|
||||||
|
} from 'shared';
|
||||||
|
import { ImmutableBoundingBox } from '../physics/bounding-boxes/immutable-bounding-box';
|
||||||
|
import { CirclePhysical } from './circle-physical';
|
||||||
|
import { Physical } from '../physics/physical';
|
||||||
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
|
|
||||||
|
@serializesTo(ProjectileBase)
|
||||||
|
export class ProjectilePhysical extends ProjectileBase implements Physical {
|
||||||
|
public readonly canCollide = true;
|
||||||
|
public readonly isInverted = false;
|
||||||
|
public readonly canMove = true;
|
||||||
|
|
||||||
|
public object: CirclePhysical;
|
||||||
|
|
||||||
|
protected commandExecutors: CommandExecutors = {
|
||||||
|
[StepCommand.type]: this.step.bind(this),
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
center: vec2,
|
||||||
|
radius: number,
|
||||||
|
startingForce: vec2,
|
||||||
|
readonly container: PhysicalContainer,
|
||||||
|
) {
|
||||||
|
super(id(), center, radius);
|
||||||
|
this.object = new CirclePhysical(center, radius, this, container);
|
||||||
|
this.object.applyForce(startingForce, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _boundingBox?: ImmutableBoundingBox;
|
||||||
|
|
||||||
|
public get boundingBox(): ImmutableBoundingBox {
|
||||||
|
if (!this._boundingBox) {
|
||||||
|
this._boundingBox = (this.object as CirclePhysical).boundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._boundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get gameObject(): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public distance(target: vec2): number {
|
||||||
|
return this.object.distance(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public step(c: StepCommand) {
|
||||||
|
const deltaTime = c.deltaTimeInMiliseconds / 1000;
|
||||||
|
this.object.applyForce(settings.gravitationalForce, deltaTime);
|
||||||
|
this.object.step(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { rotate90Deg, settings } from 'shared';
|
import { Circle, rotate90Deg } from 'shared';
|
||||||
import { CirclePhysical } from '../objects/circle-physical';
|
import { CirclePhysical } from '../objects/circle-physical';
|
||||||
import { Physical } from './physical';
|
import { Physical } from './physical';
|
||||||
|
|
||||||
|
|
@ -13,71 +13,47 @@ export const moveCircle = (
|
||||||
normal?: vec2;
|
normal?: vec2;
|
||||||
tangent?: vec2;
|
tangent?: vec2;
|
||||||
} => {
|
} => {
|
||||||
|
const nextCircle = new Circle(vec2.clone(circle.center), circle.radius);
|
||||||
|
vec2.add(nextCircle.center, nextCircle.center, delta);
|
||||||
|
|
||||||
|
possibleIntersectors = possibleIntersectors.filter(
|
||||||
|
(b) => b.gameObject !== circle.gameObject && b.canCollide,
|
||||||
|
);
|
||||||
|
|
||||||
|
const getSdfAtPoint = (point: vec2): number => {
|
||||||
|
const sdf = possibleIntersectors
|
||||||
|
.filter((i) => i.isInverted)
|
||||||
|
.reduce((min, i) => (min = Math.max(min, -i.distance(point))), -1000);
|
||||||
|
|
||||||
|
return possibleIntersectors
|
||||||
|
.filter((i) => !i.isInverted)
|
||||||
|
.reduce((min, i) => (min = Math.min(min, i.distance(point))), sdf);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sdfAtCenter = getSdfAtPoint(nextCircle.center);
|
||||||
|
|
||||||
|
if (sdfAtCenter > nextCircle.radius) {
|
||||||
circle.center = vec2.add(circle.center, circle.center, delta);
|
circle.center = vec2.add(circle.center, circle.center, delta);
|
||||||
|
|
||||||
const intersecting = possibleIntersectors.filter(
|
|
||||||
(b) =>
|
|
||||||
b.gameObject !== circle.gameObject && circle.areIntersecting(b) && b.canCollide,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (intersecting.length === 0) {
|
|
||||||
return {
|
return {
|
||||||
realDelta: delta,
|
realDelta: delta,
|
||||||
hitSurface: false,
|
hitSurface: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const points = circle.getPerimeterPoints(settings.hitDetectionCirclePointCount);
|
const dx =
|
||||||
|
getSdfAtPoint(vec2.add(vec2.create(), nextCircle.center, vec2.fromValues(1, 0))) -
|
||||||
|
sdfAtCenter;
|
||||||
|
const dy =
|
||||||
|
getSdfAtPoint(vec2.add(vec2.create(), nextCircle.center, vec2.fromValues(0, 1))) -
|
||||||
|
sdfAtCenter;
|
||||||
|
const normal = vec2.fromValues(dx, dy);
|
||||||
|
|
||||||
const distancesOfPoints = points
|
vec2.normalize(normal, normal);
|
||||||
.map((point) => ({
|
|
||||||
point,
|
|
||||||
closest: intersecting
|
|
||||||
.map((i) => ({
|
|
||||||
inverted: i.isInverted,
|
|
||||||
distance: i.distance(point),
|
|
||||||
}))
|
|
||||||
.sort((a, b) => a.distance - b.distance)[0],
|
|
||||||
}))
|
|
||||||
.filter((i) => i.closest);
|
|
||||||
|
|
||||||
const distancesOfIntersectingPoints = distancesOfPoints.filter(
|
|
||||||
(d) =>
|
|
||||||
(d.closest.distance > 0 && d.closest.inverted) ||
|
|
||||||
(d.closest.distance < 0 && !d.closest.inverted),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (distancesOfIntersectingPoints.length === 0) {
|
|
||||||
return {
|
|
||||||
realDelta: delta,
|
|
||||||
hitSurface: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const deltas = distancesOfIntersectingPoints.map((pointDistance) => {
|
|
||||||
vec2.subtract(pointDistance.point, circle.center, pointDistance.point);
|
|
||||||
vec2.normalize(pointDistance.point, pointDistance.point);
|
|
||||||
vec2.scale(
|
|
||||||
pointDistance.point,
|
|
||||||
pointDistance.point,
|
|
||||||
(pointDistance.closest.inverted ? 1 : -1) * pointDistance.closest.distance,
|
|
||||||
);
|
|
||||||
return pointDistance.point;
|
|
||||||
});
|
|
||||||
|
|
||||||
const approxNormal = deltas.reduce(
|
|
||||||
(sum, current) => vec2.add(sum, sum, current),
|
|
||||||
vec2.create(),
|
|
||||||
);
|
|
||||||
vec2.scale(approxNormal, approxNormal, 1 / deltas.length);
|
|
||||||
|
|
||||||
circle.center = vec2.add(circle.center, circle.center, approxNormal);
|
|
||||||
|
|
||||||
vec2.normalize(approxNormal, approxNormal);
|
|
||||||
return {
|
return {
|
||||||
realDelta: delta,
|
realDelta: delta,
|
||||||
hitSurface: true,
|
hitSurface: true,
|
||||||
normal: approxNormal,
|
normal,
|
||||||
tangent: rotate90Deg(approxNormal),
|
tangent: rotate90Deg(normal),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
import {
|
import {
|
||||||
CommandExecutors,
|
CommandExecutors,
|
||||||
CommandReceiver,
|
CommandReceiver,
|
||||||
|
|
@ -11,9 +12,11 @@ import {
|
||||||
StepCommand,
|
StepCommand,
|
||||||
SetAspectRatioActionCommand,
|
SetAspectRatioActionCommand,
|
||||||
calculateViewArea,
|
calculateViewArea,
|
||||||
|
SecondaryActionCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { getTimeInMilliseconds } from '../helper/get-time-in-milliseconds';
|
import { getTimeInMilliseconds } from '../helper/get-time-in-milliseconds';
|
||||||
import { CharacterPhysical } from '../objects/character-physical';
|
import { CharacterPhysical } from '../objects/character-physical';
|
||||||
|
import { ProjectilePhysical } from '../objects/projectile-physical';
|
||||||
|
|
||||||
import { BoundingBox } from '../physics/bounding-boxes/bounding-box';
|
import { BoundingBox } from '../physics/bounding-boxes/bounding-box';
|
||||||
import { PhysicalContainer } from '../physics/containers/physical-container';
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
|
|
@ -46,6 +49,15 @@ export class Player extends CommandReceiver {
|
||||||
[SetAspectRatioActionCommand.type]: (v: SetAspectRatioActionCommand) =>
|
[SetAspectRatioActionCommand.type]: (v: SetAspectRatioActionCommand) =>
|
||||||
(this.aspectRatio = v.aspectRatio),
|
(this.aspectRatio = v.aspectRatio),
|
||||||
[MoveActionCommand.type]: (c: MoveActionCommand) => this.character.sendCommand(c),
|
[MoveActionCommand.type]: (c: MoveActionCommand) => this.character.sendCommand(c),
|
||||||
|
[SecondaryActionCommand.type]: (c: SecondaryActionCommand) => {
|
||||||
|
const start = vec2.clone(this.character.center);
|
||||||
|
const direction = vec2.subtract(vec2.create(), c.position, start);
|
||||||
|
vec2.normalize(direction, direction);
|
||||||
|
vec2.add(start, start, vec2.scale(vec2.create(), direction, 100));
|
||||||
|
const force = vec2.scale(direction, direction, 1000);
|
||||||
|
const projectile = new ProjectilePhysical(start, 20, force, this.objects);
|
||||||
|
this.objects.addObject(projectile);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -70,7 +82,6 @@ export class Player extends CommandReceiver {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.measureLatency();
|
this.measureLatency();
|
||||||
|
|
||||||
this.sendObjects();
|
this.sendObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,7 +124,7 @@ export class Player extends CommandReceiver {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.socket.emit(
|
this.socket.volatile.emit(
|
||||||
TransportEvents.ServerToPlayer,
|
TransportEvents.ServerToPlayer,
|
||||||
serialize(
|
serialize(
|
||||||
new UpdateObjectsCommand([
|
new UpdateObjectsCommand([
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import { glMatrix } from 'gl-matrix';
|
import { glMatrix } from 'gl-matrix';
|
||||||
import { CharacterBase, LampBase, overrideDeserialization, TunnelBase } from 'shared';
|
import { CharacterBase, LampBase, overrideDeserialization, TunnelBase } from 'shared';
|
||||||
|
import { ProjectileBase } from 'shared/src/objects/types/projectile-base';
|
||||||
import { Game } from './scripts/game';
|
import { Game } from './scripts/game';
|
||||||
import { CharacterView } from './scripts/objects/character-view';
|
import { CharacterView } from './scripts/objects/character-view';
|
||||||
import { LampView } from './scripts/objects/lamp-view';
|
import { LampView } from './scripts/objects/lamp-view';
|
||||||
|
import { ProjectileView } from './scripts/objects/projectile-view';
|
||||||
import { TunnelView } from './scripts/objects/tunnel-view';
|
import { TunnelView } from './scripts/objects/tunnel-view';
|
||||||
import './styles/main.scss';
|
import './styles/main.scss';
|
||||||
|
|
||||||
|
|
@ -11,6 +13,7 @@ glMatrix.setMatrixArrayType(Array);
|
||||||
overrideDeserialization(CharacterBase, CharacterView);
|
overrideDeserialization(CharacterBase, CharacterView);
|
||||||
overrideDeserialization(TunnelBase, TunnelView);
|
overrideDeserialization(TunnelBase, TunnelView);
|
||||||
overrideDeserialization(LampBase, LampView);
|
overrideDeserialization(LampBase, LampView);
|
||||||
|
overrideDeserialization(ProjectileBase, ProjectileView);
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@ import {
|
||||||
SecondaryActionCommand,
|
SecondaryActionCommand,
|
||||||
TernaryActionCommand,
|
TernaryActionCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
|
import { Game } from '../../game';
|
||||||
|
|
||||||
export class MouseListener extends CommandGenerator {
|
export class MouseListener extends CommandGenerator {
|
||||||
constructor(target: HTMLElement) {
|
constructor(target: HTMLElement, private readonly game: Game) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
target.addEventListener('mousemove', (event: MouseEvent) => {
|
target.addEventListener('mousemove', (event: MouseEvent) => {
|
||||||
|
|
@ -31,6 +32,8 @@ export class MouseListener extends CommandGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private positionFromEvent(event: MouseEvent): vec2 {
|
private positionFromEvent(event: MouseEvent): vec2 {
|
||||||
return vec2.fromValues(event.clientX, event.clientY);
|
return this.game.displayToWorldCoordinates(
|
||||||
|
vec2.fromValues(event.clientX, event.clientY),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,12 @@ import {
|
||||||
TernaryActionCommand,
|
TernaryActionCommand,
|
||||||
MoveActionCommand,
|
MoveActionCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
|
import { Game } from '../../game';
|
||||||
|
|
||||||
export class TouchListener extends CommandGenerator {
|
export class TouchListener extends CommandGenerator {
|
||||||
private previousPosition = vec2.create();
|
private previousPosition = vec2.create();
|
||||||
|
|
||||||
constructor(target: HTMLElement) {
|
constructor(target: HTMLElement, private readonly game: Game) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
target.addEventListener('touchstart', (event: TouchEvent) => {
|
target.addEventListener('touchstart', (event: TouchEvent) => {
|
||||||
|
|
@ -57,6 +58,8 @@ export class TouchListener extends CommandGenerator {
|
||||||
vec2.create(),
|
vec2.create(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return vec2.scale(center, center, 1 / event.touches.length);
|
return this.game.displayToWorldCoordinates(
|
||||||
|
vec2.scale(center, center, 1 / event.touches.length),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {
|
||||||
FilteringOptions,
|
FilteringOptions,
|
||||||
Flashlight,
|
Flashlight,
|
||||||
InvertedTunnel,
|
InvertedTunnel,
|
||||||
|
PolygonFactory,
|
||||||
Renderer,
|
Renderer,
|
||||||
renderNoise,
|
renderNoise,
|
||||||
WrapOptions,
|
WrapOptions,
|
||||||
|
|
@ -18,21 +19,22 @@ import {
|
||||||
settings,
|
settings,
|
||||||
StepCommand,
|
StepCommand,
|
||||||
TransportEvents,
|
TransportEvents,
|
||||||
|
SetAspectRatioActionCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { SetAspectRatioActionCommand } from 'shared/src/main';
|
|
||||||
import io from 'socket.io-client';
|
import io from 'socket.io-client';
|
||||||
import { KeyboardListener } from './commands/generators/keyboard-listener';
|
import { KeyboardListener } from './commands/generators/keyboard-listener';
|
||||||
import { MouseListener } from './commands/generators/mouse-listener';
|
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 { 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';
|
||||||
|
|
||||||
import { GameObjectContainer } from './objects/game-object-container';
|
import { GameObjectContainer } from './objects/game-object-container';
|
||||||
import { BlobShape } from './shapes/blob-shape';
|
import { BlobShape } from './shapes/blob-shape';
|
||||||
|
|
||||||
|
const Polygon = PolygonFactory(20);
|
||||||
export class Game {
|
export class Game {
|
||||||
public readonly gameObjects = new GameObjectContainer(this);
|
public readonly gameObjects = new GameObjectContainer(this);
|
||||||
private readonly canvas: HTMLCanvasElement = document.querySelector(
|
private readonly canvas: HTMLCanvasElement = document.querySelector(
|
||||||
|
|
@ -44,12 +46,15 @@ export class Game {
|
||||||
private overlay: HTMLElement = document.querySelector('#overlay') as HTMLDivElement;
|
private overlay: HTMLElement = document.querySelector('#overlay') as HTMLDivElement;
|
||||||
|
|
||||||
private async setupCommunication(): Promise<void> {
|
private async setupCommunication(): Promise<void> {
|
||||||
await Configuration.initialize();
|
// await Configuration.initialize();
|
||||||
|
|
||||||
this.socket = io(Configuration.servers[0], {
|
this.socket = io(
|
||||||
|
'http://localhost:3000',
|
||||||
|
/*Configuration.servers[1],*/ {
|
||||||
reconnectionDelayMax: 10000,
|
reconnectionDelayMax: 10000,
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
this.socket.on('reconnect_attempt', () => {
|
this.socket.on('reconnect_attempt', () => {
|
||||||
this.socket.io.opts.transports = ['polling', 'websocket'];
|
this.socket.io.opts.transports = ['polling', 'websocket'];
|
||||||
|
|
@ -69,22 +74,26 @@ export class Game {
|
||||||
broadcastCommands(
|
broadcastCommands(
|
||||||
[
|
[
|
||||||
new KeyboardListener(document.body),
|
new KeyboardListener(document.body),
|
||||||
new MouseListener(this.canvas),
|
new MouseListener(this.canvas, this),
|
||||||
new TouchListener(this.canvas),
|
new TouchListener(this.canvas, this),
|
||||||
],
|
],
|
||||||
[this.gameObjects, new CommandReceiverSocket(this.socket)],
|
[this.gameObjects, new CommandReceiverSocket(this.socket)],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setupRenderer(): Promise<void> {
|
private async setupRenderer(): Promise<void> {
|
||||||
const noiseTexture = await renderNoise([512, 1], 50, 1 / 10);
|
const noiseTexture = await renderNoise([64, 1], 40, 1 / 10);
|
||||||
|
|
||||||
this.renderer = await compile(
|
this.renderer = await compile(
|
||||||
this.canvas,
|
this.canvas,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
...InvertedTunnel.descriptor,
|
...InvertedTunnel.descriptor,
|
||||||
shaderCombinationSteps: [0, 2, 6, 16],
|
shaderCombinationSteps: [0, 2, 6, 16, 32],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...Polygon.descriptor,
|
||||||
|
shaderCombinationSteps: [0, 1],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...BlobShape.descriptor,
|
...BlobShape.descriptor,
|
||||||
|
|
@ -92,7 +101,7 @@ export class Game {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...Circle.descriptor,
|
...Circle.descriptor,
|
||||||
shaderCombinationSteps: [0, 2, 16],
|
shaderCombinationSteps: [0, 2, 16, 32],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...CircleLight.descriptor,
|
...CircleLight.descriptor,
|
||||||
|
|
@ -107,11 +116,12 @@ export class Game {
|
||||||
shadowTraceCount: 16,
|
shadowTraceCount: 16,
|
||||||
paletteSize: 10,
|
paletteSize: 10,
|
||||||
enableStopwatch: true,
|
enableStopwatch: true,
|
||||||
|
ignoreWebGL2: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
this.renderer.setRuntimeSettings({
|
this.renderer.setRuntimeSettings({
|
||||||
isWorldInverted: true,
|
//isWorldInverted: true,
|
||||||
ambientLight: rgb(0.35, 0.1, 0.45),
|
ambientLight: rgb(0.35, 0.1, 0.45),
|
||||||
colorPalette: [
|
colorPalette: [
|
||||||
rgb(0.4, 1, 0.6),
|
rgb(0.4, 1, 0.6),
|
||||||
|
|
@ -134,11 +144,19 @@ export class Game {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
this.renderer.addDrawable(
|
||||||
|
new Polygon([
|
||||||
|
[10.0, 10.0],
|
||||||
|
[200, 500],
|
||||||
|
[500, 400],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
this.renderer.renderDrawables();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
await Promise.all([this.setupCommunication(), this.setupRenderer()]);
|
await Promise.all([/*this.setupCommunication(),*/ this.setupRenderer()]);
|
||||||
requestAnimationFrame(this.gameLoop.bind(this));
|
// requestAnimationFrame(this.gameLoop.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public displayToWorldCoordinates(p: vec2): vec2 {
|
public displayToWorldCoordinates(p: vec2): vec2 {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export class LampView extends LampBase {
|
||||||
|
|
||||||
protected commandExecutors: CommandExecutors = {
|
protected commandExecutors: CommandExecutors = {
|
||||||
[RenderCommand.type]: (c: RenderCommand) => c.renderer.addDrawable(this.light),
|
[RenderCommand.type]: (c: RenderCommand) => c.renderer.addDrawable(this.light),
|
||||||
} as any;
|
};
|
||||||
|
|
||||||
constructor(id: Id, center: vec2, color: vec3, lightness: number) {
|
constructor(id: Id, center: vec2, color: vec3, lightness: number) {
|
||||||
super(id, center, color, lightness);
|
super(id, center, color, lightness);
|
||||||
|
|
|
||||||
18
frontend/src/scripts/objects/projectile-view.ts
Normal file
18
frontend/src/scripts/objects/projectile-view.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { Circle } from 'sdf-2d';
|
||||||
|
import { CommandExecutors, Id } from 'shared';
|
||||||
|
import { ProjectileBase } from 'shared/src/objects/types/projectile-base';
|
||||||
|
import { RenderCommand } from '../commands/types/render';
|
||||||
|
|
||||||
|
export class ProjectileView extends ProjectileBase {
|
||||||
|
private circle: Circle;
|
||||||
|
|
||||||
|
protected commandExecutors: CommandExecutors = {
|
||||||
|
[RenderCommand.type]: (c: RenderCommand) => c.renderer.addDrawable(this.circle),
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(id: Id, center: vec2, radius: number) {
|
||||||
|
super(id, center, radius);
|
||||||
|
this.circle = new Circle(center, radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ export class TunnelView extends TunnelBase {
|
||||||
|
|
||||||
protected commandExecutors: CommandExecutors = {
|
protected commandExecutors: CommandExecutors = {
|
||||||
[RenderCommand.type]: (c: RenderCommand) => c.renderer.addDrawable(this.shape),
|
[RenderCommand.type]: (c: RenderCommand) => c.renderer.addDrawable(this.shape),
|
||||||
} as any;
|
};
|
||||||
|
|
||||||
constructor(id: Id, from: vec2, to: vec2, fromRadius: number, toRadius: number) {
|
constructor(id: Id, from: vec2, to: vec2, fromRadius: number, toRadius: number) {
|
||||||
super(id, from, to, fromRadius, toRadius);
|
super(id, from, to, fromRadius, toRadius);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,19 @@ export abstract class Random {
|
||||||
Random._seed = value;
|
Random._seed = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static choose<T>(values: Array<T>): T | undefined {
|
||||||
|
const to = values.length;
|
||||||
|
if (to === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values[Math.floor(this.getRandomInRange(0, to))];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getRandomInRange(from: number, to: number): number {
|
||||||
|
return from + this.getRandom() * (to - from);
|
||||||
|
}
|
||||||
|
|
||||||
public static getRandom(): number {
|
public static getRandom(): number {
|
||||||
let t = (Random._seed += 0x6d2b79f5);
|
let t = (Random._seed += 0x6d2b79f5);
|
||||||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
t = Math.imul(t ^ (t >>> 15), t | 1);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ export * from './commands/types/update-objects';
|
||||||
export * from './commands/types/step';
|
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-aspect-ratio-action';
|
|
||||||
export * from './commands/types/primary-action';
|
export * from './commands/types/primary-action';
|
||||||
export * from './commands/types/secondary-action';
|
export * from './commands/types/secondary-action';
|
||||||
export * from './commands/command-receiver';
|
export * from './commands/command-receiver';
|
||||||
|
|
@ -33,6 +32,7 @@ export * from './transport/serialization/serializable';
|
||||||
export * from './transport/serialization/override-deserialization';
|
export * from './transport/serialization/override-deserialization';
|
||||||
export * from './objects/types/character-base';
|
export * from './objects/types/character-base';
|
||||||
export * from './objects/types/lamp-base';
|
export * from './objects/types/lamp-base';
|
||||||
|
export * from './objects/types/projectile-base';
|
||||||
export * from './objects/types/tunnel-base';
|
export * from './objects/types/tunnel-base';
|
||||||
export * from './settings';
|
export * from './settings';
|
||||||
export * from './transport/transport-events';
|
export * from './transport/transport-events';
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export class CharacterBase extends GameObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public toArray(): Array<any> {
|
public toArray(): Array<any> {
|
||||||
const { id, head, leftFoot, rightFoot } = this as any;
|
const { id, head, leftFoot, rightFoot } = this;
|
||||||
return [id, head, leftFoot, rightFoot];
|
return [id, head, leftFoot, rightFoot];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export class LampBase extends GameObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public toArray(): Array<any> {
|
public toArray(): Array<any> {
|
||||||
const { id, center, color, lightness } = this as any;
|
const { id, center, color, lightness } = this;
|
||||||
return [id, center, color, lightness];
|
return [id, center, color, lightness];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
shared/src/objects/types/projectile-base.ts
Normal file
15
shared/src/objects/types/projectile-base.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { Id } from '../../transport/identity';
|
||||||
|
import { serializable } from '../../transport/serialization/serializable';
|
||||||
|
import { GameObject } from '../game-object';
|
||||||
|
|
||||||
|
@serializable
|
||||||
|
export class ProjectileBase extends GameObject {
|
||||||
|
constructor(id: Id, public center: vec2, public radius: number) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toArray(): Array<any> {
|
||||||
|
return [this.id, this.center, this.radius];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ export class TunnelBase extends GameObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public toArray(): Array<any> {
|
public toArray(): Array<any> {
|
||||||
const { id, from, to, fromRadius, toRadius } = this as any;
|
const { id, from, to, fromRadius, toRadius } = this;
|
||||||
return [id, from, to, fromRadius, toRadius];
|
return [id, from, to, fromRadius, toRadius];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue