From 6a02ea105a4e15e9f36a5b2d7d0a26169164fb1d Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 4 Nov 2018 22:02:19 +0100 Subject: Fix rounding issues in collision handling --- src/controller/gamecontext.ts | 49 +++++++++++++++++++++++++++---------------- src/math/line.ts | 45 ++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/controller/gamecontext.ts b/src/controller/gamecontext.ts index cda47a5..295872d 100644 --- a/src/controller/gamecontext.ts +++ b/src/controller/gamecontext.ts @@ -6,7 +6,7 @@ import { loadMap } from '../view/map'; import { Renderer } from '../view/renderer/renderer'; import { SpriteView } from '../view/sprite'; -import { LineSegment } from '../math/line'; +import { LineSegment, Movement } from '../math/line'; import { getJSON } from '../util'; import { vec2 } from 'gl-matrix'; @@ -40,10 +40,25 @@ export class GameContext { private readonly entityMovement: vec2 = vec2.create(); private readonly walls: LineSegment[] = [ - new LineSegment(vec2.fromValues(1, 1), vec2.fromValues(1, 0), 10), - new LineSegment(vec2.fromValues(11, 1), vec2.fromValues(0, 1), 10), - new LineSegment(vec2.fromValues(11, 11), vec2.fromValues(-1, 0), 10), - new LineSegment(vec2.fromValues(1, 11), vec2.fromValues(0, -1), 10), + LineSegment.fromPoints(vec2.fromValues(1, 1), vec2.fromValues(11, 1)), + + LineSegment.fromPoints(vec2.fromValues(11, 1), vec2.fromValues(11, 5)), + LineSegment.fromPoints(vec2.fromValues(11, 5), vec2.fromValues(12, 5)), + LineSegment.fromPoints(vec2.fromValues(12, 5), vec2.fromValues(12, 7)), + LineSegment.fromPoints(vec2.fromValues(12, 7), vec2.fromValues(11, 7)), + LineSegment.fromPoints(vec2.fromValues(11, 7), vec2.fromValues(11, 11)), + + LineSegment.fromPoints(vec2.fromValues(11, 11), vec2.fromValues(7, 11)), + LineSegment.fromPoints(vec2.fromValues(7, 11), vec2.fromValues(7, 12)), + LineSegment.fromPoints(vec2.fromValues(7, 12), vec2.fromValues(5, 12)), + LineSegment.fromPoints(vec2.fromValues(5, 12), vec2.fromValues(5, 11)), + LineSegment.fromPoints(vec2.fromValues(5, 11), vec2.fromValues(1, 11)), + + LineSegment.fromPoints(vec2.fromValues(1, 11), vec2.fromValues(1, 7)), + LineSegment.fromPoints(vec2.fromValues(1, 7), vec2.fromValues(0, 7)), + LineSegment.fromPoints(vec2.fromValues(0, 7), vec2.fromValues(0, 5)), + LineSegment.fromPoints(vec2.fromValues(0, 5), vec2.fromValues(1, 5)), + LineSegment.fromPoints(vec2.fromValues(1, 5), vec2.fromValues(1, 1)), ]; private constructor( @@ -70,34 +85,32 @@ export class GameContext { } private updateStep(): void { - let move = new LineSegment(this.entityPos, this.entityMovement, this.speed); - const p = vec2.create(); + const dest = vec2.scaleAndAdd(vec2.create(), this.entityPos, this.entityMovement, this.speed); + const dest2 = vec2.create(); let rescan = true; while (rescan) { rescan = false; - for (const w of this.walls) { - if (!w.collidesMove(move)) - continue; + if (vec2.equals(dest, this.entityPos)) + return; - move.getP2(p); - w.projectPoint(p, p); + const move = new Movement(this.entityPos, dest); - const move2 = LineSegment.fromPoints(this.entityPos, p); - if (move2.l === 0) - return; + for (const w of this.walls) { + if (!w.collidesMove(dest2, move)) + continue; - if (!move.equals(move2)) { - move = move2; + if (!vec2.exactEquals(dest, dest2)) { + vec2.copy(dest, dest2); rescan = true; break; } } } - move.getP2(this.entityPos); + vec2.copy(this.entityPos, dest); } private update(time: number): void { diff --git a/src/math/line.ts b/src/math/line.ts index 98e126b..902c9b6 100644 --- a/src/math/line.ts +++ b/src/math/line.ts @@ -37,9 +37,23 @@ export class Line { const d2 = d / crossz(this.v, l2.v); return vec2.scaleAndAdd(out, l2.p, l2.v, d2); } +} + +export class Movement { + public readonly v: vec2; - public equals(l2: Line): boolean { - return vec2.equals(this.p, l2.p) && vec2.equals(this.v, l2.v); + constructor( + public readonly p1: vec2, + public readonly p2: vec2, + ) { + this.v = vec2.sub(vec2.create(), p2, p1); + } + + public intersectLine(out: vec2, l: Line): vec2 { + const vp = vec2.sub(vec2.create(), l.p, this.p1); + const d = crossz(vp, this.v); + const d2 = d / crossz(this.v, l.v); + return vec2.scaleAndAdd(out, l.p, l.v, d2); } } @@ -69,30 +83,19 @@ export class LineSegment extends Line { return (d >= 0 && d <= this.l); } - public collidesLine(l2: Line): boolean { - const x = this.intersectLine(vec2.create(), l2); - return this.collidesPoint(x); - } - - public collidesLineSegment(l2: LineSegment): boolean { - const x = this.intersectLine(vec2.create(), l2); - if (!this.collidesPoint(x)) - return false; - if (!l2.collidesPoint(x)) + public collidesMove(out: vec2, move: Movement): boolean { + if (this.distancePoint(move.p1) < 0) return false; - return true; - } + if (this.distancePoint(move.p2) >= 0) + return false; - public collidesMove(move: LineSegment): boolean { - if (!this.collidesLineSegment(move)) + const x = move.intersectLine(vec2.create(), this); + if (!this.collidesPoint(x)) return false; - const p2 = move.getP2(vec2.create()); - return this.distancePoint(p2) < 0; - } + this.projectPoint(out, move.p2); - public equals(l2: LineSegment): boolean { - return super.equals(l2) && this.l === l2.l; + return true; } } -- cgit v1.2.3