Use Bhijn's algorithm for corner assist

This commit is contained in:
Pyrex 2025-02-22 22:45:01 -08:00
parent bccd7661b8
commit 9d4a9bc0b1
5 changed files with 91 additions and 58 deletions

View File

@ -144,6 +144,57 @@ export class Size {
}
}
export class Circle {
readonly center: Point;
readonly radius: number;
constructor(center: Point, radius: number) {
this.center = center;
this.radius = radius;
}
getContactWithRect(rect: Rect): Point | null {
// port: https://www.jeffreythompson.org/collision-detection/circle-rect.php
let cx = this.center.x;
let cy = this.center.y;
let testX = this.center.x;
let testY = this.center.y;
let rx = rect.top.x;
let ry = rect.top.y;
let rw = rect.size.w;
let rh = rect.size.h;
if (cx < rx) { testX = rx; }
else if (cx > rx + rw) { testX = rx + rw; }
if (cy < ry) { testY = ry; }
else if (cy > ry + rh) { testY = ry + rh; }
let distX = cx - testX;
let distY = cy - testY;
let sqDistance = (distX * distX) + (distY * distY);
if (sqDistance <= this.radius * this.radius) {
return new Point(testX, testY);
}
return null;
}
overlappedCells(size: Size): Rect[] {
let meAsRect = new Rect(
this.center.offset(new Point(-this.radius, -this.radius)),
new Size(this.radius * 2, this.radius * 2)
);
let all: Rect[] = [];
for (let cell of meAsRect.overlappedCells(size).values()) {
if (this.getContactWithRect(cell) != null) {
all.push(cell);
}
}
return all;
}
}
export class Rect {
readonly top: Point;
readonly size: Size;

View File

@ -1,5 +1,5 @@
import { BreakableBlockPickupCallbacks } from "./pickups.ts";
import { Point, Rect, Size } from "./engine/datatypes.ts";
import {Circle, Point} from "./engine/datatypes.ts";
import { displace } from "./physics.ts";
import { getHuntMode } from "./huntmode.ts";
import { DrawPile } from "./drawpile.ts";
@ -36,7 +36,7 @@ export class Floater {
let { displacement, dxy } = displace(
bbox,
this.velocity,
(r) => getHuntMode().isBlocked(r),
(r) => getHuntMode().getContact(r),
{ bounce: 0.6 },
);
@ -119,10 +119,10 @@ export class Floater {
return !this.collected && this.frame < 1440;
}
get bbox(): Rect {
get bbox(): Circle {
let w = 0.25;
let h = 0.25;
return new Rect(this.xy.offset(new Point(-w / 2, -h / 2)), new Size(w, h));
return new Circle(this.xy, w / 2);
}
drawParticle(projected: Point, isShadow: boolean): any {
this.#callbacks.drawParticle(

View File

@ -1,4 +1,4 @@
import { Point, Rect, Size } from "./engine/datatypes.ts";
import {Circle, Point, Rect, Size} from "./engine/datatypes.ts";
import { DrawPile } from "./drawpile.ts";
import { D, I } from "./engine/public.ts";
import { sprThrallLore } from "./sprites.ts";
@ -201,39 +201,12 @@ export class HuntMode {
this.velocity = new Point(dx, dy);
// try to push us away from walls if we're close
for (let offset of CARDINAL_DIRECTIONS.values()) {
let bigBbox = new Rect(
this.floatingPlayer
.offset(offset.scale(new Size(0.06, 0.06)))
.offset(new Point(-szX / 2, -szY / 2)),
new Size(szX, szY),
);
let hitsWall = false;
for (let cell of bigBbox.overlappedCells(new Size(1, 1)).values()) {
if (this.#blocksMovement(cell.top)) {
hitsWall = true;
break;
}
}
if (hitsWall) {
this.velocity = this.velocity.offset(
offset.scale(new Point(0.005, 0.005)).negate(),
);
}
}
let origin = new Point(szX / 2, szY / 2);
let bbox = new Rect(
this.floatingPlayer.offset(origin.negate()),
new Size(szX, szY),
);
let { displacement, dxy } = displace(bbox, this.velocity, (b: Rect) =>
this.isBlocked(b),
let bbox = new Circle( this.floatingPlayer, szX / 2);
let { displacement, dxy } = displace(bbox, this.velocity, (b: Circle) =>
this.getContact(b)
);
this.floatingPlayer = this.floatingPlayer.offset(displacement);
// this.velocity = dxy; // don't let the wall break our speed, this sucks on corners
this.velocity = dxy;
// let friction do it
getPlayerProgress().spendBlood(displacement.distance(new Point(0, 0)) * 10);
}
@ -455,13 +428,13 @@ export class HuntMode {
});
}
isBlocked(bbox: Rect): boolean {
getContact(bbox: Circle): Point | null {
for (let cell of bbox.overlappedCells(new Size(1, 1)).values()) {
if (this.#blocksMovement(cell.top)) {
return true;
return bbox.getContactWithRect(cell)!;
}
}
return false;
return null;
}
#blocksMovement(xy: Point) {

View File

@ -1,36 +1,45 @@
import { Point, Rect } from "./engine/datatypes.ts";
import {Circle, lerp, Point, Rect} from "./engine/datatypes.ts";
export function displace(
bbox: Rect,
bbox: Circle,
dxy: Point,
blocked: (where: Rect) => boolean,
getContact: (where: Circle) => Point | null,
options?: { bounce?: number },
): { bbox: Rect; displacement: Point; dxy: Point } {
): { bbox: Circle; displacement: Point; dxy: Point } {
let nSteps = 40;
let nRedirections = 40;
let bounce = options?.bounce ?? 0;
let xy = bbox.top;
let xy = bbox.center;
let redirections = 0;
for (let i = 0; i < nSteps; i++) {
let trialXy = xy.offset(new Point(dxy.x / nSteps, 0));
let trialBbox = new Rect(trialXy, bbox.size);
let trialXy = xy.offset(new Point(dxy.x / nSteps, dxy.y / nSteps));
let trialBbox = new Circle(trialXy, bbox.radius);
if (blocked(trialBbox)) {
dxy = new Point(bounce * -dxy.x, dxy.y);
} else {
xy = trialXy;
}
let contact = getContact(trialBbox);
if (contact) {
let normal = contact.offset(trialXy.negate());
let mag = normal.distance(new Point(0, 0));
let nx = mag == 0 ? 0 : normal.x / mag;
let ny = mag == 0 ? 0 : normal.y / mag;
trialXy = xy.offset(new Point(0, dxy.y / nSteps));
trialBbox = new Rect(trialXy, bbox.size);
if (blocked(trialBbox)) {
dxy = new Point(dxy.x, bounce * -dxy.y);
let dot = dxy.x * nx + dxy.y * ny;
if (redirections < nRedirections) {
dxy = new Point(dxy.x - lerp(bounce, 1, 2) * dot * nx, dxy.y - lerp(bounce, 1, 2) * dot * ny);
i -= 1; // try again with reflection
redirections += 1;
} else {
dxy = new Point(0, 0);
break;
}
} else {
xy = trialXy;
}
}
return {
bbox: new Rect(xy, bbox.size),
displacement: xy.offset(bbox.top.negate()),
bbox: new Circle(xy, bbox.radius),
displacement: xy.offset(bbox.center.negate()),
dxy,
};
}

View File

@ -281,7 +281,7 @@ export class LadderPickup {
}
blocksMovement() {
return true;
return false;
}
obstructsVision() {