diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json
index c259ab0..8275ed8 100644
--- a/frontend/.eslintrc.json
+++ b/frontend/.eslintrc.json
@@ -23,6 +23,7 @@
"unused-imports/no-unused-imports-ts": "error",
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
"@typescript-eslint/no-explicit-any": "off",
- "@typescript-eslint/explicit-module-boundary-types": "off"
+ "@typescript-eslint/explicit-module-boundary-types": "off",
+ "@typescript-eslint/no-non-null-assertion": "off"
}
}
diff --git a/frontend/src/index.html b/frontend/src/index.html
index d899376..b9450ca 100644
--- a/frontend/src/index.html
+++ b/frontend/src/index.html
@@ -12,10 +12,10 @@
-
Test
+ Decla.red
-
+
diff --git a/frontend/src/index.ts b/frontend/src/index.ts
index 2204ff0..2035f62 100644
--- a/frontend/src/index.ts
+++ b/frontend/src/index.ts
@@ -3,123 +3,9 @@ import { Game } from './scripts/game';
import { Random } from './scripts/helper/random';
import './styles/main.scss';
-glMatrix.setMatrixArrayType(
- Array
-); /*
- }
- }
+glMatrix.setMatrixArrayType(Array);
- zeroes.forEach((z) => {
- const blueIndex = z.y * (width * 4) + z.x * 4 + 2;
- imageData.data[blueIndex] = 255;
- imageData.data[blueIndex - 1] = 255;
- imageData.data[blueIndex - 2] = 255;
- });
-
- console.log(errors);
-
- ctx.putImageData(imageData, 0, 0);
-};*/ //testSDF(); // extract as new image (data-uri)
-
-/*
-const testSDF = () => {
- Random.seed = 44;
-
- const objects: Array = [
- new TunnelShape(vec2.fromValues(20, 20), vec2.fromValues(40, 80), 10, 9),
- new TunnelShape(vec2.fromValues(40, 80), vec2.fromValues(60, 20), 9, 15),
- ];
-
- const getPixelValue = (position: vec2) => {
- /*const k = 0.15;
- let res = Math.pow(2, -k * (vec2.distance(position, vec2.fromValues(20, 20)) - 20));
- res += Math.pow(2, -k * (vec2.distance(position, vec2.fromValues(40, 80)) - 30));
- res += Math.pow(2, -k * (vec2.distance(position, vec2.fromValues(60, 20)) - 20));
- res = -Math.log2(res) / k;
- return -res;
-
- */ /*let min = 1000;
- for (const t of objects) {
- min = Math.min(min, t.distance(position));
- }
- if (min < 0) {
- // min = Math.min(min, vec2.distance(position, vec2.fromValues(40, 80 - 31.62)) - 31.62);
- }
- return -min;*/ /*
-const tree = new BoundingBoxTree([
- new BoundingBox(300, 550, 150, 550, 'A'),
- new BoundingBox(400, 800, 50, 200, 'B'),
- new BoundingBox(450, 500, 175, 185, 'C'),
- new BoundingBox(100, 200, 100, 500, 'D'),
- new BoundingBox(750, 950, 450, 600, 'E'),
- new BoundingBox(940, 1000, -2, 180, 'F'),
- new BoundingBox(960, 1050, 50, 190, 'G'),
- new BoundingBox(150, 900, 0, 575, 'H'),
- new BoundingBox(-10000, 10000, -10000, 10000, 'I'),
-]);
-
-tree.print();
-console.log(tree.findIntersecting(new BoundingBox(960, 1050, 50, 190, 'G')));
-*/ /*
- return -vec2.distance(position, vec2.fromValues(40, 40)) + 30;
- };
-/*
- const width = 80;
- const height = 100;
-
- const canvas = document.createElement('canvas');
- document.body.appendChild(canvas);
- // set desired size of transparent image
- canvas.width = width;
- canvas.height = height;
-
- const ctx = canvas.getContext('2d');
-
- const imageData = ctx.createImageData(width, height);
-
- const zeroes = [];
- for (let x = 0; x < width; x++) {
- console.log(x);
- for (let y = 0; y < height; y++) {
- const position = vec2.fromValues(x, y);
- const redIndex = y * (width * 4) + x * 4;
- const dist = getPixelValue(position);
- if (Math.abs(dist) < 1.5) {
- zeroes.push(position);
- }
- const blueIndex = y * (width * 4) + x * 4 + 2;
- //imageData.data[blueIndex] = dist * 10;
-
- imageData.data[redIndex + 3] = 255;
- }
- }
-
- ctx.putImageData(imageData, 0, 0);
-
- console.log(zeroes);
- const errors = [];
- for (let x = 0; x < width; x++) {
- console.log('verify ', x);
-
- for (let y = 0; y < height; y++) {
- const position = vec2.fromValues(x, y);
- const dist = getPixelValue(position);
-
- const realDistance = zeroes.map((z) => vec2.distance(z, position)).sort()[0];
-
- if (Math.abs(realDistance - dist) < 3) {
- const greenIndex = y * (width * 4) + x * 4 + 1;
- imageData.data[greenIndex] = 255;
- } else if (dist >= 0) {
- const redIndex = y * (width * 4) + x * 4;
- errors.push(realDistance - dist);
- imageData.data[redIndex] = 255;
- }
-
- /*if (zeroes.find((z) => vec2.distance(z, position) < dist) !== undefined) {
- const blueIndex = y * (width * 4) + x * 4 + 2;
- imageData.data[blueIndex] = 255;
- }*/ const main = async () => {
+const main = async () => {
try {
Random.seed = 42;
await new Game().start();
diff --git a/frontend/src/scripts/game.ts b/frontend/src/scripts/game.ts
index 7726d73..a3634b7 100644
--- a/frontend/src/scripts/game.ts
+++ b/frontend/src/scripts/game.ts
@@ -1,31 +1,30 @@
-import { vec3 } from 'gl-matrix';
-import { CircleLight, compile, Flashlight, Renderer } from 'sdf-2d';
+import { vec2 } from 'gl-matrix';
+import { CircleLight, compile, Flashlight, InvertedTunnel, Renderer } from 'sdf-2d';
import { CommandBroadcaster } from './commands/command-broadcaster';
import { RenderCommand } from './graphics/commands/render';
+import { DeltaTimeCalculator } from './helper/delta-time-calculator';
import { prettyPrint } from './helper/pretty-print';
+import { rgb } from './helper/rgb';
import { IGame } from './i-game';
import { KeyboardListener } from './input/keyboard-listener';
import { MouseListener } from './input/mouse-listener';
import { TouchListener } from './input/touch-listener';
-import { GameObject } from './objects/game-object';
import { Objects } from './objects/objects';
import { Camera } from './objects/types/camera';
import { Character } from './objects/types/character';
import { createDungeon } from './objects/world/create-dungeon';
+import { BoundingBoxBase } from './physics/bounds/bounding-box-base';
import { MoveToCommand } from './physics/commands/move-to';
import { StepCommand } from './physics/commands/step';
import { TeleportToCommand } from './physics/commands/teleport-to';
import { Physics } from './physics/physics';
-import { BoundingBoxBase } from './shapes/bounding-box-base';
-import { CircleShape } from './shapes/types/circle-shape';
-import { TunnelShape } from './shapes/types/tunnel-shape';
+import { settings } from './settings';
+import { BlobShape } from './shapes/types/blob-shape';
export class Game implements IGame {
public readonly objects = new Objects();
public readonly physics = new Physics();
public readonly camera = new Camera();
- private previousTime?: DOMHighResTimeStamp = null;
- private previousFpsValues: Array = [];
private character: Character;
private renderer: Renderer;
private rendererPromise: Promise;
@@ -34,8 +33,6 @@ export class Game implements IGame {
constructor() {
const canvas: HTMLCanvasElement = document.querySelector('canvas#main');
- document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
-
new CommandBroadcaster(
[
new KeyboardListener(document.body),
@@ -48,16 +45,16 @@ export class Game implements IGame {
this.rendererPromise = compile(
canvas,
[
- CircleShape.descriptor,
- TunnelShape.descriptor,
+ InvertedTunnel.descriptor,
Flashlight.descriptor,
+ BlobShape.descriptor,
CircleLight.descriptor,
],
- [
- vec3.fromValues(0, 0, 0),
- vec3.fromValues(224 / 255, 96 / 255, 126 / 255),
- vec3.fromValues(119 / 255, 173 / 255, 120 / 255),
- ]
+ {
+ shadowTraceCount: 12,
+ paletteSize: 10,
+ enableStopwatch: true,
+ }
);
this.initializeScene();
this.physics.start();
@@ -67,16 +64,22 @@ export class Game implements IGame {
this.renderer = await this.rendererPromise;
this.renderer.setRuntimeSettings({
isWorldInverted: true,
- ambientLight: vec3.fromValues(0.35, 0.1, 0.45),
- shadowLength: 300,
+ ambientLight: rgb(0.35, 0.1, 0.45),
+ colorPalette: [
+ rgb(0.4, 1, 0.6),
+ rgb(1, 1, 0),
+ rgb(0.3, 1, 1),
+ rgb(0.3, 1, 1),
+ rgb(0.3, 1, 1),
+ rgb(0.3, 1, 1),
+ rgb(0.3, 1, 1),
+ ],
+ enableHighDpiRendering: false,
+ lightCutoffDistance: settings.lightCutoffDistance,
});
requestAnimationFrame(this.gameLoop.bind(this));
}
- public addObject(o: GameObject) {
- this.objects.addObject(o);
- }
-
public get viewArea(): BoundingBoxBase {
return this.camera.viewArea;
}
@@ -86,51 +89,38 @@ export class Game implements IGame {
}
private initializeScene() {
- const start = createDungeon(this.objects, this.physics);
createDungeon(this.objects, this.physics);
+ //createDungeon(this.objects, this.physics);
- this.character = new Character(this);
- // this.physics.addDynamicBoundingBox(this.character.boundingBox);
- this.addObject(this.character);
- this.addObject(this.camera);
- let pos: any = localStorage.getItem('character-position');
- pos = pos ? JSON.parse(pos) : start.from;
+ this.character = new Character(this.physics, this);
+ this.objects.addObject(this.character);
+ this.objects.addObject(this.camera);
+ let pos: any = localStorage.getItem('characterPosition');
+ pos = pos ? JSON.parse(pos) : vec2.fromValues(0, 0);
this.character.sendCommand(new TeleportToCommand(pos));
}
- private handleVisibilityChange() {
- if (!document.hidden) {
- this.previousTime = null;
- }
- }
+ private deltaTimeCalculator = new DeltaTimeCalculator();
private gameLoop(time: DOMHighResTimeStamp) {
- if (this.previousTime === null) {
- this.previousTime = time;
- }
-
- const deltaTime = time - this.previousTime;
- this.previousTime = time;
+ const deltaTime = this.deltaTimeCalculator.getNextDeltaTime(time);
this.objects.sendCommand(new StepCommand(deltaTime));
this.camera.sendCommand(new MoveToCommand(this.character.position));
-
this.camera.sendCommand(new RenderCommand(this.renderer));
const shouldBeDrawn = this.physics
.findIntersecting(this.camera.viewArea)
- .map((b) => b.shape?.gameObject);
+ .map((b) => b.owner);
for (const object of shouldBeDrawn) {
object?.sendCommand(new RenderCommand(this.renderer));
}
- this.character.sendCommand(new RenderCommand(this.renderer));
this.renderer.renderDrawables();
this.overlay.innerText = prettyPrint(this.renderer.insights);
-
- localStorage.setItem('character-position', JSON.stringify(this.character.position));
+ localStorage.setItem('characterPosition', JSON.stringify(this.character.position));
requestAnimationFrame(this.gameLoop.bind(this));
}
}
diff --git a/frontend/src/scripts/helper/exponential-decay.ts b/frontend/src/scripts/helper/exponential-decay.ts
deleted file mode 100644
index 7f321f5..0000000
--- a/frontend/src/scripts/helper/exponential-decay.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export const exponentialDecay = (
- accumulator: number,
- nextValue: number,
- biasOfNextValue: number
-) => accumulator * (1 - biasOfNextValue) + nextValue * biasOfNextValue;
diff --git a/frontend/src/scripts/helper/get-combinations.ts b/frontend/src/scripts/helper/get-combinations.ts
deleted file mode 100644
index 42c36d6..0000000
--- a/frontend/src/scripts/helper/get-combinations.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-export const getCombinations = (values: Array>): Array> => {
- if (!values.every((a) => a.length > 0)) {
- return [];
- }
-
- const result: Array> = [];
- const counters = values.map((_) => 0);
-
- const increaseCounter = (i: number) => {
- if (i >= counters.length) {
- return false;
- }
-
- counters[i]++;
-
- if (counters[i] >= values[i].length) {
- counters[i] = 0;
- return increaseCounter(i + 1);
- }
-
- return true;
- };
-
- do {
- result.push(values.map((v, i) => v[counters[i]]));
- } while (increaseCounter(0));
-
- return result;
-};
diff --git a/frontend/src/scripts/helper/rgb.ts b/frontend/src/scripts/helper/rgb.ts
new file mode 100644
index 0000000..a6ef20a
--- /dev/null
+++ b/frontend/src/scripts/helper/rgb.ts
@@ -0,0 +1,3 @@
+import { vec3 } from 'gl-matrix';
+
+export const rgb = (r: number, g: number, b: number): vec3 => vec3.fromValues(r, g, b);
diff --git a/frontend/src/scripts/helper/rgb255.ts b/frontend/src/scripts/helper/rgb255.ts
new file mode 100644
index 0000000..56c47ff
--- /dev/null
+++ b/frontend/src/scripts/helper/rgb255.ts
@@ -0,0 +1,4 @@
+import { vec3 } from 'gl-matrix';
+
+export const rgb255 = (r: number, g: number, b: number): vec3 =>
+ vec3.fromValues(r / 255, g / 255, b / 255);
diff --git a/frontend/src/scripts/helper/wait-while-false.ts b/frontend/src/scripts/helper/wait-while-false.ts
deleted file mode 100644
index 1e0f708..0000000
--- a/frontend/src/scripts/helper/wait-while-false.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-export const waitWhileFalse = (body: () => boolean): Promise => {
- let resolveOnDone: () => void;
- let rejectOnDone: (e: Error) => void;
-
- const onDone = new Promise((resolve, reject) => {
- resolveOnDone = resolve;
- rejectOnDone = reject;
- });
-
- const waiter = () => {
- let success: boolean;
-
- try {
- success = body();
- } catch (e) {
- rejectOnDone(e);
- return;
- }
-
- if (success) {
- resolveOnDone();
- } else {
- requestAnimationFrame(waiter);
- }
- };
-
- waiter();
- return onDone;
-};
diff --git a/frontend/src/scripts/i-game.ts b/frontend/src/scripts/i-game.ts
index 7b1a0da..d7e1c40 100644
--- a/frontend/src/scripts/i-game.ts
+++ b/frontend/src/scripts/i-game.ts
@@ -1,8 +1,6 @@
-import { GameObject } from './objects/game-object';
-import { BoundingBoxBase } from './shapes/bounding-box-base';
+import { BoundingBoxBase } from './physics/bounds/bounding-box-base';
export interface IGame {
- addObject(o: GameObject): void;
readonly viewArea: BoundingBoxBase;
findIntersecting(box: BoundingBoxBase): Array;
}
diff --git a/frontend/src/scripts/objects/types/camera.ts b/frontend/src/scripts/objects/types/camera.ts
index d46d196..5bd1c32 100644
--- a/frontend/src/scripts/objects/types/camera.ts
+++ b/frontend/src/scripts/objects/types/camera.ts
@@ -2,8 +2,8 @@ import { vec2 } from 'gl-matrix';
import { RenderCommand } from '../../graphics/commands/render';
import { CursorMoveCommand } from '../../input/commands/cursor-move-command';
import { ZoomCommand } from '../../input/commands/zoom';
+import { BoundingBox } from '../../physics/bounds/bounding-box';
import { MoveToCommand } from '../../physics/commands/move-to';
-import { BoundingBox } from '../../shapes/bounding-box';
import { GameObject } from '../game-object';
export class Camera extends GameObject {
diff --git a/frontend/src/scripts/objects/types/character.ts b/frontend/src/scripts/objects/types/character.ts
index 065dc1c..ded58b3 100644
--- a/frontend/src/scripts/objects/types/character.ts
+++ b/frontend/src/scripts/objects/types/character.ts
@@ -1,41 +1,61 @@
-import { vec2, vec3 } from 'gl-matrix';
+import { vec2 } from 'gl-matrix';
import { Flashlight } from 'sdf-2d';
import { RenderCommand } from '../../graphics/commands/render';
+import { rgb } from '../../helper/rgb';
import { IGame } from '../../i-game';
import { KeyDownCommand } from '../../input/commands/key-down';
import { KeyUpCommand } from '../../input/commands/key-up';
import { SwipeCommand } from '../../input/commands/swipe';
+import { BoundingBoxBase } from '../../physics/bounds/bounding-box-base';
+import { BoundingCircle } from '../../physics/bounds/bounding-circle';
import { StepCommand } from '../../physics/commands/step';
import { TeleportToCommand } from '../../physics/commands/teleport-to';
-import { IShape } from '../../shapes/i-shape';
+import { DynamicPhysicalObject } from '../../physics/dynamic-physical-object';
+import { Physics } from '../../physics/physics';
import { BlobShape } from '../../shapes/types/blob-shape';
-import { GameObject } from '../game-object';
-export class Character extends GameObject {
+export class Character extends DynamicPhysicalObject {
+ protected head: BoundingCircle;
+ protected leftFoot: BoundingCircle;
+ protected rightFoot: BoundingCircle;
+
private keysDown: Set = new Set();
+
private light = new Flashlight(
- vec2.create(),
- vec3.fromValues(1, 0.6, 0.45),
- 1.5,
+ vec2.fromValues(0, 0),
+ rgb(1, 0.6, 0.45),
+ 0.5,
vec2.fromValues(-1, 0)
);
- private shape = new BlobShape(vec2.create());
+
+ private shape = new BlobShape();
+ private boundingCircle = new BoundingCircle(this, vec2.create(), 50);
+ private center = vec2.create();
private static speed = 1.5;
- constructor(private game: IGame) {
- super();
+ constructor(physics: Physics, private game: IGame) {
+ super(physics, true);
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
this.addCommandExecutor(TeleportToCommand, (c) => this.setPosition(c.position));
this.addCommandExecutor(KeyDownCommand, (c) => this.keysDown.add(c.key));
this.addCommandExecutor(KeyUpCommand, (c) => this.keysDown.delete(c.key));
+ this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
this.addCommandExecutor(SwipeCommand, (c) => {
this.tryMoving(vec2.multiply(vec2.create(), c.delta, this.game.viewArea.size));
});
+
+ this.head = new BoundingCircle(this, this.center, 50);
+ this.leftFoot = new BoundingCircle(this, this.center, 50);
+ this.rightFoot = new BoundingCircle(this, this.center, 50);
+
+ this.shape.setCircles([this.head, this.leftFoot, this.rightFoot]);
+
+ this.addToPhysics();
}
- public get position(): vec2 {
- return this.shape.center;
+ public distance(target: vec2): number {
+ return this.shape.minDistance(target);
}
private draw(c: RenderCommand) {
@@ -43,73 +63,24 @@ export class Character extends GameObject {
c.renderer.addDrawable(this.light);
}
- private tryMoving(delta: vec2, isFirstIteration = true) {
- const maxStep = 2;
- if (vec2.length(delta) > maxStep) {
- let steppedDelta = vec2.normalize(vec2.create(), delta);
- vec2.scale(steppedDelta, steppedDelta, maxStep - 0.001);
-
- for (let i = 0; i <= vec2.length(delta) / maxStep - 1; i++) {
- this.tryMoving(vec2.clone(steppedDelta), isFirstIteration);
- }
-
- steppedDelta = vec2.normalize(vec2.create(), delta);
-
- vec2.scale(steppedDelta, steppedDelta, vec2.length(delta) % maxStep);
- this.tryMoving(vec2.clone(steppedDelta), isFirstIteration);
-
- return;
- }
-
- const nextShape = this.shape.clone();
- vec2.add(nextShape.center, nextShape.center, delta);
-
- const nextNearShapes = this.getNearShapesTo(nextShape);
-
- if (nextNearShapes.length && nextNearShapes[0].distance < 0) {
- this.setPosition(nextShape.center);
- } else {
- if (!isFirstIteration) {
- return;
- }
-
- const currentNearShapes = this.getNearShapesTo(this.shape);
- const intersecting = nextNearShapes
- .filter(
- (n) =>
- currentNearShapes.find((c) => c.shape === n.shape && c.distance <= 0) !==
- undefined
- )
- .sort((e) => Math.abs(e.distance));
-
- if (intersecting.length < 1) {
- return;
- }
- const normal = intersecting[0].shape.normal(this.shape.center);
-
- const maxDistance = intersecting.reduce((p, c) => (p.distance > c.distance ? p : c))
- .distance;
-
- vec2.add(delta, delta, vec2.scale(vec2.create(), normal, -maxDistance - 2));
-
- this.tryMoving(delta, false);
- }
+ public get position(): vec2 {
+ return this.center;
}
- private getNearShapesTo(shape: BlobShape): Array<{ shape: IShape; distance: number }> {
- return this.game
- .findIntersecting(shape.boundingBox)
- .filter((b) => b.shape)
- .map((b) => ({
- shape: b.shape,
- // TODO: fix this
- distance: b.shape.minDistance(shape.center) + shape.radius - 20,
- }))
- .sort((e) => e.distance);
+ public getBoundingCircles(): Array {
+ return [this.boundingCircle];
+ }
+
+ public getBoundingBox(): BoundingBoxBase {
+ return this.head.boundingBox;
+ }
+
+ private tryMoving(delta: vec2) {
+ this.physics.tryMovingDynamicCircle(this.head, delta);
}
private setPosition(value: vec2) {
- this.shape.position = value;
+ //this.head.center = value;
this.light.center = vec2.add(vec2.create(), value, vec2.fromValues(50, -40));
}
diff --git a/frontend/src/scripts/objects/types/lamp.ts b/frontend/src/scripts/objects/types/lamp.ts
index 6be9793..1817f80 100644
--- a/frontend/src/scripts/objects/types/lamp.ts
+++ b/frontend/src/scripts/objects/types/lamp.ts
@@ -1,21 +1,40 @@
import { vec2, vec3 } from 'gl-matrix';
import { CircleLight } from 'sdf-2d';
import { RenderCommand } from '../../graphics/commands/render';
+import { BoundingBoxBase } from '../../physics/bounds/bounding-box-base';
+import { ImmutableBoundingBox } from '../../physics/bounds/immutable-bounding-box';
import { MoveToCommand } from '../../physics/commands/move-to';
-import { GameObject } from '../game-object';
+import { PhysicalObject } from '../../physics/physical-object';
+import { Physics } from '../../physics/physics';
+import { settings } from '../../settings';
-export class Lamp extends GameObject {
+export class Lamp extends PhysicalObject {
private light: CircleLight;
- constructor(center: vec2, color: vec3, lightness: number) {
- super();
+ constructor(center: vec2, color: vec3, lightness: number, physics: Physics) {
+ super(physics, false);
this.light = new CircleLight(center, color, lightness);
+ this.addToPhysics();
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
}
+ public distance(target: vec2): number {
+ return this.light.minDistance(target);
+ }
+
+ public getBoundingBox(): BoundingBoxBase {
+ return new ImmutableBoundingBox(
+ this,
+ this.light.center.x - settings.lightCutoffDistance,
+ this.light.center.x + settings.lightCutoffDistance,
+ this.light.center.y - settings.lightCutoffDistance,
+ this.light.center.y + settings.lightCutoffDistance
+ );
+ }
+
private draw(c: RenderCommand) {
c.renderer.addDrawable(this.light);
}
diff --git a/frontend/src/scripts/objects/types/tunnel.ts b/frontend/src/scripts/objects/types/tunnel.ts
index b5c417a..60f1758 100644
--- a/frontend/src/scripts/objects/types/tunnel.ts
+++ b/frontend/src/scripts/objects/types/tunnel.ts
@@ -1,26 +1,52 @@
import { vec2 } from 'gl-matrix';
import { RenderCommand } from '../../graphics/commands/render';
+import { BoundingBoxBase } from '../../physics/bounds/bounding-box-base';
+import { ImmutableBoundingBox } from '../../physics/bounds/immutable-bounding-box';
import { Physics } from '../../physics/physics';
+import { StaticPhysicalObject } from '../../physics/static-physical-object';
import { TunnelShape } from '../../shapes/types/tunnel-shape';
-import { GameObject } from '../game-object';
-export class Tunnel extends GameObject {
+export class Tunnel extends StaticPhysicalObject {
private shape: TunnelShape;
constructor(
physics: Physics,
- public readonly from: vec2,
+ from: vec2,
to: vec2,
fromRadius: number,
toRadius: number
) {
- super();
+ super(physics, true);
+ this.shape = new TunnelShape(from, to, fromRadius, toRadius);
+ this.addToPhysics();
- this.shape = new TunnelShape(from, to, fromRadius, toRadius, this);
- physics.addStaticBoundingBox(this.shape.boundingBox);
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
}
+ public distance(target: vec2): number {
+ return this.shape.minDistance(target);
+ }
+
+ public getBoundingBox(): BoundingBoxBase {
+ const xMin = Math.min(
+ this.shape.from.x - this.shape.fromRadius,
+ this.shape.to.x - this.shape.toRadius
+ );
+ const yMin = Math.min(
+ this.shape.from.y - this.shape.fromRadius,
+ this.shape.to.y - this.shape.toRadius
+ );
+ const xMax = Math.max(
+ this.shape.from.x + this.shape.fromRadius,
+ this.shape.to.x + this.shape.toRadius
+ );
+ const yMax = Math.max(
+ this.shape.from.y + this.shape.fromRadius,
+ this.shape.to.y + this.shape.toRadius
+ );
+ return new ImmutableBoundingBox(this, xMin, xMax, yMin, yMax, true);
+ }
+
private draw(c: RenderCommand) {
c.renderer.addDrawable(this.shape);
}
diff --git a/frontend/src/scripts/objects/world/create-dungeon.ts b/frontend/src/scripts/objects/world/create-dungeon.ts
index b1e36fc..a278247 100644
--- a/frontend/src/scripts/objects/world/create-dungeon.ts
+++ b/frontend/src/scripts/objects/world/create-dungeon.ts
@@ -1,14 +1,15 @@
-import { vec2 } from 'gl-matrix';
+import { vec2, vec3 } from 'gl-matrix';
import { Random } from '../../helper/random';
import { Physics } from '../../physics/physics';
import { Objects } from '../objects';
+import { Lamp } from '../types/lamp';
import { Tunnel } from '../types/tunnel';
-export const createDungeon = (objects: Objects, physics: Physics): Tunnel => {
+export const createDungeon = (objects: Objects, physics: Physics) => {
let previousRadius = 350;
let previousEnd = vec2.create();
- let first: Tunnel;
+ let tunnelsCountSinceLastLight = 0;
for (let i = 0; i < 500000; i += 500) {
const deltaHeight = (Random.getRandom() - 0.5) * 2000;
@@ -24,30 +25,24 @@ export const createDungeon = (objects: Objects, physics: Physics): Tunnel => {
currentToRadius
);
- if (!first) {
- first = tunnel;
- }
-
objects.addObject(tunnel);
- /* if (deltaHeight > 0 && Random.getRandom() > 0.8) {
+ if (++tunnelsCountSinceLastLight > 3 && Random.getRandom() > 0.6) {
objects.addObject(
new Lamp(
currentEnd,
- Random.getRandom() * 20 + 30,
- vec3.scale(
+ vec3.normalize(
vec3.create(),
- vec3.normalize(vec3.create(), vec3.fromValues(0.5, 0.1, 0.8)),
- Random.getRandom() * 0.5 + 0.5
+ vec3.fromValues(Random.getRandom(), 0, Random.getRandom())
),
- 1
+ 0.5,
+ physics
)
);
- } */
+ tunnelsCountSinceLastLight = 0;
+ }
previousEnd = currentEnd;
previousRadius = currentToRadius;
}
-
- return first;
};
diff --git a/frontend/src/scripts/shapes/bounding-box-base.ts b/frontend/src/scripts/physics/bounds/bounding-box-base.ts
similarity index 86%
rename from frontend/src/scripts/shapes/bounding-box-base.ts
rename to frontend/src/scripts/physics/bounds/bounding-box-base.ts
index 8657388..d2c7bd7 100644
--- a/frontend/src/scripts/shapes/bounding-box-base.ts
+++ b/frontend/src/scripts/physics/bounds/bounding-box-base.ts
@@ -1,13 +1,14 @@
import { vec2 } from 'gl-matrix';
-import { IShape } from './i-shape';
+import { PhysicalObject } from '../physical-object';
export abstract class BoundingBoxBase {
constructor(
- public readonly shape: IShape,
+ public readonly owner: PhysicalObject,
protected _xMin: number = 0,
protected _xMax: number = 0,
protected _yMin: number = 0,
- protected _yMax: number = 0
+ protected _yMax: number = 0,
+ public readonly isInverted = false
) {}
public get 0(): number {
diff --git a/frontend/src/scripts/shapes/bounding-box.ts b/frontend/src/scripts/physics/bounds/bounding-box.ts
similarity index 60%
rename from frontend/src/scripts/shapes/bounding-box.ts
rename to frontend/src/scripts/physics/bounds/bounding-box.ts
index 94d20c0..23bf4f7 100644
--- a/frontend/src/scripts/shapes/bounding-box.ts
+++ b/frontend/src/scripts/physics/bounds/bounding-box.ts
@@ -3,6 +3,38 @@ import { BoundingBoxBase } from './bounding-box-base';
import { ImmutableBoundingBox } from './immutable-bounding-box';
export class BoundingBox extends BoundingBoxBase {
+ public get xMin(): number {
+ return this._xMin;
+ }
+
+ public set xMin(value: number) {
+ this._xMin = value;
+ }
+
+ public set xMax(value: number) {
+ this._xMax = value;
+ }
+
+ public get xMax(): number {
+ return this._xMax;
+ }
+
+ public set yMin(value: number) {
+ this._yMin = value;
+ }
+
+ public get yMin(): number {
+ return this._yMin;
+ }
+
+ public set yMax(value: number) {
+ this._yMax = value;
+ }
+
+ public get yMax(): number {
+ return this._yMax;
+ }
+
public get topLeft(): vec2 {
return vec2.fromValues(this._xMin, this._yMax);
}
@@ -12,18 +44,18 @@ export class BoundingBox extends BoundingBoxBase {
this._yMax = value.y;
}
- public get size(): vec2 {
- return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
- }
-
public set size(value: vec2) {
this._xMax = this.xMin + value.x;
this._yMin = this.yMax - value.y;
}
+ public get size(): vec2 {
+ return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
+ }
+
public cloneAsImmutable(): ImmutableBoundingBox {
return new ImmutableBoundingBox(
- this.shape,
+ this.owner,
this.xMin,
this.xMax,
this.yMin,
diff --git a/frontend/src/scripts/physics/bounds/bounding-circle.ts b/frontend/src/scripts/physics/bounds/bounding-circle.ts
new file mode 100644
index 0000000..72a3288
--- /dev/null
+++ b/frontend/src/scripts/physics/bounds/bounding-circle.ts
@@ -0,0 +1,67 @@
+import { vec2 } from 'gl-matrix';
+import { PhysicalObject } from '../physical-object';
+import { BoundingBox } from './bounding-box';
+import { BoundingBoxBase } from './bounding-box-base';
+
+export class BoundingCircle {
+ private _boundingBox: BoundingBox;
+
+ constructor(public readonly owner, private _center: vec2, private _radius: number) {
+ this._boundingBox = new BoundingBox(owner);
+ this.recalculateBoundingBox();
+ }
+
+ public get center(): vec2 {
+ return this._center;
+ }
+
+ public set center(value: vec2) {
+ this._center = value;
+ this.recalculateBoundingBox();
+ }
+
+ public get radius(): number {
+ return this._radius;
+ }
+
+ public set radius(value: number) {
+ this._radius = value;
+ this.recalculateBoundingBox();
+ }
+
+ public distance(target: vec2): number {
+ return vec2.distance(target, this.center) - this.radius;
+ }
+
+ public areIntersecting(other: PhysicalObject): boolean {
+ return other.distance(this.center) < this.radius;
+ }
+
+ public isInside(other: PhysicalObject): boolean {
+ return other.distance(this.center) < -this.radius;
+ }
+
+ public getPerimeterPoints(count: number): Array {
+ const result: Array = [];
+ for (let i = 0; i < count; i++) {
+ result.push(
+ vec2.fromValues(
+ Math.cos((2 * Math.PI * i) / count) * this.radius + this.center.x,
+ Math.sin((2 * Math.PI * i) / count) * this.radius + this.center.y
+ )
+ );
+ }
+ return result;
+ }
+
+ public get boundingBox(): BoundingBoxBase {
+ return this._boundingBox;
+ }
+
+ private recalculateBoundingBox() {
+ this._boundingBox.xMin = this.center.x - this._radius;
+ this._boundingBox.xMax = this.center.x + this._radius;
+ this._boundingBox.yMin = this.center.y - this._radius;
+ this._boundingBox.yMax = this.center.y + this._radius;
+ }
+}
diff --git a/frontend/src/scripts/shapes/immutable-bounding-box.ts b/frontend/src/scripts/physics/bounds/immutable-bounding-box.ts
similarity index 100%
rename from frontend/src/scripts/shapes/immutable-bounding-box.ts
rename to frontend/src/scripts/physics/bounds/immutable-bounding-box.ts
diff --git a/frontend/src/scripts/physics/containers/bounding-box-list.ts b/frontend/src/scripts/physics/containers/bounding-box-list.ts
index 7a2bc6e..5da7203 100644
--- a/frontend/src/scripts/physics/containers/bounding-box-list.ts
+++ b/frontend/src/scripts/physics/containers/bounding-box-list.ts
@@ -1,4 +1,4 @@
-import { BoundingBoxBase } from '../../shapes/bounding-box-base';
+import { BoundingBoxBase } from '../bounds/bounding-box-base';
export class BoundingBoxList {
constructor(private boundingBoxes: Array = []) {}
diff --git a/frontend/src/scripts/physics/containers/bounding-box-tree.ts b/frontend/src/scripts/physics/containers/bounding-box-tree.ts
index 3cbebcb..d469be2 100644
--- a/frontend/src/scripts/physics/containers/bounding-box-tree.ts
+++ b/frontend/src/scripts/physics/containers/bounding-box-tree.ts
@@ -1,12 +1,10 @@
// source: https://github.com/ubilabs/kd-tree-javascript/blob/master/kdTree.js
-import { ImmutableBoundingBox } from '../../shapes/immutable-bounding-box';
+import { ImmutableBoundingBox } from '../bounds/immutable-bounding-box';
class Node {
public left?: Node = null;
-
public right?: Node = null;
-
constructor(public rectangle: ImmutableBoundingBox, public parent: Node) {}
}
diff --git a/frontend/src/scripts/physics/dynamic-physical-object.ts b/frontend/src/scripts/physics/dynamic-physical-object.ts
new file mode 100644
index 0000000..b697b47
--- /dev/null
+++ b/frontend/src/scripts/physics/dynamic-physical-object.ts
@@ -0,0 +1,10 @@
+import { BoundingCircle } from './bounds/bounding-circle';
+import { PhysicalObject } from './physical-object';
+
+export abstract class DynamicPhysicalObject extends PhysicalObject {
+ public abstract getBoundingCircles(): Array;
+
+ protected addToPhysics() {
+ super.addToPhysics(true);
+ }
+}
diff --git a/frontend/src/scripts/physics/physical-object.ts b/frontend/src/scripts/physics/physical-object.ts
new file mode 100644
index 0000000..7e4cb29
--- /dev/null
+++ b/frontend/src/scripts/physics/physical-object.ts
@@ -0,0 +1,21 @@
+import { vec2 } from 'gl-matrix';
+import { GameObject } from '../objects/game-object';
+import { BoundingBoxBase } from './bounds/bounding-box-base';
+import { Physics } from './physics';
+
+export abstract class PhysicalObject extends GameObject {
+ public abstract getBoundingBox(): BoundingBoxBase;
+ public abstract distance(target: vec2): number;
+
+ constructor(protected physics: Physics, public readonly canCollide: boolean) {
+ super();
+ }
+
+ protected addToPhysics(isDynamic = false) {
+ if (isDynamic) {
+ this.physics.addDynamicBoundingBox(this.getBoundingBox());
+ } else {
+ this.physics.addStaticBoundingBox(this.getBoundingBox());
+ }
+ }
+}
diff --git a/frontend/src/scripts/physics/physics.ts b/frontend/src/scripts/physics/physics.ts
index 07d1797..729d93d 100644
--- a/frontend/src/scripts/physics/physics.ts
+++ b/frontend/src/scripts/physics/physics.ts
@@ -1,15 +1,14 @@
-import { BoundingBoxTree } from './containers/bounding-box-tree';
+import { vec2 } from 'gl-matrix';
+import { BoundingBoxBase } from './bounds/bounding-box-base';
+import { BoundingCircle } from './bounds/bounding-circle';
+import { ImmutableBoundingBox } from './bounds/immutable-bounding-box';
import { BoundingBoxList } from './containers/bounding-box-list';
-import { ImmutableBoundingBox } from '../shapes/immutable-bounding-box';
-import { BoundingBoxBase } from '../shapes/bounding-box-base';
+import { BoundingBoxTree } from './containers/bounding-box-tree';
export class Physics {
private isTreeInitialized = false;
-
private staticBoundingBoxesWaitList = [];
-
private staticBoundingBoxes = new BoundingBoxTree();
-
private dynamicBoundingBoxes = new BoundingBoxList();
public addStaticBoundingBox(boundingBox: ImmutableBoundingBox) {
@@ -24,6 +23,62 @@ export class Physics {
this.dynamicBoundingBoxes.insert(boundingBox);
}
+ public tryMovingDynamicCircle(
+ circle: BoundingCircle,
+ delta: vec2,
+ invocationCount = 0
+ ) {
+ circle.center = vec2.add(circle.center, circle.center, delta);
+
+ const intersecting = this.findIntersecting(circle.boundingBox).filter(
+ (b) =>
+ b.owner !== circle.owner && circle.areIntersecting(b.owner) && b.owner.canCollide
+ );
+
+ const pointCount = 16;
+ const points = circle.getPerimeterPoints(pointCount);
+
+ const distancesOfPoints = points.map((point) => ({
+ point,
+ distances: intersecting.map((i) => i.owner.distance(point)).sort((a, b) => a - b),
+ }));
+
+ const distancesOfIntersectingPoints = distancesOfPoints.filter(
+ (d) => d.distances[0] > 0
+ );
+
+ if (distancesOfIntersectingPoints.length === 0) {
+ return;
+ }
+
+ const distanceToLeastIntersectingForEachPoint = distancesOfIntersectingPoints.map(
+ (pointDistances) => ({
+ point: pointDistances.point,
+ distance: pointDistances.distances[0],
+ })
+ );
+
+ const deltas = distanceToLeastIntersectingForEachPoint.map((pointDistance) => {
+ vec2.subtract(pointDistance.point, circle.center, pointDistance.point);
+ vec2.normalize(pointDistance.point, pointDistance.point);
+
+ vec2.scale(pointDistance.point, pointDistance.point, pointDistance.distance);
+ return pointDistance.point;
+ });
+
+ const sumDelta = vec2.fromValues(0, 0);
+
+ deltas.forEach((d) => vec2.add(sumDelta, sumDelta, d));
+
+ vec2.scale(sumDelta, sumDelta, 1 / deltas.length);
+
+ if (invocationCount > 10) {
+ return;
+ }
+
+ this.tryMovingDynamicCircle(circle, sumDelta, ++invocationCount);
+ }
+
public findIntersecting(box: BoundingBoxBase): Array {
return [
...this.staticBoundingBoxes.findIntersecting(box),
diff --git a/frontend/src/scripts/physics/static-physical-object.ts b/frontend/src/scripts/physics/static-physical-object.ts
new file mode 100644
index 0000000..cf27574
--- /dev/null
+++ b/frontend/src/scripts/physics/static-physical-object.ts
@@ -0,0 +1,7 @@
+import { PhysicalObject } from './physical-object';
+
+export abstract class StaticPhysicalObject extends PhysicalObject {
+ protected addToPhysics() {
+ super.addToPhysics(false);
+ }
+}
diff --git a/frontend/src/scripts/settings.ts b/frontend/src/scripts/settings.ts
new file mode 100644
index 0000000..15c28bb
--- /dev/null
+++ b/frontend/src/scripts/settings.ts
@@ -0,0 +1,3 @@
+export const settings = {
+ lightCutoffDistance: 600,
+};
diff --git a/frontend/src/scripts/shapes/i-shape.ts b/frontend/src/scripts/shapes/i-shape.ts
deleted file mode 100644
index a2b48d1..0000000
--- a/frontend/src/scripts/shapes/i-shape.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { vec2 } from 'gl-matrix';
-import { GameObject } from '../objects/game-object';
-import { BoundingBox } from './bounding-box';
-
-export interface IShape {
- readonly boundingBox: BoundingBox;
- readonly gameObject?: GameObject;
-
- minDistance(target: vec2): number;
- normal(from: vec2): vec2;
- clone(): IShape;
-}
diff --git a/frontend/src/scripts/shapes/types/blob-shape.ts b/frontend/src/scripts/shapes/types/blob-shape.ts
index d88a8df..365e74e 100644
--- a/frontend/src/scripts/shapes/types/blob-shape.ts
+++ b/frontend/src/scripts/shapes/types/blob-shape.ts
@@ -1,110 +1,89 @@
import { mat2d, vec2 } from 'gl-matrix';
-import { Circle, Drawable, DrawableDescriptor } from 'sdf-2d';
-import { GameObject } from '../../objects/game-object';
-import { BoundingBox } from '../bounding-box';
-import { IShape } from '../i-shape';
-import { CircleShape } from './circle-shape';
+import { Drawable, DrawableDescriptor } from 'sdf-2d';
+import { BoundingCircle } from '../../physics/bounds/bounding-circle';
-export class BlobShape extends Drawable implements IShape {
+export class BlobShape extends Drawable {
public static descriptor: DrawableDescriptor = {
sdf: {
shader: `
- uniform struct {
- vec2 headCenter;
- vec2 leftFootCenter;
- vec2 rightFootCenter;
- float headRadius;
- float footRadius;
- float k;
- }[BLOB_COUNT] blobs;
+ uniform vec2 headCenters[BLOB_COUNT];
+ uniform vec2 leftFootCenters[BLOB_COUNT];
+ uniform vec2 rightFootCenters[BLOB_COUNT];
+ uniform float headRadii[BLOB_COUNT];
+ uniform float footRadii[BLOB_COUNT];
+ //uniform float ks[BLOB_COUNT];
- float smoothMin(float a, float b)
- {
- const float k = 80.0;
- float res = exp2( -k*a ) + exp2( -k*b );
- return -log2( res )/k;
+ float smoothMin(float a, float b)
+ {
+ const float k = 80.0;
+ float res = exp2( -k*a ) + exp2( -k*b );
+ return -log2( res )/k;
+ }
+
+ float circleDistance(vec2 circleCenter, float radius, vec2 target) {
+ return distance(target, circleCenter) - radius;
+ }
+
+ float blobMinDistance(vec2 target, out float colorIndex) {
+ float minDistance = 1000.0;
+ colorIndex = 3.0;
+
+ for (int i = 0; i < BLOB_COUNT; i++) {
+ float headDistance = circleDistance(headCenters[i], headRadii[i], target);
+ float leftFootDistance = circleDistance(leftFootCenters[i], footRadii[i], target);
+ float rightFootDistance = circleDistance(rightFootCenters[i], footRadii[i], target);
+
+ float res = min(
+ smoothMin(headDistance, leftFootDistance),
+ smoothMin(headDistance, rightFootDistance)
+ );
+
+ minDistance = min(minDistance, res);
}
- float circleDistance(vec2 circleCenter, float radius) {
- return distance(position, circleCenter) - radius;
- }
-
- void blobMinDistance(inout float minDistance, inout float color) {
- for (int i = 0; i < BLOB_COUNT; i++) {
- float headDistance = circleDistance(blobs[i].headCenter, blobs[i].headRadius);
- float leftFootDistance = circleDistance(blobs[i].leftFootCenter, blobs[i].footRadius);
- float rightFootDistance = circleDistance(blobs[i].rightFootCenter, blobs[i].footRadius);
-
- float res = min(
- smoothMin(headDistance, leftFootDistance),
- smoothMin(headDistance, rightFootDistance)
- );
-
- minDistance = min(minDistance, res);
- color = mix(1.0, color, step(distanceNdcPixelSize + SURFACE_OFFSET, res));
- }
- }
- `,
+ return minDistance;
+ }
+ `,
distanceFunctionName: 'blobMinDistance',
},
- uniformName: 'blobs',
+ propertyUniformMapping: {
+ footRadius: 'footRadii',
+ headRadius: 'headRadii',
+ rightFootCenter: 'rightFootCenters',
+ leftFootCenter: 'leftFootCenters',
+ headCenter: 'headCenters',
+ },
uniformCountMacroName: 'BLOB_COUNT',
- shaderCombinationSteps: [1],
- empty: new BlobShape(vec2.fromValues(0, 0)),
+ shaderCombinationSteps: [0, 1, 10],
+ empty: new BlobShape(),
};
+ protected head: BoundingCircle;
+ protected leftFoot: BoundingCircle;
+ protected rightFoot: BoundingCircle;
- public readonly boundingCircleRadius = 100;
-
- protected readonly headRadius = 40;
- protected readonly footRadius = 15;
-
- private readonly headOffset = vec2.fromValues(0, -15);
- private readonly leftFootOffset = vec2.fromValues(-12, -60);
- private readonly rightFootOffset = vec2.fromValues(12, -60);
-
- public readonly isInverted = false;
- protected boundingCircle = new CircleShape(vec2.create(), this.boundingCircleRadius);
- protected head = new Circle(vec2.create(), this.headRadius);
- protected leftFoot = new Circle(vec2.create(), this.footRadius);
- protected rightFoot = new Circle(vec2.create(), this.footRadius);
-
- public constructor(center: vec2, public readonly gameObject: GameObject = null) {
+ public constructor() {
super();
- this.position = center;
+
+ const circle = new BoundingCircle(null, vec2.create(), 200);
+ this.setCircles([circle, circle, circle]);
}
- public set position(value: vec2) {
- vec2.copy(this.boundingCircle.center, value);
- vec2.add(this.head.center, value, this.headOffset);
- vec2.add(this.leftFoot.center, value, this.leftFootOffset);
- vec2.add(this.rightFoot.center, value, this.rightFootOffset);
- }
-
- public get center(): vec2 {
- return this.boundingCircle.center;
- }
-
- public get radius(): number {
- return this.boundingCircle.radius;
+ public setCircles([head, leftFoot, rightFoot]: [
+ BoundingCircle,
+ BoundingCircle,
+ BoundingCircle
+ ]) {
+ this.head = head;
+ this.leftFoot = leftFoot;
+ this.rightFoot = rightFoot;
}
public minDistance(target: vec2): number {
- return this.boundingCircle.minDistance(target);
- }
- public normal(from: vec2): vec2 {
- return this.boundingCircle.normal(from);
- }
-
- public get boundingBox(): BoundingBox {
- return this.boundingCircle.boundingBox;
- }
-
- public isInside(target: vec2): boolean {
- return this.minDistance(target) < 0;
- }
-
- public clone(): BlobShape {
- return new BlobShape(this.boundingCircle.center, this.gameObject);
+ return Math.min(
+ this.head.distance(target),
+ this.leftFoot.distance(target),
+ this.rightFoot.distance(target)
+ );
}
protected getObjectToSerialize(transform2d: mat2d, transform1d: number): any {
@@ -120,8 +99,8 @@ export class BlobShape extends Drawable implements IShape {
this.rightFoot.center,
transform2d
),
- headRadius: this.headRadius * transform1d,
- footRadius: this.footRadius * transform1d,
+ headRadius: this.head.radius * transform1d,
+ footRadius: this.leftFoot.radius * transform1d,
};
}
}
diff --git a/frontend/src/scripts/shapes/types/circle-shape.ts b/frontend/src/scripts/shapes/types/circle-shape.ts
index d9fbd34..158b468 100644
--- a/frontend/src/scripts/shapes/types/circle-shape.ts
+++ b/frontend/src/scripts/shapes/types/circle-shape.ts
@@ -1,12 +1,8 @@
import { vec2 } from 'gl-matrix';
import { Circle } from 'sdf-2d';
import { GameObject } from '../../objects/game-object';
-import { BoundingBox } from '../bounding-box';
-import { IShape } from '../i-shape';
-
-export class CircleShape extends Circle implements IShape {
- public readonly isInverted = false;
+export class CircleShape extends Circle {
public constructor(
center = vec2.create(),
radius = 0,
@@ -15,34 +11,6 @@ export class CircleShape extends Circle implements IShape {
super(center, radius);
}
- public distance(target: vec2): number {
- return vec2.distance(this.center, target) - this.radius;
- }
-
- public normal(from: vec2): vec2 {
- const diff = vec2.subtract(vec2.create(), from, this.center);
- return vec2.normalize(diff, diff);
- }
-
- public get boundingBox(): BoundingBox {
- return new BoundingBox(
- this,
- this.center.x - this.radius,
- this.center.x + this.radius,
- this.center.y - this.radius,
- this.center.y + this.radius
- );
- }
-
- public isInside(target: vec2): boolean {
- return this.distance(target) < 0;
- }
-
- public areIntersecting(other: CircleShape): boolean {
- const distance = vec2.distance(this.center, other.center);
- return distance < this.radius + other.radius;
- }
-
public clone(): CircleShape {
return new CircleShape(vec2.clone(this.center), this.radius, this.gameObject);
}
diff --git a/frontend/src/scripts/shapes/types/tunnel-shape.ts b/frontend/src/scripts/shapes/types/tunnel-shape.ts
index df46f08..8e3b977 100644
--- a/frontend/src/scripts/shapes/types/tunnel-shape.ts
+++ b/frontend/src/scripts/shapes/types/tunnel-shape.ts
@@ -1,81 +1,17 @@
import { vec2 } from 'gl-matrix';
import { InvertedTunnel } from 'sdf-2d';
-import { clamp01 } from '../../helper/clamp';
-import { rotate90Deg } from '../../helper/rotate-90-deg';
-import { GameObject } from '../../objects/game-object';
-import { BoundingBox } from '../bounding-box';
-import { IShape } from '../i-shape';
-export class TunnelShape extends InvertedTunnel implements IShape {
- constructor(
- readonly from: vec2,
- readonly to: vec2,
- readonly fromRadius: number,
- readonly toRadius: number,
- public readonly gameObject: GameObject = null
- ) {
+export class TunnelShape extends InvertedTunnel {
+ constructor(from: vec2, to: vec2, fromRadius: number, toRadius: number) {
super(from, to, fromRadius, toRadius);
}
- public get boundingBox(): BoundingBox {
- const xMin = Math.min(this.from.x - this.fromRadius, this.to.x - this.toRadius);
- const yMin = Math.min(this.from.y - this.fromRadius, this.to.y - this.toRadius);
- const xMax = Math.max(this.from.x + this.fromRadius, this.to.x + this.toRadius);
- const yMax = Math.max(this.from.y + this.fromRadius, this.to.y + this.toRadius);
-
- return new BoundingBox(this, xMin, xMax, yMin, yMax);
- }
-
- public normal(target: vec2): vec2 {
- const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
-
- const toFromDelta = vec2.subtract(vec2.create(), this.to, this.from);
-
- const h = clamp01(
- vec2.dot(targetFromDelta, toFromDelta) / vec2.dot(toFromDelta, toFromDelta)
- );
-
- let diff = vec2.create();
-
- if (h == 1) {
- vec2.subtract(diff, target, this.to);
- } else if (h == 0) {
- vec2.subtract(diff, target, this.from);
- } else {
- const side = Math.sign(
- toFromDelta.x * targetFromDelta.y - toFromDelta.y * targetFromDelta.x
- );
-
- const normal = rotate90Deg(toFromDelta);
- vec2.normalize(normal, normal);
-
- const translatedFrom = vec2.add(
- vec2.create(),
- this.from,
- vec2.scale(vec2.create(), normal, side * this.fromRadius)
- );
-
- const translatedTo = vec2.add(
- vec2.create(),
- this.to,
- vec2.scale(vec2.create(), normal, side * this.toRadius)
- );
-
- diff = rotate90Deg(vec2.subtract(vec2.create(), translatedTo, translatedFrom));
-
- vec2.scale(diff, diff, side);
- }
-
- return vec2.normalize(diff, diff);
- }
-
public clone(): TunnelShape {
return new TunnelShape(
vec2.clone(this.from),
vec2.clone(this.to),
this.fromRadius,
- this.toRadius,
- this.gameObject
+ this.toRadius
);
}
}