feat(ui): 实现底部塔选择面板(DOM覆盖层)和HUD辅助工具

This commit is contained in:
Cloud Bot
2026-03-21 08:04:20 +00:00
parent 45cf1e4642
commit ff3fb0536f
2 changed files with 411 additions and 0 deletions

176
game/ui/HUD.ts Normal file
View File

@@ -0,0 +1,176 @@
import type Phaser from 'phaser'
import { GAME_WIDTH, HUD_HEIGHT } from '../constants'
/**
* 游戏 HUD 辅助工具
* 负责管理"召唤下一波"按钮和波次提示横幅
*/
export class HUD {
private scene: Phaser.Scene
private waveBtn: Phaser.GameObjects.Text | null = null
private waveBannerTimeout: (() => void) | null = null
constructor(scene: Phaser.Scene) {
this.scene = scene
}
/**
* 创建"召唤下一波"按钮
* @param onClick 点击回调
*/
createWaveButton(onClick: () => void): void {
if (this.waveBtn) this.waveBtn.destroy()
this.waveBtn = this.scene.add
.text(GAME_WIDTH - 160, HUD_HEIGHT + 20, '▶ 召唤下一波', {
fontFamily: "'Press Start 2P', monospace",
fontSize: '9px',
color: '#A78BFA',
backgroundColor: '#1e3a5f',
padding: { x: 10, y: 6 },
})
.setOrigin(0, 0)
.setDepth(20)
.setInteractive({ useHandCursor: true })
this.waveBtn.on('pointerover', () => {
if (this.waveBtn) {
this.waveBtn.setStyle({ backgroundColor: '#2d5a8e' })
}
})
this.waveBtn.on('pointerout', () => {
if (this.waveBtn) {
this.waveBtn.setStyle({ backgroundColor: '#1e3a5f' })
}
})
this.waveBtn.on('pointerdown', () => {
onClick()
})
}
/** 更新按钮文字(如禁用状态) */
setWaveButtonText(text: string): void {
this.waveBtn?.setText(text)
}
disableWaveButton(): void {
if (!this.waveBtn) return
this.waveBtn.setStyle({ color: '#4B5563', backgroundColor: '#0F172A' })
this.waveBtn.removeAllListeners('pointerdown')
}
enableWaveButton(): void {
if (!this.waveBtn) return
this.waveBtn.setStyle({ color: '#A78BFA', backgroundColor: '#1e3a5f' })
}
/**
* 显示波次开始横幅
* @param waveNumber 当前波次1-based
*/
showWaveBanner(waveNumber: number, totalWaves: number): void {
const isBoss = waveNumber === totalWaves
const label = isBoss
? `!! 第${waveNumber}空降VP来袭 !!`
: `${waveNumber} 波来袭!`
const color = isBoss ? '#FBBF24' : '#F43F5E'
const bg = isBoss ? '#7C2D12' : '#0A1628'
const banner = this.scene.add
.text(GAME_WIDTH / 2, HUD_HEIGHT + 60, label, {
fontFamily: "'Press Start 2P', monospace",
fontSize: isBoss ? '16px' : '14px',
color,
backgroundColor: bg,
padding: { x: 20, y: 10 },
})
.setOrigin(0.5, 0.5)
.setDepth(40)
.setAlpha(0)
this.scene.tweens.add({
targets: banner,
alpha: 1,
duration: 300,
yoyo: true,
hold: 1800,
onComplete: () => banner.destroy(),
})
}
/** 显示周报触发提示 */
showWeeklyReportAlert(): void {
const banner = this.scene.add
.text(GAME_WIDTH / 2, HUD_HEIGHT + 120, '📋 季度周报截止!效率翻倍!', {
fontFamily: "'Press Start 2P', monospace",
fontSize: '11px',
color: '#FCD34D',
backgroundColor: '#78350F',
padding: { x: 16, y: 8 },
})
.setOrigin(0.5, 0.5)
.setDepth(40)
.setAlpha(0)
this.scene.tweens.add({
targets: banner,
alpha: 1,
duration: 400,
yoyo: true,
hold: 2500,
onComplete: () => banner.destroy(),
})
}
/** 显示胜利画面 */
showVictory(): void {
const overlay = this.scene.add.graphics()
overlay.fillStyle(0x000000, 0.6)
overlay.fillRect(0, 0, GAME_WIDTH, 720)
overlay.setDepth(50)
this.scene.add
.text(GAME_WIDTH / 2, 300, '🎉 大厂保卫成功!', {
fontFamily: "'Press Start 2P', monospace",
fontSize: '22px',
color: '#A78BFA',
backgroundColor: '#0A1628',
padding: { x: 24, y: 12 },
})
.setOrigin(0.5, 0.5)
.setDepth(55)
this.scene.add
.text(GAME_WIDTH / 2, 380, 'KPI 绩效已发放!', {
fontFamily: 'VT323, monospace',
fontSize: '24px',
color: '#A78BFA',
})
.setOrigin(0.5, 0.5)
.setDepth(55)
}
/** 显示失败画面 */
showGameOver(): void {
const overlay = this.scene.add.graphics()
overlay.fillStyle(0x000000, 0.7)
overlay.fillRect(0, 0, GAME_WIDTH, 720)
overlay.setDepth(50)
this.scene.add
.text(GAME_WIDTH / 2, 300, 'KPI 归零!被裁了!', {
fontFamily: "'Press Start 2P', monospace",
fontSize: '18px',
color: '#EF4444',
backgroundColor: '#0A1628',
padding: { x: 24, y: 12 },
})
.setOrigin(0.5, 0.5)
.setDepth(55)
}
destroy(): void {
this.waveBtn?.destroy()
if (this.waveBannerTimeout) this.waveBannerTimeout()
}
}