feat: add game start overlay

This commit is contained in:
hypercross 2026-04-05 00:12:05 +08:00
parent 6ebfcc6c4d
commit df1c0cbb81
2 changed files with 145 additions and 5 deletions

View File

@ -6,12 +6,14 @@ import { createPieceSpawner } from './PieceSpawner';
import { SupplyUI } from './SupplyUI';
import { PieceTypeSelector } from './PieceTypeSelector';
import { WinnerOverlay } from './WinnerOverlay';
import { StartOverlay } from './StartOverlay';
export class GameScene extends GameHostScene<BoopState> {
private boardRenderer!: BoardRenderer;
private supplyUI!: SupplyUI;
private pieceTypeSelector!: PieceTypeSelector;
private winnerOverlay!: WinnerOverlay;
private startOverlay!: StartOverlay;
constructor() {
super('GameScene');
@ -25,6 +27,7 @@ export class GameScene extends GameHostScene<BoopState> {
this.supplyUI = new SupplyUI(this);
this.pieceTypeSelector = new PieceTypeSelector(this);
this.winnerOverlay = new WinnerOverlay(this, () => this.restartGame());
this.startOverlay = new StartOverlay(this, () => this.startGame());
// 设置棋子生成器
this.disposables.add(createPieceSpawner(this));
@ -33,10 +36,20 @@ export class GameScene extends GameHostScene<BoopState> {
this.boardRenderer.setupInput(
() => this.state,
(row, col) => this.handleCellClick(row, col),
() => !!this.state.winner
() => this.gameHost.status.value !== 'running' || !!this.state.winner
);
// 监听状态变化
// 监听游戏状态变化
this.addEffect(() => {
const status = this.gameHost.status.value;
if (status === 'running') {
this.startOverlay.hide();
} else if (status === 'created') {
this.startOverlay.show();
}
});
// 监听胜负状态
this.addEffect(() => {
const winner = this.state.winner;
if (winner) {
@ -52,9 +65,6 @@ export class GameScene extends GameHostScene<BoopState> {
this.supplyUI.update(this.state);
this.pieceTypeSelector.update(this.state);
});
// 设置棋子类型选择器回调
// 可以在这里添加类型改变时的额外逻辑
}
private handleCellClick(row: number, col: number): void {
@ -66,6 +76,10 @@ export class GameScene extends GameHostScene<BoopState> {
}
}
private startGame(): void {
this.gameHost.setup('setup');
}
private restartGame(): void {
this.gameHost.setup('setup');
}

View File

@ -0,0 +1,126 @@
import Phaser from 'phaser';
import { BOARD_OFFSET, CELL_SIZE, BOARD_SIZE } from './BoardRenderer';
export class StartOverlay {
private overlay?: Phaser.GameObjects.Container;
private onStartCallback?: () => void;
constructor(
private scene: Phaser.Scene,
onStart: () => void
) {
this.onStartCallback = onStart;
this.show();
}
show(): void {
if (this.overlay) {
this.overlay.destroy();
}
this.overlay = this.scene.add.container();
// 背景遮罩
const bg = this.scene.add.rectangle(
BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2,
BOARD_OFFSET.y + (BOARD_SIZE * CELL_SIZE) / 2,
BOARD_SIZE * CELL_SIZE + 200,
BOARD_SIZE * CELL_SIZE + 200,
0x000000,
0.7,
);
this.overlay.add(bg);
// 游戏标题
const titleText = this.scene.add.text(
BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2,
BOARD_OFFSET.y + (BOARD_SIZE * CELL_SIZE) / 2 - 80,
'🐱 BOOP 🐾',
{
fontSize: '60px',
fontFamily: 'Arial',
color: '#fbbf24',
fontStyle: 'bold',
},
).setOrigin(0.5);
this.overlay.add(titleText);
// 游戏说明
const rulesText = this.scene.add.text(
BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2,
BOARD_OFFSET.y + (BOARD_SIZE * CELL_SIZE) / 2 - 10,
'将小猫或大猫放在棋盘上\n推动周围的小猫\n先连成3个猫的玩家获胜',
{
fontSize: '20px',
fontFamily: 'Arial',
color: '#e5e7eb',
align: 'center',
lineSpacing: 10,
},
).setOrigin(0.5);
this.overlay.add(rulesText);
// 开始按钮
const buttonY = BOARD_OFFSET.y + (BOARD_SIZE * CELL_SIZE) / 2 + 80;
const startButton = this.scene.add.container(
BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2,
buttonY
);
const buttonBg = this.scene.add.rectangle(0, 0, 200, 60, 0xfbbf24)
.setStrokeStyle(3, 0xf59e0b)
.setInteractive({ useHandCursor: true });
const buttonText = this.scene.add.text(0, 0, '开始游戏', {
fontSize: '24px',
fontFamily: 'Arial',
color: '#1f2937',
fontStyle: 'bold',
}).setOrigin(0.5);
startButton.add([buttonBg, buttonText]);
this.overlay.add(startButton);
// 按钮交互效果
buttonBg.on('pointerover', () => {
buttonBg.setFillStyle(0xfcd34d);
});
buttonBg.on('pointerout', () => {
buttonBg.setFillStyle(0xfbbf24);
});
buttonBg.on('pointerdown', () => {
buttonBg.setFillStyle(0xf59e0b);
});
buttonBg.on('pointerup', () => {
buttonBg.setFillStyle(0xfcd34d);
this.onStartCallback?.();
});
// 标题呼吸动画
this.scene.tweens.add({
targets: titleText,
scale: 1.1,
duration: 1000,
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
});
}
hide(): void {
if (this.overlay) {
this.overlay.destroy();
this.overlay = undefined;
}
}
destroy(): void {
this.hide();
}
}