diff --git a/apps/client/src/scenes/game/input/joystick/JoystickCoordinator.tsx b/apps/client/src/scenes/game/input/joystick/JoystickCoordinator.tsx index bf5f96f..253cc64 100644 --- a/apps/client/src/scenes/game/input/joystick/JoystickCoordinator.tsx +++ b/apps/client/src/scenes/game/input/joystick/JoystickCoordinator.tsx @@ -3,8 +3,10 @@ * JoystickInputLayer からの入力を受け取り,処理と描画をまとめて仲介する * 入力処理は useJoystick に委譲し,描画は JoystickView に委譲する */ +import { useCallback } from "react"; import type React from "react"; import { JoystickView } from "./JoystickView"; +import type { NormalizedInput } from "./joystick.types"; import { useJoystick } from "./useJoystick"; /** JoystickCoordinator が提供する描画用データと入力ハンドラ */ @@ -24,8 +26,36 @@ /** 受け取った入力から描画用の状態を生成し,描画を仲介する */ export const JoystickCoordinator = ({ onInput, maxDist, children }: Props) => { - const { isMoving, center, knobOffset, radius, handleStart, handleMove, handleEnd } = - useJoystick({ onInput, maxDist }); + const { + isMoving, + center, + knobOffset, + radius, + handleStart, + handleMove: baseHandleMove, + handleEnd: baseHandleEnd, + } = useJoystick({ maxDist }); + + const emitInput = useCallback( + (normalized: NormalizedInput) => { + onInput(normalized.x, normalized.y); + }, + [onInput] + ); + + const handleMove = useCallback( + (e: React.TouchEvent | React.MouseEvent) => { + const normalized = baseHandleMove(e); + if (!normalized) return; + emitInput(normalized); + }, + [baseHandleMove, emitInput] + ); + + const handleEnd = useCallback(() => { + baseHandleEnd(); + emitInput({ x: 0, y: 0 }); + }, [baseHandleEnd, emitInput]); const view = ( diff --git a/apps/client/src/scenes/game/input/joystick/joystick.types.ts b/apps/client/src/scenes/game/input/joystick/joystick.types.ts index 9c7ce8d..d55577e 100644 --- a/apps/client/src/scenes/game/input/joystick/joystick.types.ts +++ b/apps/client/src/scenes/game/input/joystick/joystick.types.ts @@ -6,3 +6,6 @@ /** 2D座標の簡易型 */ export type Point = { x: number; y: number }; + +/** 正規化された入力ベクトル */ +export type NormalizedInput = { x: number; y: number }; diff --git a/apps/client/src/scenes/game/input/joystick/useJoystick.ts b/apps/client/src/scenes/game/input/joystick/useJoystick.ts index fe503e6..c514fbc 100644 --- a/apps/client/src/scenes/game/input/joystick/useJoystick.ts +++ b/apps/client/src/scenes/game/input/joystick/useJoystick.ts @@ -6,11 +6,10 @@ import { useCallback, useState } from "react"; import type React from "react"; import { MAX_DIST } from "./joystick.constants"; -import type { Point } from "./joystick.types"; +import type { NormalizedInput, Point } from "./joystick.types"; -/** フックに渡す入力コールバックと設定 */ +/** フックに渡す設定 */ type Props = { - onInput: (moveX: number, moveY: number) => void; maxDist?: number; }; @@ -21,7 +20,7 @@ knobOffset: Point; radius: number; handleStart: (e: React.TouchEvent | React.MouseEvent) => void; - handleMove: (e: React.TouchEvent | React.MouseEvent) => void; + handleMove: (e: React.TouchEvent | React.MouseEvent) => NormalizedInput | null; handleEnd: () => void; }; @@ -37,7 +36,7 @@ }; /** 正規化ベクトルの出力とUI用の座標を提供するフック */ -export const useJoystick = ({ onInput, maxDist }: Props): UseJoystickReturn => { +export const useJoystick = ({ maxDist }: Props): UseJoystickReturn => { const [isMoving, setIsMoving] = useState(false); const [center, setCenter] = useState({ x: 0, y: 0 }); const [knobOffset, setKnobOffset] = useState({ x: 0, y: 0 }); @@ -53,12 +52,12 @@ setIsMoving(true); }, []); - // 入力座標からベクトルを計算し,半径でクランプして正規化出力する + // 入力座標からベクトルを計算し,半径でクランプして正規化する const handleMove = useCallback( (e: React.TouchEvent | React.MouseEvent) => { - if (!isMoving) return; + if (!isMoving) return null; const point = getClientPoint(e); - if (!point) return; + if (!point) return null; const dx = point.x - center.x; const dy = point.y - center.y; @@ -72,17 +71,16 @@ const normalizedY = offsetY / radius; setKnobOffset({ x: offsetX, y: offsetY }); - onInput(normalizedX, normalizedY); + return { x: normalizedX, y: normalizedY }; }, - [isMoving, center.x, center.y, radius, onInput] + [isMoving, center.x, center.y, radius] ); - // 入力終了時に状態をリセットして停止入力を通知する + // 入力終了時に状態をリセットする const handleEnd = useCallback(() => { setIsMoving(false); setKnobOffset({ x: 0, y: 0 }); - onInput(0, 0); - }, [onInput]); + }, []); return { isMoving, center, knobOffset, radius, handleStart, handleMove, handleEnd }; };