fix: 修复了冰块惯性问题和死亡效果

This commit is contained in:
cancerai committed 2026-10-22 19:43:01 +08:00
1 parent 47a462be01
commit 4b3dfd635b
15 files changed
+633 -38

No files matched your search

+70 -21
View File
@@ -16,6 +16,13 @@ export const PHY = {
WATER_RISE: 0.42,
ICE_ACC: 0.18,
ICE_FRIC: 0.07,
// 滑出冰块后的惯性:空中几乎不损失,地面缓慢收住
SLIDE_FRIC: 0.12, // 非冰地面上收住惯性的摩擦
SLIDE_AIR: 0.02, // 空中惯性衰减(≈0,滑出冰块边缘能一路飞出去)
SLIDE_EASE: 0.14, // 超速滑行时按住同向键,收敛回正常走速的速度
SLIDE_BRAKE: 0.45, // 滑行中按反向键的刹车
SLIDE_MIN: 0.35, // 冰面上攒到多大速度才算进入滑行状态
SLIDE_STOP: 0.05, // 低于此速度惯性结束
};
export class Player {
@@ -41,6 +48,8 @@ export class Player {
this.animT = 0;
this.shootCool = 0;
this.mover = null;
this.sliding = false; // 冰面滑行惯性状态:离开冰块后仍保留
this.justRespawned = false; // 本帧刚复活(供镜头瞬移)
if (full) {
this.hasGun = !!this.level.data.startGun;
this.apples = 0;
@@ -52,16 +61,14 @@ export class Player {
get cx() { return this.x + this.w / 2; }
get cy() { return this.y + this.h / 2; }
// 死亡:不播任何死亡动画(无等待、无镜头演出),只留音效、死亡计数,
// 以及在死亡地点落一处 IWBTG 风格的血迹(由 Level 常驻渲染,复活后仍在)。
kill() {
if (this.dead || !this.alive) return;
this.dead = true; this.alive = false; this.deadT = 0;
this.vx = 0; this.vy = 0;
this.vx = 0; this.vy = 0; this.sliding = false;
sfx.death();
this.level.particles.burst(this.cx, this.cy, {
n: 26, colors: ['#e03050', '#fff', '#ffc63a', '#8e2530'],
speed: 4.5, life: 44, size: 4, grav: 0.18,
});
this.level.shake = 9;
this.level.addDeathMark(this.cx, this.cy);
if (this.onDeath) this.onDeath();
}
@@ -71,11 +78,13 @@ export class Player {
const sp = this.savePoint || L.spawn;
this.x = sp.x; this.y = sp.y;
this.vx = 0; this.vy = 0; this.jumps = 0;
this.squash = 1; this.shootCool = 0;
this.squash = 1; this.shootCool = 0; this.sliding = false;
this.deadT = 0;
// 安全兜底:万一重生点被地形/消失块堵住,向上顶出,别一复活就卡死
let guard = 0;
while (L.solidRect(this.x, this.y, this.w, this.h) && guard++ < 48) this.y -= 2;
L.softReset();
this.justRespawned = true; // 供 main.js 把镜头直接吸附到重生点
}
// ---------------- 每帧更新 ----------------
@@ -84,32 +93,60 @@ export class Player {
this.animT++;
if (this.shootCool > 0) this.shootCool--;
if (this.dead) {
if (++this.deadT > 34) this.respawn();
return;
}
// 上一帧/外部(R 键自杀、假存档)触发的死亡:立刻复活,不留死亡动画
if (this.dead) this.respawn();
const inWater = L.waterRect(this.x + 2, this.y + 4, this.w - 4, this.h - 4);
if (inWater && !this.inWater) sfx.splash();
this.inWater = inWater;
// ---- 水平输入(IWBT:瞬时,无惯性)----
// ---- 水平输入(IWBT:普通地面瞬时、无惯性;冰面打滑并保留惯性)----
const moveL = Input.is('left'), moveR = Input.is('right');
const onIce = this.grounded && L.iceAtBottom(this.x, this.y, this.w, this.h);
let target = 0;
if (moveL && !moveR) target = -PHY.HSPD;
else if (moveR && !moveL) target = PHY.HSPD;
const ICE_MAX = PHY.HSPD * 1.35;
const decel = f => {
if (Math.abs(this.vx) <= f) this.vx = 0;
else this.vx -= sgn(this.vx) * f;
};
if (onIce) {
// 冰面:打滑
// 冰面:加速/减速都很慢,速度上限也更高
if (target !== 0) this.vx += sgn(target) * PHY.ICE_ACC;
else {
if (Math.abs(this.vx) < PHY.ICE_FRIC) this.vx = 0;
else this.vx -= sgn(this.vx) * PHY.ICE_FRIC;
else decel(PHY.ICE_FRIC);
this.vx = clamp(this.vx, -ICE_MAX, ICE_MAX);
// 攒出一定速度即进入「滑行」状态——这是惯性得以保留的关键
if (Math.abs(this.vx) > PHY.SLIDE_MIN) this.sliding = true;
} else if (this.sliding && Math.abs(this.vx) > PHY.SLIDE_STOP) {
// 滑出冰块后:惯性依然保留 —— 空中几乎不衰减,地面才慢慢收住
if (target !== 0) {
const dir = sgn(this.vx);
if (dir === 0 || sgn(target) === dir) {
if (Math.abs(this.vx) >= PHY.HSPD) {
// 还在超速滑行:同向按键只让它缓慢收敛回正常走速,不再继续加速
this.vx -= dir * PHY.SLIDE_EASE;
if (Math.abs(this.vx) < PHY.HSPD) { this.vx = target; this.sliding = false; }
} else {
this.vx = target; // 已低于走速:交还玩家控制
this.sliding = false;
}
} else {
this.vx -= dir * PHY.SLIDE_BRAKE; // 反向按键:刹车,刹住后立刻反向
if (sgn(this.vx) === sgn(target)) { this.vx = target; this.sliding = false; }
}
this.vx = clamp(this.vx, -ICE_MAX, ICE_MAX);
} else {
decel(this.grounded ? PHY.SLIDE_FRIC : PHY.SLIDE_AIR);
if (this.vx === 0) this.sliding = false;
}
this.vx = clamp(this.vx, -PHY.HSPD * 1.35, PHY.HSPD * 1.35);
if (this.vx !== 0 && Math.abs(this.vx) < PHY.SLIDE_STOP) { this.vx = 0; this.sliding = false; }
} else {
// 常规地面/空中(非滑行):IWBT 标准手感,松手即停、无惯性
this.vx = target;
this.sliding = false;
}
if (target !== 0) this.facing = sgn(target);
@@ -194,13 +231,21 @@ export class Player {
}
this.squash += (1 - this.squash) * 0.22;
// ---- 滑行反馈:超速滑行(冰上,或刚滑出冰块仍带惯性)时脚下溅起冰屑 ----
if (this.sliding && this.grounded && Math.abs(this.vx) > PHY.HSPD && this.animT % 5 === 0) {
L.particles.spawn(this.cx - sgn(this.vx) * 5, this.y + this.h - 2, 2, {
colors: ['#dff4ff', '#8fd8f0', '#fff'], speed: 1.1, life: 16, size: 2,
dir: -Math.PI / 2, spread: 1.7, grav: 0.1,
});
}
// ---- 边界 ----
this.x = clamp(this.x, 0, L.pxW - this.w);
if (this.y > L.pxH + 200) this.kill();
if (this.y > L.pxH + 200) { this.kill(); if (this.dead) { this.respawn(); return; } }
if (this.y < -300) { this.y = -300; this.vy = 0; }
// ---- 致死 ----
if (L.deadlyRect(this.x, this.y, this.w, this.h)) this.kill();
// ---- 致死:同帧立刻复活,画面上不留任何死亡状态 ----
if (L.deadlyRect(this.x, this.y, this.w, this.h)) { this.kill(); if (this.dead) { this.respawn(); return; } }
// ---- 射击 ----
if (this.hasGun && Input.hit('shoot') && this.shootCool === 0 && L.bullets.length < 5) {
@@ -221,6 +266,9 @@ export class Player {
}
}
}
// 兜底:本帧内任何环节致死(如假存档)都立刻复活
if (this.dead) this.respawn();
}
shoot() {
@@ -254,7 +302,8 @@ export class Player {
sfx.save();
L.particles.spawn(e.x + e.w / 2, e.y + e.h / 2, 18,
{ colors: ['#b06bff', '#fff', '#e5d0ff'], speed: 2.6, life: 30 });
if (e.fake) { L.showToast('假的。', 90); this.kill(); }
// 假存档:立刻复活回上一个存档点,不播死亡动画
if (e.fake) { L.showToast('假的。', 90); this.kill(); if (this.dead) { this.respawn(); return; } }
}
break;
case 'goal':