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 };
};