diff --git a/apps/client/src/scenes/GameScene.tsx b/apps/client/src/scenes/GameScene.tsx index e6a3c6e..d425e65 100644 --- a/apps/client/src/scenes/GameScene.tsx +++ b/apps/client/src/scenes/GameScene.tsx @@ -24,6 +24,7 @@ const playersRef = useRef>({}); // 全プレイヤーのスプライト参照 const lastPositionSentTimeRef = useRef(0); // サーバーへの位置送信タイミングを制御 const targetPositionsRef = useRef>({}); // 他プレイヤーの目標位置(補完用) + const wasMovingRef = useRef(false); // 移動状態の変化を検知するためのフラグ useEffect(() => { if (!pixiContainerRef.current) return; @@ -110,32 +111,54 @@ // 自分の移動処理 const { x: dx, y: dy } = joystickInputRef.current; - if (dx !== 0 || dy !== 0) { + const isMoving = dx !== 0 || dy !== 0; + + if (isMoving) { me.move(dx / MAX_DIST, dy / MAX_DIST, ticker.deltaTime); - // 通信負荷軽減のため、一定間隔(20Hz等)でのみサーバーへ位置を送信 + // 通信負荷軽減のため、一定間隔でのみサーバーへ位置を送信 const now = performance.now(); if (now - lastPositionSentTimeRef.current >= GAME_CONFIG.PLAYER_POSITION_UPDATE_MS) { socketClient.sendMove(me.x, me.y); lastPositionSentTimeRef.current = now; } + } else if (wasMovingRef.current) { + // 💡 止まった瞬間の確定座標を1回だけ送信して他プレイヤーとのズレを防ぐ + socketClient.sendMove(me.x, me.y); } - // 他プレイヤーの線形補間(Lerp)処理:カクつきを抑えて滑らかに移動させる + // 今回の移動状態を次回ループのために保存 + wasMovingRef.current = isMoving; + + // 他プレイヤーの線形補間(Lerp)処理と吸着(Snap) Object.entries(playersRef.current).forEach(([id, player]) => { if (id === myId) return; const targetPos = targetPositionsRef.current[id]; if (targetPos) { - player.x += (targetPos.x - player.x) * GAME_CONFIG.PLAYER_LERP_SMOOTHNESS * ticker.deltaTime; - player.y += (targetPos.y - player.y) * GAME_CONFIG.PLAYER_LERP_SMOOTHNESS * ticker.deltaTime; + const diffX = targetPos.x - player.x; + const diffY = targetPos.y - player.y; + + // X軸の補完と吸着 + if (Math.abs(diffX) < GAME_CONFIG.PLAYER_LERP_SNAP_THRESHOLD) { + player.x = targetPos.x; + } else { + player.x += diffX * GAME_CONFIG.PLAYER_LERP_SMOOTHNESS * ticker.deltaTime; + } + + // Y軸の補完と吸着 + if (Math.abs(diffY) < GAME_CONFIG.PLAYER_LERP_SNAP_THRESHOLD) { + player.y = targetPos.y; + } else { + player.y += diffY * GAME_CONFIG.PLAYER_LERP_SMOOTHNESS * ticker.deltaTime; + } } }); // カメラ追従:自分を中心に世界を逆方向にずらす worldContainer.position.set( - -(me.x - window.innerWidth / 2), - -(me.y - window.innerHeight / 2) + -(me.x - app.screen.width / 2), + -(me.y - app.screen.height / 2) ); }); }; diff --git a/packages/shared/src/config/gameConfig.ts b/packages/shared/src/config/gameConfig.ts index a8c17aa..6ed6666 100644 --- a/packages/shared/src/config/gameConfig.ts +++ b/packages/shared/src/config/gameConfig.ts @@ -10,4 +10,9 @@ // プレイヤー設定 PLAYER_RADIUS: 10, // キャラの大きさ PLAYER_SPEED: 5, // 移動速度 (ピクセル/秒) + + // ネットワーク・描画設定 + PLAYER_POSITION_UPDATE_MS: 50, // 座標送信の間隔 (20Hz = 50ms) + PLAYER_LERP_SMOOTHNESS: 0.3, // 他プレイヤーの動きの滑らかさ (0.1〜0.5程度で調整) + PLAYER_LERP_SNAP_THRESHOLD: 0.5, // これ以下の距離になったら座標を強制的に合わせる } as const; \ No newline at end of file