diff --git a/apps/client/src/app.tsx b/apps/client/src/app.tsx index 3a5e6d4..8f7d4ac 100644 --- a/apps/client/src/app.tsx +++ b/apps/client/src/app.tsx @@ -4,7 +4,6 @@ import { socketClient } from "./network/SocketClient"; import { GameMap, MAP_SIZE } from "./entities/GameMap"; import { PlayerSprite } from "./entities/PlayerSprite"; -// ✅ 追加: 切り出したジョイスティックを読み込む import { VirtualJoystick, MAX_DIST } from "./input/VirtualJoystick"; type Player = { @@ -23,28 +22,55 @@ }); const myPosRef = useRef({ x: MAP_SIZE / 2, y: MAP_SIZE / 2 }); + + // 🌟 追加1: ジョイスティックの「現在の傾き」を常に記憶しておく変数 + const joystickInputRef = useRef({ x: 0, y: 0 }); - // 🕹️ ジョイスティックから受け取った移動量で計算するだけ + // 🌟 変更2: ジョイスティックが動いた時は「傾きを記憶するだけ」にする const handleJoystickMove = (moveX: number, moveY: number) => { - if (!myId) return; - const speed = 5.0; - let nextX = myPosRef.current.x + (moveX / MAX_DIST) * speed; - let nextY = myPosRef.current.y + (moveY / MAX_DIST) * speed; - - nextX = Math.max(20, Math.min(MAP_SIZE - 20, nextX)); - nextY = Math.max(20, Math.min(MAP_SIZE - 20, nextY)); - - myPosRef.current = { x: nextX, y: nextY }; - - // サーバーへ送信 - socketClient.sendMove(nextX, nextY); - - setPlayers((prev) => { - if (!prev[myId]) return prev; - return { ...prev, [myId]: { ...prev[myId], x: nextX, y: nextY } }; - }); + joystickInputRef.current = { x: moveX, y: moveY }; }; + // 🌟 追加3: 毎秒60回実行される「ゲームループ(エンジン)」 + useEffect(() => { + let animationFrameId: number; + + const tick = () => { + const { x: dx, y: dy } = joystickInputRef.current; + + // 入力(傾き)がある場合のみ移動処理を行う + if (myId && (dx !== 0 || dy !== 0)) { + const speed = 5.0; + let nextX = myPosRef.current.x + (dx / MAX_DIST) * speed; + let nextY = myPosRef.current.y + (dy / MAX_DIST) * speed; + + nextX = Math.max(20, Math.min(MAP_SIZE - 20, nextX)); + nextY = Math.max(20, Math.min(MAP_SIZE - 20, nextY)); + + myPosRef.current = { x: nextX, y: nextY }; + + // サーバーへ送信 + socketClient.sendMove(nextX, nextY); + + // ローカルの画面も更新 + setPlayers((prev) => { + if (!prev[myId]) return prev; + return { ...prev, [myId]: { ...prev[myId], x: nextX, y: nextY } }; + }); + } + + // 次のフレームでもう一度 tick を呼ぶ(これがループの正体) + animationFrameId = requestAnimationFrame(tick); + }; + + // ループ開始 + animationFrameId = requestAnimationFrame(tick); + + // コンポーネントが消える時にループを止める + return () => cancelAnimationFrame(animationFrameId); + }, [myId]); // 自分のIDが決まったらループ開始 + + // 👇 以下は通信処理と描画処理(変更なし) useEffect(() => { socketClient.onConnect((id) => setMyId(id)); @@ -117,7 +143,6 @@ - {/* ✅ 複雑だった div タグの塊がたったの1行に! */}
{ setIsMoving(false); setStickPos({ x: 0, y: 0 }); + onMove(0, 0); // 👈 追加: 指を離したら移動量 0,0 を伝える }; return (