Add grid-based death pulse visual effect for enemies

This commit is contained in:
Brian Fertig 2026-03-07 18:53:47 -07:00
parent 802f11430c
commit e0a6e6d116
3 changed files with 101 additions and 0 deletions

View File

@ -1,6 +1,7 @@
const HP_BAR_WIDTH = 30;
const HP_BAR_HEIGHT = 4;
const HP_BAR_OFFSET_Y = -30;
const GRID = 80;
export class BaseEnemy {
constructor(scene, x, y, player, config) {
@ -120,6 +121,36 @@ export class BaseEnemy {
this._checkContact(delta);
}
_gridDeathPulse() {
if (!this._deathPulseColor || !this.scene._pulseEffects) return;
const scene = this.scene;
const W = scene.scale.width;
const H = scene.scale.height;
const col = Math.floor(this.x / GRID);
const row = Math.floor(this.y / GRID);
const color = this._deathPulseColor;
const makeTile = (c, r, alpha) => {
if (c < 0 || r < 0 || c * GRID >= W || r * GRID >= H) return null;
return scene.add.rectangle(
c * GRID + GRID / 2, r * GRID + GRID / 2,
GRID, GRID, color, alpha,
).setDepth(2);
};
const center = makeTile(col, row, 0.55);
const adjacents = [];
[[-1, 0], [1, 0], [0, -1], [0, 1]].forEach(([dc, dr]) => {
const t = makeTile(col + dc, row + dr, 0.38);
if (t) { t.setVisible(false); adjacents.push(t); }
});
scene._pulseEffects.push({
center, adjacents, elapsed: 0, adjSpawned: false,
ADJ_MS: 80, FADE_MS: 120, END_MS: 400,
});
}
destroy() {
this.sprite?.destroy();
this._hpBg?.destroy();

View File

@ -1,5 +1,7 @@
import { BaseEnemy } from './BaseEnemy.js';
const GRID = 80;
export class ChaseEnemy extends BaseEnemy {
constructor(scene, x, y, player) {
super(scene, x, y, player, {
@ -20,4 +22,45 @@ export class ChaseEnemy extends BaseEnemy {
Math.sin(angle) * this.speed
);
}
_die() {
this._gridDeathPulse(this.x, this.y);
super._die();
}
_gridDeathPulse(px, py) {
const scene = this.scene;
const W = scene.scale.width;
const H = scene.scale.height;
const col = Math.floor(px / GRID);
const row = Math.floor(py / GRID);
const makeTile = (c, r, alpha) => {
if (c < 0 || r < 0 || c * GRID >= W || r * GRID >= H) return null;
return scene.add.rectangle(
c * GRID + GRID / 2,
r * GRID + GRID / 2,
GRID, GRID, 0x00ff44, alpha,
).setDepth(2);
};
const center = makeTile(col, row, 0.55);
// Pre-create adjacent tiles but hide them until ADJ_MS elapses
const adjacents = [];
[[-1, 0], [1, 0], [0, -1], [0, 1]].forEach(([dc, dr]) => {
const t = makeTile(col + dc, row + dr, 0.38);
if (t) { t.setVisible(false); adjacents.push(t); }
});
scene._pulseEffects.push({
center,
adjacents,
elapsed: 0,
adjSpawned: false,
ADJ_MS: 80,
FADE_MS: 120,
END_MS: 400,
});
}
}

View File

@ -53,6 +53,7 @@ export class GameScene extends Phaser.Scene {
this._levelUpPending = false;
this._frozen = false;
this._waitingForExit = false;
this._pulseEffects = [];
this.events.on('zone-waves-complete', () => this._startZoneExit());
@ -117,10 +118,35 @@ export class GameScene extends Phaser.Scene {
this._checkBulletHits();
this._checkEnemyProjectileHits();
this._pruneEnemyProjectiles();
this._updatePulseEffects(delta);
if (this._waitingForExit) this._checkPlayerExit();
}
_updatePulseEffects(delta) {
this._pulseEffects = this._pulseEffects.filter(effect => {
effect.elapsed += delta;
if (!effect.adjSpawned && effect.elapsed >= effect.ADJ_MS) {
effect.adjSpawned = true;
effect.adjacents.forEach(t => { if (t?.active) t.setVisible(true); });
}
if (effect.elapsed >= effect.FADE_MS) {
const progress = Math.min(1, (effect.elapsed - effect.FADE_MS) / (effect.END_MS - effect.FADE_MS));
if (effect.center?.active) effect.center.alpha = Math.max(0, 0.55 * (1 - progress));
effect.adjacents.forEach(t => { if (t?.active) t.alpha = Math.max(0, 0.38 * (1 - progress)); });
}
if (effect.elapsed >= effect.END_MS) {
if (effect.center?.active) effect.center.destroy();
effect.adjacents.forEach(t => { if (t?.active) t.destroy(); });
return false; // remove from array
}
return true;
});
}
_checkBulletHits() {
const bullets = this.player.bullets.getChildren();
const enemies = this.waveManager.enemies;
@ -339,5 +365,6 @@ export class GameScene extends Phaser.Scene {
this.waveManager?.reset();
this.hud?.destroy();
this._enemyProjectiles = [];
this._pulseEffects = [];
}
}