feat: add game start overlay
This commit is contained in:
parent
6ebfcc6c4d
commit
df1c0cbb81
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue