diff --git a/src/components/Yatai/YataiStage.tsx b/src/components/Yatai/YataiStage.tsx index cd25219..d7d79c6 100644 --- a/src/components/Yatai/YataiStage.tsx +++ b/src/components/Yatai/YataiStage.tsx @@ -1,6 +1,7 @@ import { Physics } from "@react-three/cannon"; import { Canvas } from "@react-three/fiber"; import { memo } from "react"; +import { initialTargets } from "../../const/target"; import { CameraController } from "./CameraController"; import { YataiFoundation } from "./YataiFoundation"; import styles from "./YataiStage.module.css"; @@ -28,9 +29,12 @@ export const YataiStage = memo(() => { - - - + {initialTargets.map((target) => ( + + ))} diff --git a/src/components/Yatai/YataiTarget.tsx b/src/components/Yatai/YataiTarget.tsx index f44f56d..77a00f1 100644 --- a/src/components/Yatai/YataiTarget.tsx +++ b/src/components/Yatai/YataiTarget.tsx @@ -1,6 +1,6 @@ import { useBox } from "@react-three/cannon"; import type { ThreeElements } from "@react-three/fiber"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import type { BufferGeometry, Material, @@ -9,25 +9,56 @@ import type { Object3DEventMap, } from "three"; import { randFloat } from "three/src/math/MathUtils.js"; +import { type TargetProperty, initialTargets } from "../../const/target"; import { useSocketReceiver } from "../../hooks/useSocketReceiver"; import { useSocketSender } from "../../hooks/useSocketSender"; +import { useTargetStatusStore } from "../../store"; import { message_type } from "../../type/schema"; import { type ActionSchema, MessageType, type Target, } from "../../type/shooting"; +import { TargetStatus } from "../../type/target"; + +const getTargetProperty = (pos: [number, number, number]): TargetProperty => { + const target = initialTargets.find( + (target) => + target.pos.x === pos[0] && + target.pos.y === pos[1] && + target.pos.z === pos[2], + ); + return ( + target || { + index: -1, + pos: { x: 0, y: 0, z: 0 }, + size: { x: 0, y: 0, z: 0 }, + } + ); +}; export const YataiTarget = (props: ThreeElements["mesh"]) => { const { sendData } = useSocketSender(); const { onMessage } = useSocketReceiver(); + const position = props.position as [number, number, number]; + const property = getTargetProperty(position); + const { targetStatus, updateTargetStatus } = useTargetStatusStore( + (state) => ({ + targetStatus: state.targetStatus, + updateTargetStatus: state.updateTargetStatus, + }), + ); + + // + const size: [number, number, number] = useMemo(() => { + return [property.size.x, property.size.y, property.size.z]; + }, [property.size]); - const args: [number, number, number] = [0.7, 2, 0.7]; const [ref, api] = useBox(() => ({ mass: 1, position: position, - args: args, + args: size, })); useEffect(() => { @@ -49,18 +80,30 @@ export const YataiTarget = (props: ThreeElements["mesh"]) => { 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 + targetStatus[property.index] === TargetStatus.Live && + target.x * 8 > position[0] - size[0] / 2 && + target.x * 8 < position[0] + size[0] / 2 && + target.y * 8 > position[1] - size[1] / 2 - 2 && + target.y * 8 < position[1] + size[1] / 2 - 2 ) { api.applyImpulse( [randFloat(-2, 2), 4, 8], [randFloat(-1, 1), randFloat(-1, 1), randFloat(-1, 1)], ); + updateTargetStatus(property.index, TargetStatus.Hit); sendData(message_type.hit, uuid, { alpha: 0, beta: 0 }); } - }, [uuid, target, position, api, sendData]); + }, [ + target, + targetStatus, + property, + position, + size, + api, + sendData, + updateTargetStatus, + uuid, + ]); return ( { castShadow receiveShadow > - + ); diff --git a/src/const/target.ts b/src/const/target.ts new file mode 100644 index 0000000..893da0c --- /dev/null +++ b/src/const/target.ts @@ -0,0 +1,18 @@ +export type TargetProperty = { + index: number; + pos: ThreeDim; + size: ThreeDim; +}; + +export type ThreeDim = { + x: number; + y: number; + z: number; +}; + +export const initialTargets: TargetProperty[] = [ + // 本来posはy=1.8だが0.1だけ埋め込むことで反動を利用したいい感じのアニメーションを出している + { index: 0, pos: { x: -3, y: 1.7, z: 0 }, size: { x: 0.7, y: 1.8, z: 0.7 } }, + { index: 1, pos: { x: 0, y: 1.7, z: 0 }, size: { x: 0.7, y: 1.8, z: 0.7 } }, + { index: 2, pos: { x: 3, y: 1.7, z: 0 }, size: { x: 0.7, y: 1.8, z: 0.7 } }, +]; diff --git a/src/store/index.ts b/src/store/index.ts index 725c6ae..c28cd5d 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,3 +1,4 @@ export { useSocketRefStore } from "./useSocketRefStore"; export { useUUIDStore } from "./useUUIDStore"; export { useRoomIdStore } from "./useRoomIdStore"; +export { useTargetStatusStore } from "./useTargetStatusStore"; diff --git a/src/store/useTargetStatusStore.ts b/src/store/useTargetStatusStore.ts new file mode 100644 index 0000000..dcda4c1 --- /dev/null +++ b/src/store/useTargetStatusStore.ts @@ -0,0 +1,21 @@ +import { create } from "zustand"; +import { TargetStatus } from "../type/target"; + +type State = { + targetStatus: TargetStatus[]; +}; + +type Action = { + updateTargetStatus: (index: number, status: TargetStatus) => void; +}; + +export const useTargetStatusStore = create((set) => ({ + // left, center, right + targetStatus: [TargetStatus.Live, TargetStatus.Live, TargetStatus.Live], + updateTargetStatus: (index: number, status: TargetStatus) => + set((state) => { + const targetStatus = [...state.targetStatus]; + targetStatus[index] = status; + return { targetStatus }; + }), +})); diff --git a/src/type/target.ts b/src/type/target.ts new file mode 100644 index 0000000..994d42e --- /dev/null +++ b/src/type/target.ts @@ -0,0 +1,4 @@ +export enum TargetStatus { + Live = "live", + Hit = "hit", +}