diff --git a/apps/client/src/scenes/game/GameManager.ts b/apps/client/src/scenes/game/GameManager.ts index 5ee9da1..c38fbfa 100644 --- a/apps/client/src/scenes/game/GameManager.ts +++ b/apps/client/src/scenes/game/GameManager.ts @@ -3,8 +3,9 @@ * ゲーム全体の初期化,更新,破棄のライフサイクルを管理する * マップ,ネットワーク同期,ゲームループを統合する */ -import { Application, Container, Ticker } from "pixi.js"; +import { Application, Container } from "pixi.js"; // 💡 Tickerのインポートは不要になりました import { socketManager } from "@client/network/SocketManager"; +import { config } from "@repo/shared"; import { GameMapController } from "./entities/map/GameMapController"; import { GameTimer } from "./application/GameTimer"; import { GameNetworkSync } from "./application/GameNetworkSync"; @@ -32,16 +33,25 @@ public getRemainingTime(): number { return this.timer.getRemainingTime(); } - + // 入力と状態管理 private joystickInput = { x: 0, y: 0 }; private isInitialized = false; private isDestroyed = false; constructor(container: HTMLDivElement, myId: string) { - this.container = container; // 明示的に代入 + this.container = container; this.myId = myId; - this.app = new Application(); + + // 💡 PixiJS v7: コンストラクタで画面サイズを固定して初期化 + const { SCREEN_WIDTH, SCREEN_HEIGHT } = config.GAME_CONFIG; + this.app = new Application({ + width: SCREEN_WIDTH, + height: SCREEN_HEIGHT, + backgroundColor: 0x111111, + antialias: true, + }); + this.worldContainer = new Container(); } @@ -49,16 +59,19 @@ * ゲームエンジンの初期化 */ public async init() { - // PixiJS本体の初期化 - await this.app.init({ resizeTo: window, backgroundColor: 0x111111, antialias: true }); - - // 初期化完了前に destroy() が呼ばれていたら、ここで処理を中断して破棄する if (this.isDestroyed) { - this.app.destroy(true, { children: true }); - return; + this.app.destroy(true, { children: true }); + return; } - this.container.appendChild(this.app.canvas); + // 💡 PixiJS v7: viewをHTMLCanvasElementとしてキャスト + const canvas = this.app.view as HTMLCanvasElement; + this.container.appendChild(canvas); + + // 💡 全デバイスで同じ範囲が見えるようにフィットさせる + canvas.style.width = "100vw"; + canvas.style.height = "100vh"; + canvas.style.objectFit = "contain"; // 背景マップの配置 const gameMap = new GameMapController(); @@ -86,7 +99,7 @@ // サーバーへゲーム準備完了を通知 socketManager.game.readyForGame(); - // メインループの登録 + // 💡 修正ポイント: PixiJS v7のTicker仕様に合わせた登録 this.app.ticker.add(this.tick); this.isInitialized = true; } @@ -101,8 +114,10 @@ /** * 毎フレームの更新処理(メインゲームループ) */ - private tick = (ticker: Ticker) => { - this.gameLoop?.tick(ticker); + // 💡 修正ポイント: 引数を deltaTime (number) に変更 + private tick = (_deltaTime: number) => { + // GameLoop内の処理が app.ticker を参照しているため、実体を渡す + this.gameLoop?.tick(this.app.ticker); }; /** @@ -111,11 +126,11 @@ public destroy() { this.isDestroyed = true; if (this.isInitialized) { + // 💡 修正ポイント: tickerから削除してから破棄 + this.app.ticker.remove(this.tick); this.app.destroy(true, { children: true }); } this.players = {}; - - // イベント購読の解除 this.networkSync?.unbind(); } -} \ No newline at end of file +} diff --git a/apps/client/src/scenes/game/GameView.tsx b/apps/client/src/scenes/game/GameView.tsx index 8e67e0a..705fb77 100644 --- a/apps/client/src/scenes/game/GameView.tsx +++ b/apps/client/src/scenes/game/GameView.tsx @@ -3,6 +3,7 @@ * ゲーム画面の描画専用コンポーネント * タイマー表示,PixiJSの描画領域,入力UIの配置のみを担当する */ +import React from "react"; import { JoystickInputPresenter } from "./input/joystick/JoystickInputPresenter"; /** 表示と入力に必要なプロパティ */ @@ -12,6 +13,8 @@ onJoystickInput: (x: number, y: number) => void; }; +// --- スタイル定義(エラー回避のため、すべてこのファイル内で完結) --- + const ROOT_STYLE: React.CSSProperties = { width: "100vw", height: "100vh", @@ -33,8 +36,7 @@ fontWeight: "bold", textShadow: "2px 2px 4px rgba(0,0,0,0.5)", fontFamily: "monospace", - userSelect: "none", - WebkitUserSelect: "none", + pointerEvents: "none", // タイマーがジョイスティック操作を邪魔しないように }; const PIXI_LAYER_STYLE: React.CSSProperties = { @@ -42,30 +44,57 @@ top: 0, left: 0, zIndex: 1, + // 💡 全機種で範囲を統一するための重要設定 + width: "100vw", + height: "100vh", + display: "flex", + justifyContent: "center", + alignItems: "center", }; const UI_LAYER_STYLE: React.CSSProperties = { position: "absolute", + top: 0, + left: 0, zIndex: 20, width: "100%", height: "100%", + // 💡 TypeScriptの型エラーを防ぐため "as const" を付与 + pointerEvents: "none" as const, }; -const TimerOverlay = ({ timeLeft }: { timeLeft: string }) =>
{timeLeft}
; +const JOYSTICK_AREA_STYLE: React.CSSProperties = { + position: "absolute", + width: "100%", + height: "100%", + pointerEvents: "auto" as const, // ジョイスティック操作だけを有効にする +}; + +/** タイマー表示用パーツ */ +const TimerOverlay = ({ timeLeft }: { timeLeft: string }) => ( +
{timeLeft}
+); /** 画面描画と入力UIをまとめて描画する */ -export const GameView = ({ timeLeft, pixiContainerRef, onJoystickInput }: Props) => { +export const GameView = ({ + timeLeft, + pixiContainerRef, + onJoystickInput, +}: Props) => { return (
- {/* タイマーUIの表示 */} + {/* 1. タイマー(最前面付近) */} - {/* PixiJS Canvas 配置領域 */} + {/* 2. ゲーム本体(PixiJS) */}
- {/* UI 配置領域 */} + {/* 3. 入力レイヤー(ジョイスティック) */}
- +
+ {/* ジョイスティックのファイルはいじらず、そのまま呼び出し */} + +
);