Finish circle x rect detection

This commit is contained in:
Schluffe
2026-04-12 17:12:18 +02:00
parent 20a1de7647
commit 452d788090
4 changed files with 83 additions and 98 deletions
+45 -60
View File
@@ -111,13 +111,13 @@ function castOnCircle(
const b = 2 * (dx * vx + dy * vy);
const c = dx * dx + dy * dy - radius * radius;
let dis = b * b - 4 * a * c;
if (dis < 0) {
let disc = b * b - 4 * a * c;
if (disc < 0) {
return null;
}
dis = Math.sqrt(dis);
const time = (-b - dis) / (2 * a);
disc = Math.sqrt(disc);
const time = (-b - disc) / (2 * a);
if (time >= 0 && time <= 1) {
return time;
}
@@ -150,14 +150,14 @@ function sweptAABB(
inflAABB.width = colliderB.width + colliderA.width;
inflAABB.height = colliderB.height + colliderA.height;
const disX = velocityA.x * delta;
const disY = velocityA.y * delta;
const vx = velocityA.x * delta;
const vy = velocityA.y * delta;
let txNear = (inflAABB.x - ax) / disX;
let txFar = (inflAABB.x + inflAABB.width - ax) / disX;
let txNear = (inflAABB.x - ax) / vx;
let txFar = (inflAABB.x + inflAABB.width - ax) / vx;
let tyNear = (inflAABB.y - ay) / disY;
let tyFar = (inflAABB.y + inflAABB.height - ay) / disY;
let tyNear = (inflAABB.y - ay) / vy;
let tyFar = (inflAABB.y + inflAABB.height - ay) / vy;
if (txNear > txFar) {
[txNear, txFar] = [txFar, txNear];
@@ -183,9 +183,9 @@ function sweptAABB(
collision.normal.x = 0;
collision.normal.y = 0;
if (txNear > tyNear) {
collision.normal.x = disX > 0 ? -1 : 1;
collision.normal.x = vx > 0 ? -1 : 1;
} else {
collision.normal.y = disY > 0 ? -1 : 1;
collision.normal.y = vy > 0 ? -1 : 1;
}
// thNear == -Infinity if velocity is 0
@@ -219,10 +219,6 @@ function circleOnCircle(
const time = castOnCircle(dx, dy, vx, vy, radius);
if (time === null) {
collision.cid = "";
collision.time = 0;
collision.normal.x = 0;
collision.normal.y = 0;
return;
}
@@ -230,62 +226,51 @@ function circleOnCircle(
invScale(collision.normal, dx + vx * time, dy + vy * time);
}
const inflCircleAABB = {
x: 0,
y: 0,
width: 0,
height: 0,
};
function circleOnRect(
collision: Collision,
circleColl: Collider,
circlePos: Vector,
circleVel: Vector,
rectColl: Collider,
rectPos: Vector,
colliderA: Collider,
positionA: Vector,
velocityA: Vector,
colliderB: Collider,
positionB: Vector,
delta: number,
) {
const cx = circlePos.x + circleColl.offset.x;
const cy = circlePos.y + circleColl.offset.y;
const rx = rectPos.x + rectColl.offset.x;
const ry = rectPos.y + rectColl.offset.y;
const radius = (circleColl.body as Circle).radius;
const radius = (colliderA.body as Circle).radius;
const cWidth = (colliderB.body as Rect).width / 2;
const cHeight = (colliderB.body as Rect).height / 2;
inflCircleAABB.x = rx - radius;
inflCircleAABB.y = ry - radius;
inflCircleAABB.width = rectColl.width + radius;
inflCircleAABB.height = rectColl.height + radius;
const ax = positionA.x + colliderA.offset.x + radius;
const ay = positionA.y + colliderA.offset.y + radius;
const bx = positionB.x + colliderB.offset.x + cWidth;
const by = positionB.y + colliderB.offset.y + cHeight;
const disX = circleVel.x * delta;
const disY = circleVel.y * delta;
const vx = velocityA.x * delta;
const vy = velocityA.y * delta;
let txNear = (inflCircleAABB.x - cx) / disX;
let txFar = (inflCircleAABB.x + inflCircleAABB.width - cx) / disX;
let tyNear = (inflCircleAABB.y - cy) / disY;
let tyFar = (inflCircleAABB.y + inflCircleAABB.width - cy) / disY;
if (txNear > txFar) {
[txNear, txFar] = [txFar, txNear];
}
if (tyNear > tyFar) {
[tyNear, tyFar] = [tyFar, tyNear];
}
// no collision at all
if (txNear > tyFar || tyNear > txFar) {
const left = bx - cWidth;
const right = bx + cWidth;
const hitX = ax + vx * collision.time;
if (hitX >= left && hitX <= right) {
return;
}
const thNear = Math.max(txNear, tyNear);
const thFar = Math.min(txFar, tyFar);
// thFar < 0, collision is behind a frame
// thNear > 1, collision is ahead a frame
if (thFar < 0 || thNear > 1) {
const top = by - cHeight;
const bottom = by + cHeight;
const hitY = ay + vy * collision.time;
if (hitY >= top && hitY <= bottom) {
return;
}
// WIP
const dx = ax - (hitX < left ? left : right);
const dy = ay - (hitY < top ? top : bottom);
const time = castOnCircle(dx, dy, vx, vy, radius);
if (time === null) {
collision.cid = "";
return;
}
collision.time = time;
invScale(collision.normal, dx + vx * time, dy + vy * time);
}
function circleOnPolygon(