Fix collision detection
This commit is contained in:
parent
006ab3c4e6
commit
5b4e67cbf0
9 changed files with 169 additions and 49 deletions
|
|
@ -22,11 +22,11 @@ export const settings = {
|
|||
tileMultiplier: 5,
|
||||
shaderMacros: {
|
||||
distanceScale: 64,
|
||||
distanceOffset: 0.15,
|
||||
distanceOffset: 0.0,
|
||||
edgeSmoothing: 10,
|
||||
},
|
||||
shaderCombinations: {
|
||||
lineSteps: [0, 1, 2, 3, 4, 8, 16, 32],
|
||||
lineSteps: [0, 1, 2, 3, 4, 8, 16, 128],
|
||||
circleLightSteps: [0, 1, 2, 3],
|
||||
pointLightSteps: [0, 1, 2, 3],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -58,7 +58,10 @@ void main() {
|
|||
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
||||
float lightCenterDistance = distance(circleLights[i].center, worldCoordinates);
|
||||
|
||||
|
||||
/*if (lightCenterDistance < circleLights[i].radius) {
|
||||
fragmentColor = vec4(1.0, 1.0, 0.0, 1.0);
|
||||
return;
|
||||
}*/
|
||||
vec3 lightColorAtPosition = circleLights[i].value / pow(
|
||||
lightCenterDistance / LIGHT_DROP + 1.0, 2.0
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import { createCharacter } from './objects/world/create-character';
|
|||
import { createDungeon } from './objects/world/create-dungeon';
|
||||
import { RenderCommand } from './drawing/commands/render';
|
||||
import { Physics } from './physics/physics';
|
||||
import { MoveToCommand } from './physics/commands/move-to';
|
||||
import { TeleportToCommand } from './physics/commands/teleport-to';
|
||||
import { IRenderer } from './drawing/i-renderer';
|
||||
import { Random } from './helper/random';
|
||||
|
||||
export class Game {
|
||||
private previousTime?: DOMHighResTimeStamp = null;
|
||||
|
|
@ -28,6 +28,8 @@ export class Game {
|
|||
const canvas: HTMLCanvasElement = document.querySelector('canvas#main');
|
||||
const overlay: HTMLElement = document.querySelector('#overlay');
|
||||
|
||||
Random.seed = 42;
|
||||
|
||||
document.addEventListener(
|
||||
'visibilitychange',
|
||||
this.handleVisibilityChange.bind(this)
|
||||
|
|
|
|||
18
frontend/src/scripts/helper/random.ts
Normal file
18
frontend/src/scripts/helper/random.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// src
|
||||
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript
|
||||
// Mulberry32
|
||||
|
||||
export abstract class Random {
|
||||
private static _seed = Math.random();
|
||||
|
||||
public static set seed(value: number) {
|
||||
Random._seed = value;
|
||||
}
|
||||
|
||||
public static getRandom(): number {
|
||||
let t = (Random._seed += 0x6d2b79f5);
|
||||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
||||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
}
|
||||
}
|
||||
3
frontend/src/scripts/helper/rotate-90-deg.ts
Normal file
3
frontend/src/scripts/helper/rotate-90-deg.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export const rotate90Deg = (vec: vec2): vec2 => vec2.fromValues(-vec.y, vec.x);
|
||||
|
|
@ -9,18 +9,21 @@ import { TeleportToCommand } from '../../physics/commands/teleport-to';
|
|||
import { Physics } from '../../physics/physics';
|
||||
import { GameObject } from '../game-object';
|
||||
import { Camera } from './camera';
|
||||
import { IShape } from '../../shapes/i-shape';
|
||||
import { rotate90Deg } from '../../helper/rotate-90-deg';
|
||||
import { max } from 'gl-matrix/src/gl-matrix/vec2';
|
||||
|
||||
export class Character extends GameObject {
|
||||
private keysDown: Set<string> = new Set();
|
||||
|
||||
private primitive: Circle;
|
||||
private shape: Circle;
|
||||
private static speed = 1.5;
|
||||
|
||||
constructor(private physics: Physics, private camera: Camera) {
|
||||
super();
|
||||
|
||||
this.primitive = new Circle();
|
||||
this.primitive.radius = 40;
|
||||
this.shape = new Circle();
|
||||
this.shape.radius = 80;
|
||||
|
||||
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
|
||||
this.addCommandExecutor(TeleportToCommand, (c) =>
|
||||
|
|
@ -28,57 +31,100 @@ export class Character extends GameObject {
|
|||
);
|
||||
this.addCommandExecutor(KeyDownCommand, (c) => this.keysDown.add(c.key));
|
||||
this.addCommandExecutor(KeyUpCommand, (c) => this.keysDown.delete(c.key));
|
||||
this.addCommandExecutor(SwipeCommand, (c) =>
|
||||
this.checkAndSetPosition(
|
||||
vec2.add(
|
||||
vec2.create(),
|
||||
this.primitive.center,
|
||||
this.addCommandExecutor(SwipeCommand, (c) => {
|
||||
this.tryMoving(
|
||||
vec2.multiply(vec2.create(), c.delta, this.camera.viewAreaSize)
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private checkAndSetPosition(value: vec2) {
|
||||
const nextPrimitive = this.primitive.clone();
|
||||
nextPrimitive.center = value;
|
||||
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);
|
||||
|
||||
const intersects = this.physics
|
||||
.findIntersecting(nextPrimitive.boundingBox)
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private getNearShapesTo(
|
||||
shape: Circle
|
||||
): Array<{ shape: IShape; distance: number }> {
|
||||
return this.physics
|
||||
.findIntersecting(shape.boundingBox)
|
||||
.filter((b) => b.shape)
|
||||
.map(
|
||||
(b) => b.shape.distance(nextPrimitive.center) + nextPrimitive.radius
|
||||
);
|
||||
|
||||
console.log(intersects);
|
||||
if (intersects.find((d) => d < 0) !== undefined) {
|
||||
this.setPosition(value);
|
||||
}
|
||||
.map((b) => ({
|
||||
shape: b.shape,
|
||||
distance: b.shape.distance(shape.center) + shape.radius,
|
||||
}))
|
||||
.sort((e) => e.distance);
|
||||
}
|
||||
|
||||
private setPosition(value: vec2) {
|
||||
// console.log('character', value);
|
||||
|
||||
this.primitive.center = value;
|
||||
this.camera.sendCommand(new MoveToCommand(this.primitive.center));
|
||||
this.shape.center = value;
|
||||
this.camera.sendCommand(new MoveToCommand(this.shape.center));
|
||||
}
|
||||
|
||||
public stepHandler(c: StepCommand) {
|
||||
const deltaTime = c.deltaTimeInMiliseconds;
|
||||
|
||||
const up = ~~this.keysDown.has('w');
|
||||
const down = ~~this.keysDown.has('s');
|
||||
const left = ~~this.keysDown.has('a');
|
||||
const right = ~~this.keysDown.has('d');
|
||||
const up = ~~(this.keysDown.has('w') || this.keysDown.has('arrowup'));
|
||||
const down = ~~(this.keysDown.has('s') || this.keysDown.has('arrowdown'));
|
||||
const left = ~~(this.keysDown.has('a') || this.keysDown.has('arrowleft'));
|
||||
const right = ~~(this.keysDown.has('d') || this.keysDown.has('arrowright'));
|
||||
|
||||
const movementVector = vec2.fromValues(right - left, up - down);
|
||||
if (movementVector.length > 0) {
|
||||
if (vec2.length(movementVector) > 0) {
|
||||
vec2.normalize(movementVector, movementVector);
|
||||
vec2.scale(movementVector, movementVector, Character.speed * deltaTime);
|
||||
|
||||
this.checkAndSetPosition(
|
||||
vec2.add(vec2.create(), this.primitive.center, movementVector)
|
||||
);
|
||||
this.tryMoving(movementVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { vec2 } from 'gl-matrix';
|
|||
import { Physics } from '../../physics/physics';
|
||||
import { Objects } from '../objects';
|
||||
import { Tunnel } from '../types/tunnel';
|
||||
import { Random } from '../../helper/random';
|
||||
|
||||
export const createDungeon = (objects: Objects, physics: Physics): Tunnel => {
|
||||
let previousRadius = 350;
|
||||
|
|
@ -10,10 +11,10 @@ export const createDungeon = (objects: Objects, physics: Physics): Tunnel => {
|
|||
let first: Tunnel;
|
||||
|
||||
for (let i = 0; i < 50000; i += 500) {
|
||||
const deltaHeight = (Math.random() - 0.5) * 2000;
|
||||
const deltaHeight = (Random.getRandom() - 0.5) * 2000;
|
||||
const height = previousEnd.y + deltaHeight;
|
||||
const currentEnd = vec2.fromValues(i, height);
|
||||
const currentToRadius = Math.random() * 300 + 150;
|
||||
const currentToRadius = Random.getRandom() * 300 + 150;
|
||||
|
||||
const tunnel = new Tunnel(
|
||||
physics,
|
||||
|
|
@ -29,15 +30,15 @@ export const createDungeon = (objects: Objects, physics: Physics): Tunnel => {
|
|||
|
||||
objects.addObject(tunnel);
|
||||
|
||||
/*if (deltaHeight > 0 && Math.random() > 0.8) {
|
||||
/*if (deltaHeight > 0 && Random.getRandom() > 0.8) {
|
||||
objects.addObject(
|
||||
new Lamp(
|
||||
currentEnd,
|
||||
Math.random() * 20 + 30,
|
||||
Random.getRandom() * 20 + 30,
|
||||
vec3.scale(
|
||||
vec3.create(),
|
||||
vec3.normalize(vec3.create(), vec3.fromValues(0.5, 0.1, 0.8)),
|
||||
Math.random() * 0.5 + 0.5
|
||||
Random.getRandom() * 0.5 + 0.5
|
||||
),
|
||||
1
|
||||
)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,6 @@ export class Circle implements IShape {
|
|||
}
|
||||
|
||||
public clone(): Circle {
|
||||
return new Circle(this.center, this.radius);
|
||||
return new Circle(vec2.clone(this.center), this.radius);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { BoundingBox } from '../bounding-box';
|
|||
import { clamp01 } from '../../helper/clamp';
|
||||
import { mix } from '../../helper/mix';
|
||||
import { IShape } from '../i-shape';
|
||||
import { rotate90Deg } from '../../helper/rotate-90-deg';
|
||||
|
||||
export class TunnelShape implements IShape {
|
||||
public readonly isInverted = true;
|
||||
|
|
@ -39,8 +40,49 @@ export class TunnelShape implements IShape {
|
|||
return new BoundingBox(this, xMin, xMax, yMin, yMax);
|
||||
}
|
||||
|
||||
public normal(from: vec2): vec2 {
|
||||
throw new Error('Unimplemented');
|
||||
public normal(target: vec2): vec2 {
|
||||
const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
|
||||
|
||||
const h = clamp01(
|
||||
vec2.dot(targetFromDelta, this.toFromDelta) /
|
||||
vec2.dot(this.toFromDelta, this.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(
|
||||
this.toFromDelta.x * targetFromDelta.y -
|
||||
this.toFromDelta.y * targetFromDelta.x
|
||||
);
|
||||
|
||||
const normal = rotate90Deg(this.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 distance(target: vec2): number {
|
||||
|
|
@ -60,6 +102,11 @@ export class TunnelShape implements IShape {
|
|||
}
|
||||
|
||||
public clone(): TunnelShape {
|
||||
return new TunnelShape(this.from, this.to, this.fromRadius, this.toRadius);
|
||||
return new TunnelShape(
|
||||
vec2.clone(this.from),
|
||||
vec2.clone(this.to),
|
||||
this.fromRadius,
|
||||
this.toRadius
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue