👽

Phaser3 + Redux + TypeScript

に公開

Reduxを使えば異なるPhaserシーン間でスコアを共有できます。以下に具体的な実装方法を説明します。
「ランダムに画面に的(ターゲット)が出現し、それをクリックすると得点がアップする」
このようなゲームは「クリックターゲットゲーム」「モグラたたき風ゲーム」などと呼ばれます。

実装手順(Phaser3 + Redux + TypeScript)

1. Redux Storeの設定

// src/redux/gameSlice.ts
import { createSlice } from '@reduxjs/toolkit';

interface GameState {
  score: number;
}

const initialState: GameState = {
  score: 0,
};

export const gameSlice = createSlice({
  name: 'game',
  initialState,
  reducers: {
    incrementScore: (state) => {
      state.score += 1;
    },
    resetScore: (state) => {
      state.score = 0;
    }
  }
});

export const { incrementScore, resetScore } = gameSlice.actions;
export default gameSlice.reducer;

2. Storeの統合

// src/redux/store.ts
import { configureStore } from '@reduxjs/toolkit';
import gameReducer from './gameSlice';

export const store = configureStore({
  reducer: {
    game: gameReducer
  }
});

export type RootState = ReturnType;
export type AppDispatch = typeof store.dispatch;

3. HUDシーンの実装

// src/scenes/HudScene.ts
import Phaser from 'phaser';
import { store } from '../redux/store';
import { useAppSelector } from '../redux/hooks';

export class HudScene extends Phaser.Scene {
  private scoreText!: Phaser.GameObjects.Text;

  constructor() {
    super({ key: 'HudScene' });
  }

  create() {
    this.scoreText = this.add.text(10, 10, 'Score: 0', {
      fontSize: '24px',
      color: '#ffffff'
    });

    // Reduxの状態変化を監視
    store.subscribe(() => {
      const score = store.getState().game.score;
      this.scoreText.setText(`Score: ${score}`);
    });
  }
}

4. ゲームシーンの修正

// src/scenes/GameScene.ts
import Phaser from 'phaser';
import { store } from '../redux/store';
import { incrementScore } from '../redux/gameSlice';

export class GameScene extends Phaser.Scene {
  constructor() {
    super({ key: 'GameScene' });
  }

  create() {
    // 的を作成するロジック
    const target = this.add.circle(400, 300, 20, 0xff0000)
      .setInteractive()
      .on('pointerdown', () => {
        store.dispatch(incrementScore());
        target.destroy();
      });
  }
}

5. シーン起動設定

// main.ts
import Phaser from 'phaser';
import { GameScene } from './scenes/GameScene';
import { HudScene } from './scenes/HudScene';

const config: Phaser.Types.Core.GameConfig = {
  type: Phaser.AUTO,
  width: 800,
  height: 600,
  scene: [GameScene, HudScene], // 複数シーンを同時起動
  parent: 'game-container',
};

動作のポイント

  1. 複数シーン同時実行:

    • scene: [GameScene, HudScene] で両シーンを並列実行
    • HUDシーンは常に前面に表示される
  2. Reduxの状態同期:

    • store.subscribe() で状態変化を監視
    • スコア更新時に自動でテキスト更新
  3. PhaserとReactの統合:

// Reactコンポーネント例
import { useSelector } from 'react-redux';

const GameContainer = () => {
    const score = useSelector((state: RootState) => state.game.score);
    return (
        <div>
            <div id="game-container" />
            <div className="overlay-score">React側スコア: {score}</div>
        </div>
    );
};

最適化のヒント

  • デバウンス処理: 頻繁な更新がある場合

    store.subscribe(() => {
      this.time.delayedCall(100, () => {
        this.scoreText.setText(`Score: ${store.getState().game.score}`);
      });
    });
    
  • パフォーマンス計測:

    // store.ts
    import { Middleware } from '@reduxjs/toolkit';
    
    const phaserMiddleware: Middleware = store => next => action => {
      const start = performance.now();
      const result = next(action);
      console.log(`Action ${action.type} took ${performance.now() - start}ms`);
      return result;
    };
    
    export const store = configureStore({
      reducer: { /* ... */ },
      middleware: (getDefaultMiddleware) => 
        getDefaultMiddleware().concat(phaserMiddleware)
    });
    

Reduxを活用することで、シーン間の状態管理がシンプルになり、ゲームの状態を一元管理できます。HUDシーン以外にも、ポーズメニューやリザルト画面など、さまざまなUI要素と連携可能です。

Discussion