diff --git a/packages/boop-game/src/scenes/GameScene.ts b/packages/boop-game/src/scenes/GameScene.ts index 944b0fd..8a55593 100644 --- a/packages/boop-game/src/scenes/GameScene.ts +++ b/packages/boop-game/src/scenes/GameScene.ts @@ -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 { 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 { 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 { 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 { 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 { } } + private startGame(): void { + this.gameHost.setup('setup'); + } + private restartGame(): void { this.gameHost.setup('setup'); } diff --git a/packages/boop-game/src/scenes/StartOverlay.ts b/packages/boop-game/src/scenes/StartOverlay.ts new file mode 100644 index 0000000..6ba2b05 --- /dev/null +++ b/packages/boop-game/src/scenes/StartOverlay.ts @@ -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(); + } +}