diff --git a/apps/client/src/scenes/game/GameManager.ts b/apps/client/src/scenes/game/GameManager.ts index 861ead2..57613df 100644 --- a/apps/client/src/scenes/game/GameManager.ts +++ b/apps/client/src/scenes/game/GameManager.ts @@ -98,7 +98,7 @@ this.worldContainer = new Container(); this.worldContainer.sortableChildren = true; this.gameEventFacade = new GameEventFacade({ - onGameStart: (startTime) => { + onGameStarted: (startTime) => { this.sessionFacade.setGameStart(startTime); this.uiStateSyncService.emitIfChanged(); }, @@ -122,15 +122,15 @@ moveSender, getElapsedMs: () => this.sessionFacade.getElapsedMs(), eventPorts: { - onGameStart: this.gameEventFacade.handleGameStart.bind(this.gameEventFacade), - onGameEnd: this.lockInput.bind(this), - onBombPlacedFromOthers: (payload) => { - this.gameEventFacade.handleBombPlacedFromOthers(payload); + onGameStarted: this.gameEventFacade.applyGameStarted.bind(this.gameEventFacade), + onGameEnded: this.lockInput.bind(this), + onRemoteBombPlaced: (payload) => { + this.gameEventFacade.applyRemoteBombPlaced(payload); }, - onBombPlacedAckFromNetwork: (payload) => { - this.gameEventFacade.handleBombPlacedAck(payload); + onBombPlacementAcknowledged: (payload) => { + this.gameEventFacade.applyBombPlacementAcknowledged(payload); }, - onPlayerDeadFromNetwork: (payload) => { + onRemotePlayerDead: (payload) => { this.combatFacade.handleNetworkPlayerDead(payload); }, onBombExploded: (payload) => { diff --git a/apps/client/src/scenes/game/GameView.tsx b/apps/client/src/scenes/game/GameView.tsx index 9120496..2604944 100644 --- a/apps/client/src/scenes/game/GameView.tsx +++ b/apps/client/src/scenes/game/GameView.tsx @@ -5,10 +5,10 @@ */ import { GameInputOverlay } from "./input/GameInputOverlay"; import { - PIXI_LAYER_STYLE, - ROOT_STYLE, - START_COUNTDOWN_STYLE, - TIMER_STYLE, + GAME_VIEW_PIXI_LAYER_STYLE, + GAME_VIEW_ROOT_STYLE, + GAME_VIEW_START_COUNTDOWN_STYLE, + GAME_VIEW_TIMER_STYLE, } from "./styles/GameView.styles"; /** 表示と入力に必要なプロパティ */ @@ -22,7 +22,7 @@ }; const TimerOverlay = ({ timeLeft }: { timeLeft: string }) => ( -
{timeLeft}
+
{timeLeft}
); /** 画面描画と入力UIをまとめて描画する */ @@ -35,16 +35,16 @@ onPlaceBomb, }: Props) => { return ( -
+
{/* タイマーUIの表示 */} {startCountdownText && ( -
{startCountdownText}
+
{startCountdownText}
)} {/* PixiJS Canvas 配置領域 */} -
+
{/* 入力UI レイヤー */} void; + onGameStarted: (startTime: number) => void; getBombManager: () => BombManager | null; }; /** 受信イベントの適用窓口を提供する */ export class GameEventFacade { - private readonly onGameStart: (startTime: number) => void; + private readonly onGameStarted: (startTime: number) => void; private readonly getBombManager: () => BombManager | null; - constructor({ onGameStart, getBombManager }: GameEventFacadeOptions) { - this.onGameStart = onGameStart; + constructor({ onGameStarted, getBombManager }: GameEventFacadeOptions) { + this.onGameStarted = onGameStarted; this.getBombManager = getBombManager; } - /** サーバー同期のゲーム開始時刻を適用する */ - public handleGameStart(startTime: number): void { - this.onGameStart(startTime); + /** 内部ゲーム開始イベントを適用する */ + public applyGameStarted(startTime: number): void { + this.onGameStarted(startTime); } - /** 他プレイヤー爆弾設置を反映する */ - public handleBombPlacedFromOthers(payload: BombPlacedPayload): void { + /** 内部リモート爆弾設置イベントを適用する */ + public applyRemoteBombPlaced(payload: BombPlacedPayload): void { this.getBombManager()?.applyPlacedBombFromOthers(payload); } - /** 爆弾設置ACKを反映する */ - public handleBombPlacedAck(payload: BombPlacedAckPayload): void { + /** 内部爆弾設置ACKイベントを適用する */ + public applyBombPlacementAcknowledged(payload: BombPlacedAckPayload): void { this.getBombManager()?.applyPlacedBombAck(payload); } } \ No newline at end of file diff --git a/apps/client/src/scenes/game/application/GameNetworkSync.ts b/apps/client/src/scenes/game/application/GameNetworkSync.ts index fa34c80..198f43d 100644 --- a/apps/client/src/scenes/game/application/GameNetworkSync.ts +++ b/apps/client/src/scenes/game/application/GameNetworkSync.ts @@ -16,6 +16,12 @@ createNetworkSubscriptions, type SocketSubscriptionDictionary, } from "./network/NetworkSubscriptions"; +import { + toBombPlacementAcknowledgedPayload, + toGameStartedAt, + toRemoteBombPlacedPayload, + toRemotePlayerDeadPayload, +} from "./network/adapters/GameNetworkEventAdapter"; import { PlayerSyncHandler } from "./network/handlers/PlayerSyncHandler"; import { MapSyncHandler } from "./network/handlers/MapSyncHandler"; import { CombatSyncHandler } from "./network/handlers/CombatSyncHandler"; @@ -29,11 +35,11 @@ myId: string; gameMap: GameMapController; appearanceResolver: AppearanceResolver; - onGameStart: (startTime: number) => void; - onGameEnd: () => void; - onBombPlacedFromOthers: (payload: BombPlacedPayload) => void; - onBombPlacedAckFromNetwork: (payload: BombPlacedAckPayload) => void; - onPlayerDeadFromNetwork: (payload: PlayerDeadPayload) => void; + onGameStarted: (startTime: number) => void; + onGameEnded: () => void; + onRemoteBombPlaced: (payload: BombPlacedPayload) => void; + onBombPlacementAcknowledged: (payload: BombPlacedAckPayload) => void; + onRemotePlayerDead: (payload: PlayerDeadPayload) => void; }; /** ゲーム中のネットワークイベント購読と同期処理を管理する */ @@ -41,8 +47,8 @@ private playerSyncHandler: PlayerSyncHandler; private mapSyncHandler: MapSyncHandler; private combatSyncHandler: CombatSyncHandler; - private onGameStart: (startTime: number) => void; - private onGameEnd: () => void; + private onGameStarted: (startTime: number) => void; + private onGameEnded: () => void; private socketSubscriptions: SocketSubscriptionDictionary; private isBound = false; @@ -54,15 +60,16 @@ console.log(message); }; - private handleGameStart = (data: GameStartPayload) => { - if (data && data.startTime) { - this.onGameStart(data.startTime); - this.debugLog(`[GameNetworkSync] ゲーム開始時刻同期完了: ${data.startTime}`); + private handleReceivedGameStart = (payload: GameStartPayload) => { + const startTime = toGameStartedAt(payload); + if (startTime !== null) { + this.onGameStarted(startTime); + this.debugLog(`[GameNetworkSync] ゲーム開始時刻同期完了: ${startTime}`); } }; - private handleGameEnd = () => { - this.onGameEnd(); + private handleReceivedGameEnd = () => { + this.onGameEnded(); }; constructor({ @@ -71,11 +78,11 @@ myId, gameMap, appearanceResolver, - onGameStart, - onGameEnd, - onBombPlacedFromOthers, - onBombPlacedAckFromNetwork, - onPlayerDeadFromNetwork, + onGameStarted, + onGameEnded, + onRemoteBombPlaced, + onBombPlacementAcknowledged, + onRemotePlayerDead, }: GameNetworkSyncOptions) { this.playerSyncHandler = new PlayerSyncHandler({ worldContainer, @@ -87,23 +94,29 @@ gameMap, }); this.combatSyncHandler = new CombatSyncHandler({ - onBombPlacedFromOthers, - onBombPlacedAckFromNetwork, - onPlayerDeadFromNetwork, + onRemoteBombPlaced: (payload) => { + onRemoteBombPlaced(toRemoteBombPlacedPayload(payload)); + }, + onBombPlacementAcknowledged: (payload) => { + onBombPlacementAcknowledged(toBombPlacementAcknowledgedPayload(payload)); + }, + onRemotePlayerDead: (payload) => { + onRemotePlayerDead(toRemotePlayerDeadPayload(payload)); + }, }); - this.onGameStart = onGameStart; - this.onGameEnd = onGameEnd; + this.onGameStarted = onGameStarted; + this.onGameEnded = onGameEnded; this.socketSubscriptions = createNetworkSubscriptions({ onCurrentPlayers: this.playerSyncHandler.handleCurrentPlayers, onNewPlayer: this.playerSyncHandler.handleNewPlayer, - onGameStart: this.handleGameStart, + onGameStart: this.handleReceivedGameStart, onUpdatePlayers: this.playerSyncHandler.handlePlayerUpdates, onRemovePlayer: this.playerSyncHandler.handleRemovePlayer, onUpdateMapCells: this.mapSyncHandler.handleUpdateMapCells, - onGameEnd: this.handleGameEnd, - onBombPlaced: this.combatSyncHandler.handleBombPlaced, - onBombPlacedAck: this.combatSyncHandler.handleBombPlacedAck, - onPlayerDead: this.combatSyncHandler.handlePlayerDead, + onGameEnd: this.handleReceivedGameEnd, + onBombPlaced: this.combatSyncHandler.handleReceivedBombPlaced, + onBombPlacedAck: this.combatSyncHandler.handleReceivedBombPlacedAck, + onPlayerDead: this.combatSyncHandler.handleReceivedPlayerDead, }); } diff --git a/apps/client/src/scenes/game/application/network/adapters/GameNetworkEventAdapter.ts b/apps/client/src/scenes/game/application/network/adapters/GameNetworkEventAdapter.ts new file mode 100644 index 0000000..0ea57ac --- /dev/null +++ b/apps/client/src/scenes/game/application/network/adapters/GameNetworkEventAdapter.ts @@ -0,0 +1,41 @@ +/** + * GameNetworkEventAdapter + * 受信イベントペイロードを内部イベント入力へ変換する + * 外部プロトコル互換を維持しつつ内部命名へ正規化する + */ +import type { + BombPlacedAckPayload, + BombPlacedPayload, + GameStartPayload, + PlayerDeadPayload, +} from "@repo/shared"; + +/** ゲーム開始受信ペイロードから開始時刻を抽出する */ +export const toGameStartedAt = (payload: GameStartPayload): number | null => { + if (!payload || !payload.startTime) { + return null; + } + + return payload.startTime; +}; + +/** 爆弾設置受信ペイロードを内部ペイロードへ正規化する */ +export const toRemoteBombPlacedPayload = ( + payload: BombPlacedPayload, +): BombPlacedPayload => { + return payload; +}; + +/** 爆弾設置ACK受信ペイロードを内部ペイロードへ正規化する */ +export const toBombPlacementAcknowledgedPayload = ( + payload: BombPlacedAckPayload, +): BombPlacedAckPayload => { + return payload; +}; + +/** プレイヤー死亡受信ペイロードを内部ペイロードへ正規化する */ +export const toRemotePlayerDeadPayload = ( + payload: PlayerDeadPayload, +): PlayerDeadPayload => { + return payload; +}; \ No newline at end of file diff --git a/apps/client/src/scenes/game/application/network/handlers/CombatSyncHandler.ts b/apps/client/src/scenes/game/application/network/handlers/CombatSyncHandler.ts index 03e58fe..f2f23df 100644 --- a/apps/client/src/scenes/game/application/network/handlers/CombatSyncHandler.ts +++ b/apps/client/src/scenes/game/application/network/handlers/CombatSyncHandler.ts @@ -11,39 +11,39 @@ /** CombatSyncHandler の初期化入力 */ export type CombatSyncHandlerOptions = { - onBombPlacedFromOthers: (payload: BombPlacedPayload) => void; - onBombPlacedAckFromNetwork: (payload: BombPlacedAckPayload) => void; - onPlayerDeadFromNetwork: (payload: PlayerDeadPayload) => void; + onRemoteBombPlaced: (payload: BombPlacedPayload) => void; + onBombPlacementAcknowledged: (payload: BombPlacedAckPayload) => void; + onRemotePlayerDead: (payload: PlayerDeadPayload) => void; }; /** 戦闘関連イベントの橋渡しを担当する */ export class CombatSyncHandler { - private readonly onBombPlacedFromOthers: (payload: BombPlacedPayload) => void; - private readonly onBombPlacedAckFromNetwork: (payload: BombPlacedAckPayload) => void; - private readonly onPlayerDeadFromNetwork: (payload: PlayerDeadPayload) => void; + private readonly onRemoteBombPlaced: (payload: BombPlacedPayload) => void; + private readonly onBombPlacementAcknowledged: (payload: BombPlacedAckPayload) => void; + private readonly onRemotePlayerDead: (payload: PlayerDeadPayload) => void; constructor({ - onBombPlacedFromOthers, - onBombPlacedAckFromNetwork, - onPlayerDeadFromNetwork, + onRemoteBombPlaced, + onBombPlacementAcknowledged, + onRemotePlayerDead, }: CombatSyncHandlerOptions) { - this.onBombPlacedFromOthers = onBombPlacedFromOthers; - this.onBombPlacedAckFromNetwork = onBombPlacedAckFromNetwork; - this.onPlayerDeadFromNetwork = onPlayerDeadFromNetwork; + this.onRemoteBombPlaced = onRemoteBombPlaced; + this.onBombPlacementAcknowledged = onBombPlacementAcknowledged; + this.onRemotePlayerDead = onRemotePlayerDead; } - /** 他プレイヤーの爆弾設置イベントを橋渡しする */ - public handleBombPlaced = (payload: BombPlacedPayload): void => { - this.onBombPlacedFromOthers(payload); + /** 他プレイヤーの爆弾設置受信イベントを橋渡しする */ + public handleReceivedBombPlaced = (payload: BombPlacedPayload): void => { + this.onRemoteBombPlaced(payload); }; - /** 設置ACKイベントを橋渡しする */ - public handleBombPlacedAck = (payload: BombPlacedAckPayload): void => { - this.onBombPlacedAckFromNetwork(payload); + /** 爆弾設置ACK受信イベントを橋渡しする */ + public handleReceivedBombPlacedAck = (payload: BombPlacedAckPayload): void => { + this.onBombPlacementAcknowledged(payload); }; - /** 被弾通知イベントを橋渡しする */ - public handlePlayerDead = (payload: PlayerDeadPayload): void => { - this.onPlayerDeadFromNetwork(payload); + /** プレイヤー死亡受信イベントを橋渡しする */ + public handleReceivedPlayerDead = (payload: PlayerDeadPayload): void => { + this.onRemotePlayerDead(payload); }; } \ No newline at end of file diff --git a/apps/client/src/scenes/game/application/orchestrators/GameSceneOrchestrator.ts b/apps/client/src/scenes/game/application/orchestrators/GameSceneOrchestrator.ts index c670f80..d614355 100644 --- a/apps/client/src/scenes/game/application/orchestrators/GameSceneOrchestrator.ts +++ b/apps/client/src/scenes/game/application/orchestrators/GameSceneOrchestrator.ts @@ -24,11 +24,11 @@ myId: string; gameMap: GameMapController; appearanceResolver: AppearanceResolver; - onGameStart: (startTime: number) => void; - onGameEnd: () => void; - onBombPlacedFromOthers: (payload: BombPlacedPayload) => void; - onBombPlacedAckFromNetwork: (payload: BombPlacedAckPayload) => void; - onPlayerDeadFromNetwork: (payload: PlayerDeadPayload) => void; + onGameStarted: (startTime: number) => void; + onGameEnded: () => void; + onRemoteBombPlaced: (payload: BombPlacedPayload) => void; + onBombPlacementAcknowledged: (payload: BombPlacedAckPayload) => void; + onRemotePlayerDead: (payload: PlayerDeadPayload) => void; }; /** BombManager 生成入力型 */ @@ -43,11 +43,11 @@ /** シーン層で扱うイベント通知ポート群 */ export type GameSceneEventPorts = { - onGameStart: (startTime: number) => void; - onGameEnd: () => void; - onBombPlacedFromOthers: (payload: BombPlacedPayload) => void; - onBombPlacedAckFromNetwork: (payload: BombPlacedAckPayload) => void; - onPlayerDeadFromNetwork: (payload: PlayerDeadPayload) => void; + onGameStarted: (startTime: number) => void; + onGameEnded: () => void; + onRemoteBombPlaced: (payload: BombPlacedPayload) => void; + onBombPlacementAcknowledged: (payload: BombPlacedAckPayload) => void; + onRemotePlayerDead: (payload: PlayerDeadPayload) => void; onBombExploded: (payload: BombExplodedPayload) => void; }; @@ -162,11 +162,11 @@ myId: this.myId, gameMap, appearanceResolver: this.appearanceResolver, - onGameStart: this.eventPorts.onGameStart, - onGameEnd: this.eventPorts.onGameEnd, - onBombPlacedFromOthers: this.eventPorts.onBombPlacedFromOthers, - onBombPlacedAckFromNetwork: this.eventPorts.onBombPlacedAckFromNetwork, - onPlayerDeadFromNetwork: this.eventPorts.onPlayerDeadFromNetwork, + onGameStarted: this.eventPorts.onGameStarted, + onGameEnded: this.eventPorts.onGameEnded, + onRemoteBombPlaced: this.eventPorts.onRemoteBombPlaced, + onBombPlacementAcknowledged: this.eventPorts.onBombPlacementAcknowledged, + onRemotePlayerDead: this.eventPorts.onRemotePlayerDead, }); networkSync.bind(); return networkSync; diff --git a/apps/client/src/scenes/game/styles/GameView.styles.ts b/apps/client/src/scenes/game/styles/GameView.styles.ts index 5fb2e0a..d13d802 100644 --- a/apps/client/src/scenes/game/styles/GameView.styles.ts +++ b/apps/client/src/scenes/game/styles/GameView.styles.ts @@ -6,7 +6,7 @@ import type { CSSProperties } from "react"; /** ゲーム画面全体のルートスタイル */ -export const ROOT_STYLE: CSSProperties = { +export const GAME_VIEW_ROOT_STYLE: CSSProperties = { width: "100vw", height: "100vh", overflow: "hidden", @@ -17,7 +17,7 @@ }; /** 上部中央のタイマー表示スタイル */ -export const TIMER_STYLE: CSSProperties = { +export const GAME_VIEW_TIMER_STYLE: CSSProperties = { position: "absolute", top: "20px", left: "50%", @@ -33,7 +33,7 @@ }; /** Pixi描画レイヤーの配置スタイル */ -export const PIXI_LAYER_STYLE: CSSProperties = { +export const GAME_VIEW_PIXI_LAYER_STYLE: CSSProperties = { position: "absolute", top: 0, left: 0, @@ -41,7 +41,7 @@ }; /** 画面中央の開始カウントダウン表示スタイル */ -export const START_COUNTDOWN_STYLE: CSSProperties = { +export const GAME_VIEW_START_COUNTDOWN_STYLE: CSSProperties = { position: "absolute", top: "50%", left: "50%",