diff --git a/apps/client/src/app.tsx b/apps/client/src/app.tsx index ef5d4ef..59067dd 100644 --- a/apps/client/src/app.tsx +++ b/apps/client/src/app.tsx @@ -2,7 +2,9 @@ import { Joystick } from "react-joystick-component"; import { io, Socket } from "socket.io-client"; -// プレイヤーの型定義(サーバーと同じ形にする) +// 設定:マップの広さ(全員共通) +const MAP_SIZE = 2000; + type Player = { id: string; x: number; @@ -11,127 +13,205 @@ }; function App() { - // 自分のIDを保存する場所 const [myId, setMyId] = useState(null); - // 全員のプレイヤー情報を保存する場所 { "ID": {プレイヤー情報}, ... } const [players, setPlayers] = useState>({}); - // 通信の接続(二重接続防止のため useRef を使う) + // 画面サイズ(カメラ計算用) + const [viewport, setViewport] = useState({ w: window.innerWidth, h: window.innerHeight }); + + // 自分の位置(計算用) + const myPosRef = useRef({ x: MAP_SIZE / 2, y: MAP_SIZE / 2 }); + const socketRef = useRef(null); + // ★ 追加:初期化フラグ + const initializedRef = useRef(false); + + // ★ 追加:サーバー上の位置と、手元の計算用位置を同期させる魔法 useEffect(() => { - // 1. サーバーに接続 - // Viteのプロキシ設定があるので、パスは自動で解決されます + // まだIDがない、または既に同期済みなら何もしない + if (!myId || initializedRef.current) return; + + const me = players[myId]; + if (me) { + // サーバーの座標を、自分の計算基準(ref)にコピー! + myPosRef.current = { x: me.x, y: me.y }; + initializedRef.current = true; + console.log("📍 初期位置を同期しました:", me.x, me.y); + } + }, [players, myId]); + + useEffect(() => { + const handleResize = () => setViewport({ w: window.innerWidth, h: window.innerHeight }); + window.addEventListener("resize", handleResize); + socketRef.current = io(); const socket = socketRef.current; - // 接続成功したとき socket.on("connect", () => { - console.log("✅ Connected with ID:", socket.id); + console.log("Connected:", socket.id); setMyId(socket.id || null); + // 初期位置ランダム + /* + myPosRef.current = { + x: Math.random() * (MAP_SIZE - 200) + 100, + y: Math.random() * (MAP_SIZE - 200) + 100 + }; + */ }); - // --- サーバーからの命令を受け取る --- - - // 1. 今いる全員のリストを受け取る + // 1. 今いるプレイヤー情報 socket.on("current_players", (serverPlayers: Player[]) => { - const playersMap: Record = {}; - serverPlayers.forEach((p) => { - playersMap[p.id] = p; - }); - setPlayers(playersMap); + const pMap: Record = {}; + serverPlayers.forEach(p => pMap[p.id] = p); + setPlayers(pMap); }); // 2. 新しい人が来た - socket.on("new_player", (player: Player) => { - setPlayers((prev) => ({ ...prev, [player.id]: player })); + socket.on("new_player", (p: Player) => { + setPlayers(prev => ({ ...prev, [p.id]: p })); }); - // 3. 誰かが動いた - socket.on("update_player", (data: { id: string; x: number; y: number }) => { - setPlayers((prev) => { - const target = prev[data.id]; - if (!target) return prev; // 知らない人なら何もしない - // その人の位置だけ更新して新しいリストを作る - return { - ...prev, - [data.id]: { ...target, x: data.x, y: data.y }, + // 3. 誰かが動いた(★ここをサーバーに合わせて修正しました!) + socket.on("update_player", (d: { id: string; x: number; y: number }) => { + // ログを出して確認できるようにする + console.log("Receive:", d); + + setPlayers(prev => { + const target = prev[d.id]; + // 知らないIDなら無視(または追加処理を入れてもOK) + if (!target) return prev; + + return { + ...prev, + [d.id]: { ...target, x: d.x, y: d.y } }; }); }); // 4. 誰かがいなくなった - socket.on("remove_player", (id: string) => { - setPlayers((prev) => { - const newPlayers = { ...prev }; - delete newPlayers[id]; - return newPlayers; + socket.on("player_disconnected", (id: string) => { // ※ここもServer実装によっては要確認 + setPlayers(prev => { + const next = { ...prev }; + delete next[id]; + return next; }); }); - // 片付け(画面を閉じたときに通信を切る) return () => { socket.disconnect(); + window.removeEventListener("resize", handleResize); }; }, []); - // ジョイスティックを動かしたとき const handleMove = (event: any) => { - if (socketRef.current && typeof event.x === "number" && typeof event.y === "number") { - socketRef.current.emit("move", { x: event.x, y: event.y }); - } + if (!socketRef.current || !myId) return; + + const speed = 4.0; + let nextX = myPosRef.current.x + (event.x * speed); + let nextY = myPosRef.current.y - (event.y * speed); + + if (nextX < 20) nextX = 20; + if (nextX > MAP_SIZE - 20) nextX = MAP_SIZE - 20; + if (nextY < 20) nextY = 20; + if (nextY > MAP_SIZE - 20) nextY = MAP_SIZE - 20; + + myPosRef.current = { x: nextX, y: nextY }; + + // 自分の画面更新 + setPlayers(prev => { + if (!prev[myId]) return prev; + return { ...prev, [myId]: { ...prev[myId], x: nextX, y: nextY } }; + }); + + // サーバーへ送信(ここは 'move' で合っています) + socketRef.current.emit("move", { x: nextX, y: nextY }); }; - const handleStop = () => { - if (socketRef.current) { - socketRef.current.emit("move", { x: 0, y: 0 }); - } - }; + // カメラ計算 + let cameraX = 0; + let cameraY = 0; + + if (myId && players[myId]) { + const me = players[myId]; + cameraX = me.x - viewport.w / 2; + cameraY = me.y - viewport.h / 2; +/* + if (cameraX < 0) cameraX = 0; + if (cameraY < 0) cameraY = 0; + if (cameraX > MAP_SIZE - viewport.w) cameraX = MAP_SIZE - viewport.w; + if (cameraY > MAP_SIZE - viewport.h) cameraY = MAP_SIZE - viewport.h; +*/ + } return ( -
-

Multiplayer: {Object.keys(players).length} Online

- - {/* ゲーム画面エリア */} -
- {/* 全員分のボールを描画するループ処理 */} - {Object.values(players).map((player) => ( +
+ ● Online: {Object.keys(players).length} +
+
+ ID: {myId?.slice(0, 4)} +
+
+ + {/* ゲームワールド */} +
+ + {Object.values(players).map((p) => (
+ > +
+ {p.id.slice(0,4)} +
+
))}
- + {/* ジョイスティック */} +
+ +
); }