UNPKG

@rpgjs/physic

Version:

A deterministic 2D top-down physics library for RPG, sandbox and MMO games

120 lines (119 loc) 4.62 kB
import { Vector2 } from "./index2.js"; import { AABB } from "./index4.js"; import { AABBCollider } from "./index12.js"; import { CircleCollider } from "./index11.js"; import { PolygonCollider } from "./index19.js"; import { createCollider } from "./index18.js"; function sweepEntities(a, b, delta) { const ca = createCollider(a); const cb = createCollider(b); if (!ca || !cb) return null; return sweepColliders(ca, cb, delta); } function sweepColliders(a, b, delta) { if (a instanceof CircleCollider && b instanceof CircleCollider) { return sweepCircleCircle(a, b, delta); } if (a instanceof CircleCollider && b instanceof AABBCollider) { return sweepCircleAABB(a, b, delta); } if (a instanceof AABBCollider && b instanceof CircleCollider) { const res = sweepCircleAABB(b, a, delta.mul(-1)); if (!res) return null; return { time: res.time, normal: res.normal.mul(-1), point: res.point }; } if (a instanceof AABBCollider && b instanceof AABBCollider) { return sweepAABBAABB(a, b, delta); } if (a instanceof PolygonCollider || b instanceof PolygonCollider) { const aa = a.getBounds(); const bb = b.getBounds(); const ca = new AABBCollider(a.getEntity()); const cb = new AABBCollider(b.getEntity()); ca.getBounds = () => aa; cb.getBounds = () => bb; return sweepAABBAABB(ca, cb, delta); } return null; } function sweepCircleCircle(a, b, delta) { const p0 = a.getCenter(); const c = b.getCenter(); const r = a.getRadius() + b.getRadius(); const m = p0.sub(c); const d = delta; const A = d.dot(d); const B = 2 * m.dot(d); const C = m.dot(m) - r * r; const disc = B * B - 4 * A * C; if (disc < 0 || A === 0) return null; const t = (-B - Math.sqrt(disc)) / (2 * A); if (t < 0 || t > 1) return null; const hitPoint = p0.add(d.mul(t)); const normal = hitPoint.sub(c).normalize(); return { time: t, normal, point: hitPoint.sub(normal.mul(a.getRadius())) }; } function sweepCircleAABB(circle, box, delta) { const r = circle.getRadius(); const b = box.getBounds(); const expanded = new AABB(b.minX - r, b.minY - r, b.maxX + r, b.maxY + r); const p0 = circle.getCenter(); const dir = delta; const maxDist = 1; const origin = new Vector2(0, 0); const bb = new AABB(expanded.minX - p0.x, expanded.minY - p0.y, expanded.maxX - p0.x, expanded.maxY - p0.y); const res = rayVsAABB(origin, dir, bb, maxDist); if (!res) return null; const t = res.t; const hitPos = p0.add(dir.mul(t)); const normal = res.normal; return { time: t, normal, point: hitPos.sub(normal.mul(r)) }; } function sweepAABBAABB(a, b, delta) { const A = a.getBounds(); const B = b.getBounds(); const invDx = 1 / (delta.x === 0 ? 1e-9 : delta.x); const invDy = 1 / (delta.y === 0 ? 1e-9 : delta.y); const xEntry = (delta.x > 0 ? B.minX - A.maxX : B.maxX - A.minX) * invDx; const xExit = (delta.x > 0 ? B.maxX - A.minX : B.minX - A.maxX) * invDx; const yEntry = (delta.y > 0 ? B.minY - A.maxY : B.maxY - A.minY) * invDy; const yExit = (delta.y > 0 ? B.maxY - A.minY : B.minY - A.maxY) * invDy; const tEntry = Math.max(Math.min(xEntry, xExit), Math.min(yEntry, yExit)); const tExit = Math.min(Math.max(xEntry, xExit), Math.max(yEntry, yExit)); if (tEntry > tExit || tEntry < 0 || tEntry > 1) return null; let normal; if (Math.min(xEntry, xExit) > Math.min(yEntry, yExit)) { normal = new Vector2(delta.x > 0 ? -1 : 1, 0); } else { normal = new Vector2(0, delta.y > 0 ? -1 : 1); } const hitPoint = new Vector2( delta.x !== 0 ? delta.x > 0 ? A.maxX : A.minX : (A.minX + A.maxX) * 0.5, delta.y !== 0 ? delta.y > 0 ? A.maxY : A.minY : (A.minY + A.maxY) * 0.5 ); return { time: tEntry, normal, point: hitPoint }; } function rayVsAABB(origin, dir, b, maxT) { const invDx = 1 / (dir.x === 0 ? 1e-9 : dir.x); const invDy = 1 / (dir.y === 0 ? 1e-9 : dir.y); let tmin = 0; let tmax = maxT; const tx1 = (b.minX - origin.x) * invDx; const tx2 = (b.maxX - origin.x) * invDx; const ty1 = (b.minY - origin.y) * invDy; const ty2 = (b.maxY - origin.y) * invDy; const tminX = Math.min(tx1, tx2); const tmaxX = Math.max(tx1, tx2); const tminY = Math.min(ty1, ty2); const tmaxY = Math.max(ty1, ty2); tmin = Math.max(tmin, Math.max(tminX, tminY)); tmax = Math.min(tmax, Math.min(tmaxX, tmaxY)); if (tmax < tmin || tmin < 0 || tmin > maxT) return null; const normal = tmin === tminX ? new Vector2(dir.x > 0 ? -1 : 1, 0) : new Vector2(0, dir.y > 0 ? -1 : 1); return { t: tmin, normal }; } export { sweepColliders, sweepEntities }; //# sourceMappingURL=index22.js.map