import * as Phaser from "phaser"; /** * 生命值(爱心)显示组件。 * 使用参数化心形曲线渲染一行爱心图标,表示剩余生命值。 */ export class HeartDisplay { private heartGraphics: Phaser.GameObjects.Graphics[] = []; private heartStates: boolean[] = []; private heartDrawSize: number; constructor( scene: Phaser.Scene, parent: Phaser.GameObjects.Container, options: { startX: number; y: number; spacing: number; drawSize: number; maxLives: number; initialLives: number; }, ) { this.heartDrawSize = options.drawSize; for (let i = 0; i < options.maxLives; i++) { const isAlive = i < options.initialLives; const g = scene.add.graphics(); g.setPosition(options.startX + i * options.spacing, options.y); this.heartGraphics.push(g); this.heartStates.push(isAlive); parent.add(g); this.drawHeartIcon(g, this.heartDrawSize, isAlive); } } /** * 设置当前生命值,更新爱心图标的显示状态。 * @param lives 当前生命值 */ public setLives(lives: number): void { const clamped = Math.max(0, Math.min(lives, this.heartGraphics.length)); this.heartGraphics.forEach((g, index) => { const isAlive = index < clamped; if (this.heartStates[index] !== isAlive) { this.heartStates[index] = isAlive; this.drawHeartIcon(g, this.heartDrawSize, isAlive); } }); } /** * 使用参数化心形曲线绘制爱心图标(纯前端绘制)。 * @param g 图形对象 * @param size 爱心大小 * @param alive 是否存活(存活为红色,否则为灰色) */ private drawHeartIcon( g: Phaser.GameObjects.Graphics, size: number, alive: boolean, ): void { g.clear(); const fillColor = alive ? 0xeb5757 : 0xcccccc; const fillAlpha = alive ? 1 : 0.4; // 使用参数化心形曲线构建爱心路径 const points: { x: number; y: number }[] = []; const numPoints = 50; for (let i = 0; i <= numPoints; i++) { const t = (i / numPoints) * Math.PI * 2; const px = (size * 16 * Math.pow(Math.sin(t), 3)) / 16; const py = (-size * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t))) / 16; points.push({ x: px, y: py }); } if (points.length === 0) return; // 填充爱心主体 g.fillStyle(fillColor, fillAlpha); g.beginPath(); g.moveTo(points[0].x, points[0].y); for (let i = 1; i < points.length; i++) { g.lineTo(points[i].x, points[i].y); } g.closePath(); g.fillPath(); } }