fix: 优化跳跃和下落手感
This commit is contained in:
1 parent
ada64fe4be
commit
afe2f64b9b
25 files changed
+1197
-188
No files matched your search
+33
-14
@@ -328,14 +328,14 @@ export class Level {
|
||||
for (let tx = x0; tx <= x1; tx++) {
|
||||
const t = this.tileAt(tx, ty);
|
||||
if (!DEADLY[t]) continue;
|
||||
const r = spikeBox(t, tx, ty);
|
||||
if (x < r.x + r.w && x + w > r.x && y < r.y + r.h && y + h > r.y) return true;
|
||||
const tr = spikeTri(t, tx * TILE, ty * TILE);
|
||||
if (triRectOverlap(tr.ax, tr.ay, tr.bx, tr.by, tr.cx, tr.cy, x, y, w, h)) return true;
|
||||
}
|
||||
}
|
||||
for (const e of this.entities) {
|
||||
if (e.type === 'dropspike' && e.falling) {
|
||||
const r = spikeBox(T.SPIKE_U, e.x / TILE, e.y / TILE);
|
||||
if (x < r.x + r.w && x + w > r.x && y < r.y + r.h && y + h > r.y) return true;
|
||||
const tr = spikeTri(T.SPIKE_L, e.x, e.y); // 与 drawDropSpike 的 dir=2(朝左)一致
|
||||
if (triRectOverlap(tr.ax, tr.ay, tr.bx, tr.by, tr.cx, tr.cy, x, y, w, h)) return true;
|
||||
}
|
||||
}
|
||||
for (const b of this.ebullets) {
|
||||
@@ -646,14 +646,33 @@ export class Level {
|
||||
}
|
||||
}
|
||||
|
||||
// 刺的判定盒:对齐齿尖、收窄到齿根之间(IWBT 三角形判定的矩形近似)。
|
||||
// 配合 11×21 的玩家判定盒,可以像素级贴刺、穿对刺缝。
|
||||
function spikeBox(t, tx, ty) {
|
||||
const x = tx * TILE, y = ty * TILE;
|
||||
if (t === T.SPIKE_U) return { x: x + 6, y: y + 8, w: TILE - 12, h: TILE - 8 }; // 齿尖 y+5,缝高 24
|
||||
if (t === T.SPIKE_D) return { x: x + 6, y: y + 1, w: TILE - 12, h: TILE - 9 }; // 齿尖 y+27
|
||||
if (t === T.SPIKE_L) return { x: x + 5, y: y + 6, w: TILE - 10, h: TILE - 12 }; // 齿尖 x+5,缝宽 22
|
||||
if (t === T.SPIKE_R) return { x: x + 5, y: y + 6, w: TILE - 10, h: TILE - 12 }; // 齿尖 x+27
|
||||
return { x: x + 6, y: y + 6, w: TILE - 12, h: TILE - 10 }; // KILLER(美味水果)
|
||||
// 单三角形刺的顶点(与 sprites.drawSpike 完全一致)。
|
||||
// 尖端指向刺的方向,底边贴在相反一侧(地面 / 天花板 / 墙面)。
|
||||
function spikeTri(t, x, y) {
|
||||
const s = TILE;
|
||||
if (t === T.SPIKE_U) return { ax: x + s / 2, ay: y, bx: x, by: y + s, cx: x + s, cy: y + s };
|
||||
if (t === T.SPIKE_D) return { ax: x + s / 2, ay: y + s, bx: x, by: y, cx: x + s, cy: y };
|
||||
if (t === T.SPIKE_L) return { ax: x, ay: y + s / 2, bx: x + s, by: y, cx: x + s, cy: y + s };
|
||||
if (t === T.SPIKE_R) return { ax: x + s, ay: y + s / 2, bx: x, by: y, cx: x, cy: y + s };
|
||||
return { ax: x, ay: y, bx: x + s, by: y, cx: x, cy: y + s }; // 兜底(不应到达)
|
||||
}
|
||||
export { spikeBox };
|
||||
|
||||
// 三角形与轴对齐矩形的相交判定(分离轴定理,凸多边形充要条件)。
|
||||
function triRectOverlap(ax, ay, bx, by, cx, cy, rx, ry, rw, rh) {
|
||||
const rMinX = rx, rMaxX = rx + rw, rMinY = ry, rMaxY = ry + rh;
|
||||
const tMinX = Math.min(ax, bx, cx), tMaxX = Math.max(ax, bx, cx);
|
||||
const tMinY = Math.min(ay, by, cy), tMaxY = Math.max(ay, by, cy);
|
||||
if (tMaxX < rMinX || tMinX > rMaxX || tMaxY < rMinY || tMinY > rMaxY) return false; // AABB 快速排除
|
||||
const edges = [[bx - ax, by - ay], [cx - bx, cy - by], [ax - cx, ay - cy]];
|
||||
for (const [ex, ey] of edges) {
|
||||
const nx = -ey, ny = ex; // 边法线
|
||||
const t0 = nx * ax + ny * ay, t1 = nx * bx + ny * by, t2 = nx * cx + ny * cy;
|
||||
const tMin = Math.min(t0, t1, t2), tMax = Math.max(t0, t1, t2);
|
||||
const p0 = nx * rMinX + ny * rMinY, p1 = nx * rMaxX + ny * rMinY,
|
||||
p2 = nx * rMinX + ny * rMaxY, p3 = nx * rMaxX + ny * rMaxY;
|
||||
const rMin = Math.min(p0, p1, p2, p3), rMax = Math.max(p0, p1, p2, p3);
|
||||
if (tMax < rMin || rMax < tMin) return false; // 找到分离轴
|
||||
}
|
||||
return true;
|
||||
}
|
||||
export { spikeTri, triRectOverlap };
|
||||
Reference in new issue
Block a user