feat: boop game
This commit is contained in:
parent
696872d5d7
commit
39f5e6159c
|
|
@ -15,6 +15,12 @@ export class GameScene extends GameHostScene<BoopState> {
|
||||||
private turnText!: Phaser.GameObjects.Text;
|
private turnText!: Phaser.GameObjects.Text;
|
||||||
private infoText!: Phaser.GameObjects.Text;
|
private infoText!: Phaser.GameObjects.Text;
|
||||||
private winnerOverlay?: Phaser.GameObjects.Container;
|
private winnerOverlay?: Phaser.GameObjects.Container;
|
||||||
|
private whiteSupplyText!: Phaser.GameObjects.Text;
|
||||||
|
private blackSupplyText!: Phaser.GameObjects.Text;
|
||||||
|
private pieceTypeSelector!: Phaser.GameObjects.Container;
|
||||||
|
private selectedPieceType: PieceType = 'kitten';
|
||||||
|
private kittenButton!: Phaser.GameObjects.Container;
|
||||||
|
private catButton!: Phaser.GameObjects.Container;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('GameScene');
|
super('GameScene');
|
||||||
|
|
@ -26,6 +32,7 @@ export class GameScene extends GameHostScene<BoopState> {
|
||||||
this.boardContainer = this.add.container(0, 0);
|
this.boardContainer = this.add.container(0, 0);
|
||||||
this.gridGraphics = this.add.graphics();
|
this.gridGraphics = this.add.graphics();
|
||||||
this.drawGrid();
|
this.drawGrid();
|
||||||
|
this.createSupplyUI();
|
||||||
|
|
||||||
this.disposables.add(spawnEffect(new BoopPartSpawner(this, this.gameHost.state)));
|
this.disposables.add(spawnEffect(new BoopPartSpawner(this, this.gameHost.state)));
|
||||||
|
|
||||||
|
|
@ -42,8 +49,11 @@ export class GameScene extends GameHostScene<BoopState> {
|
||||||
this.watch(() => {
|
this.watch(() => {
|
||||||
const currentPlayer = this.state.currentPlayer;
|
const currentPlayer = this.state.currentPlayer;
|
||||||
this.updateTurnText(currentPlayer);
|
this.updateTurnText(currentPlayer);
|
||||||
|
this.updateSupplyUI();
|
||||||
|
this.updatePieceTypeSelector();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.createPieceTypeSelector();
|
||||||
this.setupInput();
|
this.setupInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +69,7 @@ export class GameScene extends GameHostScene<BoopState> {
|
||||||
if (this.state.winner) return;
|
if (this.state.winner) return;
|
||||||
if (this.isCellOccupied(row, col)) return;
|
if (this.isCellOccupied(row, col)) return;
|
||||||
|
|
||||||
const cmd = commands.play(this.state.currentPlayer, row, col, 'kitten');
|
const cmd = commands.play(this.state.currentPlayer, row, col, this.selectedPieceType);
|
||||||
const error = this.gameHost.onInput(cmd);
|
const error = this.gameHost.onInput(cmd);
|
||||||
if (error) {
|
if (error) {
|
||||||
console.warn('Invalid move:', error);
|
console.warn('Invalid move:', error);
|
||||||
|
|
@ -97,8 +107,8 @@ export class GameScene extends GameHostScene<BoopState> {
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
|
|
||||||
this.turnText = this.add.text(
|
this.turnText = this.add.text(
|
||||||
BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2,
|
BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2,
|
||||||
BOARD_OFFSET.y + BOARD_SIZE * CELL_SIZE + 30,
|
BOARD_OFFSET.y + BOARD_SIZE * CELL_SIZE + 30,
|
||||||
'',
|
'',
|
||||||
{
|
{
|
||||||
fontSize: '22px',
|
fontSize: '22px',
|
||||||
|
|
@ -121,6 +131,149 @@ export class GameScene extends GameHostScene<BoopState> {
|
||||||
this.updateTurnText(this.state.currentPlayer);
|
this.updateTurnText(this.state.currentPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createSupplyUI(): void {
|
||||||
|
const boardCenterX = BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2;
|
||||||
|
const uiY = BOARD_OFFSET.y - 20;
|
||||||
|
|
||||||
|
// 白色玩家储备
|
||||||
|
this.whiteSupplyText = this.add.text(
|
||||||
|
boardCenterX - 150,
|
||||||
|
uiY,
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
fontSize: '16px',
|
||||||
|
fontFamily: 'Arial',
|
||||||
|
color: '#ffffff',
|
||||||
|
backgroundColor: '#000000',
|
||||||
|
padding: { x: 10, y: 5 },
|
||||||
|
}
|
||||||
|
).setOrigin(0.5).setDepth(100);
|
||||||
|
|
||||||
|
// 黑色玩家储备
|
||||||
|
this.blackSupplyText = this.add.text(
|
||||||
|
boardCenterX + 150,
|
||||||
|
uiY,
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
fontSize: '16px',
|
||||||
|
fontFamily: 'Arial',
|
||||||
|
color: '#ffffff',
|
||||||
|
backgroundColor: '#333333',
|
||||||
|
padding: { x: 10, y: 5 },
|
||||||
|
}
|
||||||
|
).setOrigin(0.5).setDepth(100);
|
||||||
|
|
||||||
|
this.updateSupplyUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateSupplyUI(): void {
|
||||||
|
const white = this.state.players.white;
|
||||||
|
const black = this.state.players.black;
|
||||||
|
|
||||||
|
this.whiteSupplyText.setText(
|
||||||
|
`⚪ WHITE\n🐾 ${white.kitten.supply} | 🐱 ${white.cat.supply}`
|
||||||
|
);
|
||||||
|
|
||||||
|
this.blackSupplyText.setText(
|
||||||
|
`⚫ BLACK\n🐾 ${black.kitten.supply} | 🐱 ${black.cat.supply}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 高亮当前玩家
|
||||||
|
const isWhiteTurn = this.state.currentPlayer === 'white';
|
||||||
|
this.whiteSupplyText.setStyle({ backgroundColor: isWhiteTurn ? '#fbbf24' : '#000000' });
|
||||||
|
this.blackSupplyText.setStyle({ backgroundColor: !isWhiteTurn ? '#fbbf24' : '#333333' });
|
||||||
|
}
|
||||||
|
|
||||||
|
private createPieceTypeSelector(): void {
|
||||||
|
const boardCenterX = BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2;
|
||||||
|
const selectorY = BOARD_OFFSET.y + BOARD_SIZE * CELL_SIZE + 100;
|
||||||
|
|
||||||
|
this.pieceTypeSelector = this.add.container(boardCenterX, selectorY);
|
||||||
|
|
||||||
|
// 标签文字
|
||||||
|
const label = this.add.text(-120, 0, '放置:', {
|
||||||
|
fontSize: '18px',
|
||||||
|
fontFamily: 'Arial',
|
||||||
|
color: '#4b5563',
|
||||||
|
}).setOrigin(0.5, 0.5);
|
||||||
|
|
||||||
|
// 小猫按钮
|
||||||
|
this.kittenButton = this.createPieceButton('kitten', '🐾 小猫', -30);
|
||||||
|
this.catButton = this.createPieceButton('cat', '🐱 大猫', 50);
|
||||||
|
|
||||||
|
this.pieceTypeSelector.add([label, this.kittenButton, this.catButton]);
|
||||||
|
this.updatePieceTypeSelector();
|
||||||
|
}
|
||||||
|
|
||||||
|
private createPieceButton(type: PieceType, text: string, xOffset: number): Phaser.GameObjects.Container {
|
||||||
|
const container = this.add.container(xOffset, 0);
|
||||||
|
|
||||||
|
const bg = this.add.rectangle(0, 0, 100, 40, 0xe5e7eb)
|
||||||
|
.setStrokeStyle(2, 0x9ca3af);
|
||||||
|
|
||||||
|
const textObj = this.add.text(0, 0, text, {
|
||||||
|
fontSize: '16px',
|
||||||
|
fontFamily: 'Arial',
|
||||||
|
color: '#1f2937',
|
||||||
|
}).setOrigin(0.5);
|
||||||
|
|
||||||
|
container.add([bg, textObj]);
|
||||||
|
|
||||||
|
// 使按钮可交互
|
||||||
|
bg.setInteractive({ useHandCursor: true });
|
||||||
|
bg.on('pointerdown', () => {
|
||||||
|
this.selectedPieceType = type;
|
||||||
|
this.updatePieceTypeSelector();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 存储引用以便后续更新
|
||||||
|
if (type === 'kitten') {
|
||||||
|
this.kittenButton = container;
|
||||||
|
} else {
|
||||||
|
this.catButton = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
private updatePieceTypeSelector(): void {
|
||||||
|
if (!this.kittenButton || !this.catButton) return;
|
||||||
|
|
||||||
|
const white = this.state.players.white;
|
||||||
|
const black = this.state.players.black;
|
||||||
|
const currentPlayer = this.state.players[this.state.currentPlayer];
|
||||||
|
const isWhiteTurn = this.state.currentPlayer === 'white';
|
||||||
|
|
||||||
|
// 更新按钮状态
|
||||||
|
const kittenAvailable = currentPlayer.kitten.supply > 0;
|
||||||
|
const catAvailable = currentPlayer.cat.supply > 0;
|
||||||
|
|
||||||
|
// 更新小猫按钮
|
||||||
|
const kittenBg = this.kittenButton.list[0] as Phaser.GameObjects.Rectangle;
|
||||||
|
const kittenText = this.kittenButton.list[1] as Phaser.GameObjects.Text;
|
||||||
|
const isKittenSelected = this.selectedPieceType === 'kitten';
|
||||||
|
|
||||||
|
kittenBg.setFillStyle(isKittenSelected ? 0xfbbf24 : (kittenAvailable ? 0xe5e7eb : 0xd1d5db));
|
||||||
|
kittenText.setText(`🐾 小猫 (${currentPlayer.kitten.supply})`);
|
||||||
|
|
||||||
|
// 更新大猫按钮
|
||||||
|
const catBg = this.catButton.list[0] as Phaser.GameObjects.Rectangle;
|
||||||
|
const catText = this.catButton.list[1] as Phaser.GameObjects.Text;
|
||||||
|
const isCatSelected = this.selectedPieceType === 'cat';
|
||||||
|
|
||||||
|
catBg.setFillStyle(isCatSelected ? 0xfbbf24 : (catAvailable ? 0xe5e7eb : 0xd1d5db));
|
||||||
|
catText.setText(`🐱 大猫 (${currentPlayer.cat.supply})`);
|
||||||
|
|
||||||
|
// 如果选中的类型不可用,切换到可用的
|
||||||
|
if (!kittenAvailable && this.selectedPieceType === 'kitten' && catAvailable) {
|
||||||
|
this.selectedPieceType = 'cat';
|
||||||
|
this.updatePieceTypeSelector();
|
||||||
|
} else if (!catAvailable && this.selectedPieceType === 'cat' && kittenAvailable) {
|
||||||
|
this.selectedPieceType = 'kitten';
|
||||||
|
this.updatePieceTypeSelector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private updateTurnText(player: PlayerType): void {
|
private updateTurnText(player: PlayerType): void {
|
||||||
if (this.turnText) {
|
if (this.turnText) {
|
||||||
const whitePieces = this.state.players.white;
|
const whitePieces = this.state.players.white;
|
||||||
|
|
@ -201,8 +354,15 @@ class BoopPartSpawner implements Spawner<BoopPart, Phaser.GameObjects.Container>
|
||||||
const [row, col] = part.position;
|
const [row, col] = part.position;
|
||||||
const x = BOARD_OFFSET.x + col * CELL_SIZE + CELL_SIZE / 2;
|
const x = BOARD_OFFSET.x + col * CELL_SIZE + CELL_SIZE / 2;
|
||||||
const y = BOARD_OFFSET.y + row * CELL_SIZE + CELL_SIZE / 2;
|
const y = BOARD_OFFSET.y + row * CELL_SIZE + CELL_SIZE / 2;
|
||||||
obj.x = x;
|
|
||||||
obj.y = y;
|
// 使用 tween 动画平滑移动棋子
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: obj,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
duration: 200,
|
||||||
|
ease: 'Power2',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSpawn(part: BoopPart) {
|
onSpawn(part: BoopPart) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue