diff --git a/src/components/Yatai/CameraController.tsx b/src/components/Yatai/CameraController.tsx new file mode 100644 index 0000000..6883b00 --- /dev/null +++ b/src/components/Yatai/CameraController.tsx @@ -0,0 +1,8 @@ +import { useThree } from "@react-three/fiber"; + +export const CameraController = () => { + const { camera } = useThree(); + camera.position.set(0, 3, 12); + camera.lookAt(0, 2, 0); + return null; +}; diff --git a/src/components/Yatai/TargetOverlay.module.css b/src/components/Yatai/TargetOverlay.module.css new file mode 100644 index 0000000..4388050 --- /dev/null +++ b/src/components/Yatai/TargetOverlay.module.css @@ -0,0 +1,7 @@ +.target { + position: absolute; + z-index: 1; + width: 100px; + height: 100px; +} + \ No newline at end of file diff --git a/src/components/Yatai/TargetOverlay.tsx b/src/components/Yatai/TargetOverlay.tsx new file mode 100644 index 0000000..d539fcb --- /dev/null +++ b/src/components/Yatai/TargetOverlay.tsx @@ -0,0 +1,57 @@ +import { useEffect, useState } from "react"; +import { useSocketReceiver } from "../../hooks/useSocketReceiver"; +import { + type ActionSchema, + MessageType, + type Target, +} from "../../type/shooting"; +import styles from "./TargetOverlay.module.css"; + +export const TargetOverlay = () => { + const { onMessage } = useSocketReceiver(); + + useEffect(() => { + onMessage((data) => { + // ここも本来はPointerSchemaになる + if (data.message_type === MessageType.Action) { + shotTarget(data); + } + }); + }, [onMessage]); + + // TODO: これらは一人用,いつかマルチプレイヤー対応する + const [aim, setAim] = useState(undefined); + // TODO: エイム照準の実装 + // const aimTarget = (data: PointerSchema) => { + // const x = window.innerWidth * data.target.x + window.innerWidth / 2; + // const y = window.innerHeight * data.target.y + window.innerHeight / 2; + // setAim({ x, y }); + // }; + + // const [target, setTarget] = useState(undefined); + const shotTarget = (data: ActionSchema) => { + const x = window.innerWidth / 2 + data.target.x * 1200; + const y = window.innerHeight / 2 + data.target.y * 1200; + // TODO: エイム実装ができたらここのsetAimは削除する + setAim({ x, y }); + // setTarget({ x, y }); + }; + + return ( +
+ 照準の表示 +
+ ); +}; diff --git a/src/components/Yatai/YataiFoundation.tsx b/src/components/Yatai/YataiFoundation.tsx new file mode 100644 index 0000000..d29c252 --- /dev/null +++ b/src/components/Yatai/YataiFoundation.tsx @@ -0,0 +1,37 @@ +import { useBox } from "@react-three/cannon"; +import type { ThreeElements } from "@react-three/fiber"; +import type { + BufferGeometry, + Material, + Mesh, + NormalBufferAttributes, + Object3DEventMap, +} from "three"; + +export const YataiFoundation = (props: ThreeElements["mesh"]) => { + const args: [number, number, number] = [10, 2, 2]; + const [ref] = useBox(() => ({ + mass: 0, + position: props.position as [number, number, number], + args: args, + })); + return ( + , + Material | Material[], + Object3DEventMap + > + > + } + {...props} + castShadow + receiveShadow + > + + + + ); +}; diff --git a/src/components/Yatai/YataiStage.module.css b/src/components/Yatai/YataiStage.module.css new file mode 100644 index 0000000..ec97b7d --- /dev/null +++ b/src/components/Yatai/YataiStage.module.css @@ -0,0 +1,6 @@ +.canvas { + width: 100%; + height: 100%; + background-color: transparent; +} + \ No newline at end of file diff --git a/src/components/Yatai/YataiStage.tsx b/src/components/Yatai/YataiStage.tsx new file mode 100644 index 0000000..cd25219 --- /dev/null +++ b/src/components/Yatai/YataiStage.tsx @@ -0,0 +1,38 @@ +import { Physics } from "@react-three/cannon"; +import { Canvas } from "@react-three/fiber"; +import { memo } from "react"; +import { CameraController } from "./CameraController"; +import { YataiFoundation } from "./YataiFoundation"; +import styles from "./YataiStage.module.css"; +import { YataiTarget } from "./YataiTarget"; + +export const YataiStage = memo(() => { + return ( +
+ + + {/* 全体ライト */} + + {/* スポットライト */} + + + + + + + + + +
+ ); +}); diff --git a/src/components/Yatai/YataiTarget.tsx b/src/components/Yatai/YataiTarget.tsx new file mode 100644 index 0000000..4bc60dc --- /dev/null +++ b/src/components/Yatai/YataiTarget.tsx @@ -0,0 +1,84 @@ +import { useBox } from "@react-three/cannon"; +import type { ThreeElements } from "@react-three/fiber"; +import { useEffect, useState } from "react"; +import type { + BufferGeometry, + Material, + Mesh, + NormalBufferAttributes, + Object3DEventMap, +} from "three"; +import { randFloat } from "three/src/math/MathUtils.js"; +import { useSocketReceiver } from "../../hooks/useSocketReceiver"; +import { useSocketSender } from "../../hooks/useSocketSender"; +import { message_type } from "../../type/schema"; +import { + type ActionSchema, + MessageType, + type Target, +} from "../../type/shooting"; + +export const YataiTarget = (props: ThreeElements["mesh"]) => { + const { sendData } = useSocketSender(); + const { onMessage } = useSocketReceiver(); + const position = props.position as [number, number, number]; + + const args: [number, number, number] = [0.7, 2, 0.7]; + const [ref, api] = useBox(() => ({ + mass: 1, + position: position, + args: args, + })); + + useEffect(() => { + onMessage((data) => { + if (data.message_type === MessageType.Action) { + shotTarget(data); + } + }); + }, [onMessage]); + + // TODO: これらは一人用,いつかマルチプレイヤー対応する + const [uuid, setUuid] = useState(""); + const [target, setTarget] = useState(undefined); + const shotTarget = (data: ActionSchema) => { + setUuid(data.id); + setTarget({ x: data.target.x, y: data.target.y }); + }; + + useEffect(() => { + if (!target) return; + if ( + target.x * 8 > position[0] - args[0] / 2 && + target.x * 8 < position[0] + args[0] / 2 && + target.y * 8 > position[1] - args[1] / 2 - 2 && + target.y * 8 < position[1] + args[1] / 2 - 2 + ) { + api.applyImpulse( + [randFloat(-2, 2), 4, 8], + [randFloat(-1, 1), randFloat(-1, 1), randFloat(-1, 1)], + ); + sendData(message_type.Hit, uuid, { alpha: 0, beta: 0 }); + } + }, [uuid, target, position, api, sendData]); + + return ( + , + Material | Material[], + Object3DEventMap + > + > + } + {...props} + castShadow + receiveShadow + > + + + + ); +}; diff --git a/src/pages/yatai/index.module.css b/src/pages/yatai/index.module.css index 86e4da0..58649d9 100644 --- a/src/pages/yatai/index.module.css +++ b/src/pages/yatai/index.module.css @@ -8,16 +8,3 @@ justify-content: center; align-items: center; } - -.canvas { - width: 100%; - height: 100%; - background-color: transparent; -} - -.target { - position: absolute; - z-index: 1; - width: 100px; - height: 100px; -} diff --git a/src/pages/yatai/index.tsx b/src/pages/yatai/index.tsx index 136ee50..596ebe5 100644 --- a/src/pages/yatai/index.tsx +++ b/src/pages/yatai/index.tsx @@ -1,207 +1,8 @@ -import { Physics, useBox } from "@react-three/cannon"; -import { Canvas, type ThreeElements, useThree } from "@react-three/fiber"; -import { memo, useEffect, useState } from "react"; -import type { - BufferGeometry, - Material, - Mesh, - NormalBufferAttributes, - Object3DEventMap, -} from "three"; -import { randFloat } from "three/src/math/MathUtils.js"; import Gallery from "../../components/Gallery"; -import { useSocketReceiver } from "../../hooks/useSocketReceiver"; -import { useSocketSender } from "../../hooks/useSocketSender"; -import { message_type } from "../../type/schema"; -import { - type ActionSchema, - MessageType, - type Target, -} from "../../type/shooting"; +import { TargetOverlay } from "../../components/Yatai/TargetOverlay"; +import { YataiStage } from "../../components/Yatai/YataiStage"; import styles from "./index.module.css"; -const YataiStage = memo(() => { - // 土台 - const Foundation = (props: ThreeElements["mesh"]) => { - const args: [number, number, number] = [10, 2, 2]; - const [ref] = useBox(() => ({ - mass: 0, - position: props.position as [number, number, number], - args: args, - })); - return ( - , - Material | Material[], - Object3DEventMap - > - > - } - {...props} - castShadow - receiveShadow - > - - - - ); - }; - - // 的 - const Target = (props: ThreeElements["mesh"]) => { - const { sendData } = useSocketSender(); - const { onMessage } = useSocketReceiver(); - const position = props.position as [number, number, number]; - - const args: [number, number, number] = [0.7, 2, 0.7]; - const [ref, api] = useBox(() => ({ - mass: 1, - position: position, - args: args, - })); - - useEffect(() => { - onMessage((data) => { - if (data.message_type === MessageType.Action) { - shotTarget(data); - } - }); - }, [onMessage]); - - // TODO: これらは一人用,いつかマルチプレイヤー対応する - const [uuid, setUuid] = useState(""); - const [target, setTarget] = useState(undefined); - const shotTarget = (data: ActionSchema) => { - setUuid(data.id); - setTarget({ x: data.target.x, y: data.target.y }); - }; - - useEffect(() => { - if (!target) return; - if ( - target.x * 8 > position[0] - args[0] / 2 && - target.x * 8 < position[0] + args[0] / 2 && - target.y * 8 > position[1] - args[1] / 2 - 2 && - target.y * 8 < position[1] + args[1] / 2 - 2 - ) { - api.applyImpulse( - [randFloat(-2, 2), 4, 8], - [randFloat(-1, 1), randFloat(-1, 1), randFloat(-1, 1)], - ); - sendData(message_type.Hit, uuid, { alpha: 0, beta: 0 }); - } - }, [uuid, target, position, api, sendData]); - - return ( - , - Material | Material[], - Object3DEventMap - > - > - } - {...props} - castShadow - receiveShadow - > - - - - ); - }; - - const CameraController = () => { - const { camera } = useThree(); - camera.position.set(0, 3, 12); - camera.lookAt(0, 2, 0); - return null; - }; - - return ( -
- - - {/* 全体ライト */} - - {/* スポットライト */} - - - - - - - - - -
- ); -}); - -const TargetOverlay = () => { - const { onMessage } = useSocketReceiver(); - - useEffect(() => { - onMessage((data) => { - // ここも本来はPointerSchemaになる - if (data.message_type === MessageType.Action) { - shotTarget(data); - } - }); - }, [onMessage]); - - // TODO: これらは一人用,いつかマルチプレイヤー対応する - const [aim, setAim] = useState(undefined); - // TODO: エイム照準の実装 - // const aimTarget = (data: PointerSchema) => { - // const x = window.innerWidth * data.target.x + window.innerWidth / 2; - // const y = window.innerHeight * data.target.y + window.innerHeight / 2; - // setAim({ x, y }); - // }; - - // const [target, setTarget] = useState(undefined); - const shotTarget = (data: ActionSchema) => { - const x = window.innerWidth / 2 + data.target.x * 1200; - const y = window.innerHeight / 2 + data.target.y * 1200; - // TODO: エイム実装ができたらここのsetAimは削除する - setAim({ x, y }); - // setTarget({ x, y }); - }; - - return ( -
- 照準の表示 -
- ); -}; - function Yatai() { return (