fix: 优化跳跃和下落手感

This commit is contained in:
cancerai committed 2026-10-24 01:54:39 +08:00
1 parent ada64fe4be
commit afe2f64b9b
25 files changed
+1197 -188

No files matched your search

+33 -14
View File
@@ -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 };