diff --git a/apps/client/src/scenes/game/application/GameLoop.ts b/apps/client/src/scenes/game/application/GameLoop.ts index 8973ac4..1e23d4e 100644 --- a/apps/client/src/scenes/game/application/GameLoop.ts +++ b/apps/client/src/scenes/game/application/GameLoop.ts @@ -13,6 +13,7 @@ import { CameraStep } from "./loopSteps/CameraStep"; import { BombStep } from "./loopSteps/BombStep"; import { resolveFrameDelta } from "./loopSteps/frameDelta"; +import { SocketPlayerMoveSender } from "./network/PlayerMoveSender"; type GameLoopOptions = { app: Application; @@ -40,7 +41,9 @@ this.players = players; this.myId = myId; this.inputStep = new InputStep({ getJoystickInput }); - this.simulationStep = new SimulationStep(); + this.simulationStep = new SimulationStep({ + moveSender: new SocketPlayerMoveSender(), + }); this.bombStep = new BombStep({ bombManager }); this.cameraStep = new CameraStep(); } diff --git a/apps/client/src/scenes/game/application/loopSteps/SimulationStep.ts b/apps/client/src/scenes/game/application/loopSteps/SimulationStep.ts index 750a396..92b13b3 100644 --- a/apps/client/src/scenes/game/application/loopSteps/SimulationStep.ts +++ b/apps/client/src/scenes/game/application/loopSteps/SimulationStep.ts @@ -4,10 +4,16 @@ * ローカル更新とリモート補間更新を順に実行する */ import { config } from "@client/config"; -import { socketManager } from "@client/network/SocketManager"; import { LocalPlayerController, RemotePlayerController } from "@client/scenes/game/entities/player/PlayerController"; +import type { MoveSender } from "@client/scenes/game/application/network/PlayerMoveSender"; import type { GamePlayers } from "../game.types"; +/** SimulationStep の初期化入力 */ +type SimulationStepOptions = { + moveSender: MoveSender; + nowMsProvider?: () => number; +}; + type SimulationStepParams = { me: LocalPlayerController; players: GamePlayers; @@ -17,9 +23,16 @@ /** シミュレーション段の更新処理を担うステップ */ export class SimulationStep { + private readonly moveSender: MoveSender; + private readonly nowMsProvider: () => number; private lastPositionSentTime = 0; private wasMoving = false; + constructor({ moveSender, nowMsProvider = () => performance.now() }: SimulationStepOptions) { + this.moveSender = moveSender; + this.nowMsProvider = nowMsProvider; + } + public run({ me, players, deltaSeconds, isMoving }: SimulationStepParams) { this.runLocalSimulation({ me, isMoving }); this.runRemoteSimulation({ players, deltaSeconds }); @@ -29,16 +42,16 @@ if (isMoving) { me.tick(); - const now = performance.now(); + const now = this.nowMsProvider(); if (now - this.lastPositionSentTime >= config.GAME_CONFIG.PLAYER_POSITION_UPDATE_MS) { const position = me.getPosition(); - socketManager.game.sendMove(position.x, position.y); + this.moveSender.sendMove(position.x, position.y); this.lastPositionSentTime = now; } } else if (this.wasMoving) { me.tick(); const position = me.getPosition(); - socketManager.game.sendMove(position.x, position.y); + this.moveSender.sendMove(position.x, position.y); } else { me.tick(); } diff --git a/apps/client/src/scenes/game/application/network/PlayerMoveSender.ts b/apps/client/src/scenes/game/application/network/PlayerMoveSender.ts new file mode 100644 index 0000000..9efa0c6 --- /dev/null +++ b/apps/client/src/scenes/game/application/network/PlayerMoveSender.ts @@ -0,0 +1,19 @@ +/** + * PlayerMoveSender + * ローカルプレイヤー移動の送信責務を提供する + * シミュレーション層から通信実装を分離する + */ +import { socketManager } from "@client/network/SocketManager"; + +/** 移動送信のインターフェース型 */ +export type MoveSender = { + sendMove: (x: number, y: number) => void; +}; + +/** ソケット経由で移動送信を行う実装 */ +export class SocketPlayerMoveSender implements MoveSender { + /** 指定座標をサーバーへ送信する */ + public sendMove(x: number, y: number): void { + socketManager.game.sendMove(x, y); + } +} \ No newline at end of file