fea: 优化死亡特效
This commit is contained in:
1 parent
afe2f64b9b
commit
e7200989ab
9 files changed
+373
-94
No files matched your search
+37
-14
@@ -50,6 +50,7 @@ export class Player {
|
||||
this.alive = true;
|
||||
this.dead = false;
|
||||
this.deadT = 0;
|
||||
this.deathX = 0; this.deathY = 0; // 死亡动画圆心(掉坑死亡时 clamp 到关卡内)
|
||||
this.inWater = false;
|
||||
this.animT = 0;
|
||||
this.shootCool = 0;
|
||||
@@ -67,15 +68,36 @@ export class Player {
|
||||
get cx() { return this.x + this.w / 2; }
|
||||
get cy() { return this.y + this.h / 2; }
|
||||
|
||||
// 死亡:不播任何死亡动画(无等待、无镜头演出),只留音效、死亡计数,
|
||||
// 以及在死亡地点落一处 IWBTG 风格的血迹(由 Level 常驻渲染,复活后仍在)。
|
||||
kill() {
|
||||
// 纯死亡演出(不进死亡状态):粒子爆开 + 屏幕轻震 + 死亡点扩散冲击环(Level 层,见 addDeathFX)。
|
||||
// 主动按重生键时直接调它再当帧 respawn——特效留在死亡位置自然播完。
|
||||
// 圆心 clamp 到关卡内:掉坑死亡时玩家已在屏幕外,保证红叉/粒子/圆环可见
|
||||
deathBurst() {
|
||||
const L = this.level;
|
||||
const dx = clamp(this.cx, 10, Math.max(10, L.pxW - 10));
|
||||
const dy = clamp(this.cy, 10, Math.max(10, L.pxH - 10));
|
||||
sfx.death();
|
||||
L.particles.spawn(dx, dy, 16, {
|
||||
colors: ['#ff5c7a', '#ff8095', '#fff', '#ffc63a'], speed: 3.4, life: 34, size: 3, grav: 0.16,
|
||||
});
|
||||
L.shake = Math.max(L.shake || 0, 6);
|
||||
L.addDeathFX(dx, dy);
|
||||
return { dx, dy };
|
||||
}
|
||||
|
||||
// 死亡:定格在原地,等玩家手动按重生键(R / 触屏重生键)。
|
||||
// 视觉:① 死亡地点留下常驻红叉(IWBTG 血迹,复活后仍在;opts.mark=false 时不留)
|
||||
// ② deathBurst() 的"小"死亡动画(粒子+轻震+冲击环)
|
||||
// opts:
|
||||
// mark=false → 不留红叉
|
||||
// count=false → 不触发 onDeath 计数(不计入死亡数)
|
||||
kill(opts = {}) {
|
||||
if (this.dead || !this.alive) return;
|
||||
this.dead = true; this.alive = false; this.deadT = 0;
|
||||
this.vx = 0; this.vy = 0; this.sliding = false;
|
||||
sfx.death();
|
||||
this.level.addDeathMark(this.cx, this.cy);
|
||||
if (this.onDeath) this.onDeath();
|
||||
const { dx, dy } = this.deathBurst();
|
||||
this.deathX = dx; this.deathY = dy;
|
||||
if (opts.mark !== false) this.level.addDeathMark(dx, dy);
|
||||
if (opts.count !== false && this.onDeath) this.onDeath();
|
||||
}
|
||||
|
||||
respawn() {
|
||||
@@ -90,7 +112,7 @@ export class Player {
|
||||
// 安全兜底:万一重生点被地形/消失块堵住,向上顶出,别一复活就卡死
|
||||
let guard = 0;
|
||||
while (L.solidRect(this.x, this.y, this.w, this.h) && guard++ < 48) this.y -= 2;
|
||||
L.softReset();
|
||||
L.softReset(); // 不清粒子/震屏:死亡特效留在死亡位置自然播完
|
||||
this.justRespawned = true; // 供 main.js 把镜头直接吸附到重生点
|
||||
}
|
||||
|
||||
@@ -100,8 +122,9 @@ export class Player {
|
||||
this.animT++;
|
||||
if (this.shootCool > 0) this.shootCool--;
|
||||
|
||||
// 上一帧/外部(R 键自杀、假存档)触发的死亡:立刻复活,不留死亡动画
|
||||
if (this.dead) this.respawn();
|
||||
// 死亡:定格在原地等玩家手动重生(R 键 / 触屏重生键),
|
||||
// 世界继续运转(敌人/子弹/粒子照走),但玩家不响应任何输入
|
||||
if (this.dead) { this.deadT++; return; }
|
||||
|
||||
const inWater = L.waterRect(this.x + 2, this.y + 4, this.w - 4, this.h - 4);
|
||||
if (inWater && !this.inWater) sfx.splash();
|
||||
@@ -252,11 +275,11 @@ export class Player {
|
||||
|
||||
// ---- 边界 ----
|
||||
this.x = clamp(this.x, 0, L.pxW - this.w);
|
||||
if (this.y > L.pxH + 200) { this.kill(); if (this.dead) { this.respawn(); return; } }
|
||||
if (this.y > L.pxH + 200) { this.kill(); 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 (this.dead) { this.respawn(); return; } }
|
||||
if (L.deadlyRect(this.x, this.y, this.w, this.h)) { this.kill(); return; }
|
||||
|
||||
// ---- 射击 ----
|
||||
if (this.hasGun && Input.hit('shoot') && this.shootCool === 0 && L.bullets.length < 5) {
|
||||
@@ -278,8 +301,6 @@ export class Player {
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底:本帧内任何环节致死(如假存档)都立刻复活
|
||||
if (this.dead) this.respawn();
|
||||
}
|
||||
|
||||
shoot() {
|
||||
@@ -314,7 +335,7 @@ export class Player {
|
||||
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 (this.dead) { this.respawn(); return; } }
|
||||
if (e.fake) { L.showToast('假的。', 90); this.kill(); return; }
|
||||
}
|
||||
break;
|
||||
case 'goal':
|
||||
@@ -368,6 +389,8 @@ export class Player {
|
||||
}
|
||||
|
||||
draw(ctx) {
|
||||
// 死亡中:kid 已爆开,不画本体(粒子/冲击环由 Level 层渲染,重生后仍能播完)
|
||||
if (this.dead) return;
|
||||
S.drawKid(ctx, this, this.animT);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user