Skip to content
This repository has been archived by the owner on Nov 26, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into feat/target-overlay-size
Browse files Browse the repository at this point in the history
  • Loading branch information
Sea10wood authored Aug 17, 2024
2 parents 008fd1e + 696ed2d commit 3207409
Show file tree
Hide file tree
Showing 17 changed files with 430 additions and 252 deletions.
2 changes: 1 addition & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"ignorePaths": ["node_modules/**", "*.svg"],
"version": "0.2",
"language": "en",
"words": ["NATSUMATSURI", "yatai", "Dela", "Yuji", "Syuku", "zustand", "wadaiko", "qrcode"]
"words": ["NATSUMATSURI", "yatai", "Dela", "Yuji", "Syuku", "zustand", "wadaiko", "qrcode","nobori","natsu","matsuri"]
}
61 changes: 61 additions & 0 deletions src/components/Gallery/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
h1{
font-size: 40px;
}

.title{
position: absolute;
top: 0;
left: 0;
z-index: 1;
padding: 16px;
margin: 0;
}
.nobori img{
position: absolute;
bottom: 0;
left: -240px;
height: 80vh;
aspect-ratio: 1 / 1;
z-index: -1;
}

.light img{
position: absolute;
top: 0;
left: 0px;
height: 80vh;
aspect-ratio: 1 / 1;
z-index: -1;
}

.container {
display: flex;
align-items: flex-end;
position: absolute;
bottom: 16px;
left: 4vw;
z-index: 1;
}

.logo img {
height: 16vh;
aspect-ratio: 1 / 1;
margin-right: 2vw;
}

.shop {
color: var(--white);
font-size: 4vw;
padding: 16px;
margin: 0;
}

.light-right img{
position: absolute;
top: 0;
right: 0px;
height: 80vh;
aspect-ratio: 1 / 1;
z-index: -1;
transform: scale(-1, 1);
}
32 changes: 32 additions & 0 deletions src/components/Gallery/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import styles from "./index.module.css";

function Gallery() {
return (
<div>
{/* biome-ignore lint/a11y/useMediaCaption: 夏祭りの音を再生します。 */}
<audio
src="/sound/natsu_matsuri.mp3"
autoPlay
loop
aria-label="夏祭りの音"
/>
<h1 className={styles.title}>VIRTUAL_NATSUMATSURI</h1>
<div className={styles.nobori}>
<img src="/2D_material/nobori.webp" alt="夏祭り_のぼり" />
</div>
<div className={styles.light}>
<img src="/2D_material/red_small_lite.webp" alt="提灯" />
</div>
<div className={styles.container}>
<div className={styles.logo}>
<img src="/logo.webp" alt="logo" />
</div>
<p className={styles.shop}>VIRTUAL_NATSUMATSURI</p>
</div>
<div className={styles["light-right"]}>
<img src="/2D_material/red_small_lite.webp" alt="提灯" />
</div>
</div>
);
}
export default Gallery;
8 changes: 8 additions & 0 deletions src/components/Yatai/CameraController.tsx
Original file line number Diff line number Diff line change
@@ -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;
};
12 changes: 12 additions & 0 deletions src/components/Yatai/TargetOverlay.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.target {
position: absolute;
z-index: 1;
width: 100px;
height: 100px;
transform: translate(calc(-50%+50px), calc(-50%+50px));
}

.image {
width: 100%;
height: 100%;
}
55 changes: 55 additions & 0 deletions src/components/Yatai/TargetOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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<Target | undefined>(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<Target | undefined>(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 (
<div
className={styles.target}
style={{
left: `${aim?.x}px`,
top: `${aim?.y}px`,
}}
>
<img
src="/2D_material/target.webp"
alt="照準の表示"
className={styles.image}
/>
</div>
);
};
37 changes: 37 additions & 0 deletions src/components/Yatai/YataiFoundation.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<mesh
ref={
ref as React.Ref<
Mesh<
BufferGeometry<NormalBufferAttributes>,
Material | Material[],
Object3DEventMap
>
>
}
{...props}
castShadow
receiveShadow
>
<boxGeometry args={[...args]} />
<meshStandardMaterial color={"red"} />
</mesh>
);
};
6 changes: 6 additions & 0 deletions src/components/Yatai/YataiStage.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.canvas {
width: 100%;
height: 100%;
background-color: transparent;
}

38 changes: 38 additions & 0 deletions src/components/Yatai/YataiStage.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={styles.canvas}>
<Canvas shadows camera={{ fov: 25 }}>
<Physics>
{/* 全体ライト */}
<ambientLight intensity={Math.PI / 16} />
{/* スポットライト */}
<spotLight
castShadow
position={[4, 12, 8]}
angle={0.5}
penumbra={0}
decay={0}
intensity={Math.PI}
shadow-mapSize-width={1024}
shadow-mapSize-height={1024}
shadow-bias={-0.0001}
/>
<CameraController />
<YataiFoundation position={[0, 2, -2]} />
<YataiFoundation position={[0, 0, 0]} />
<YataiTarget position={[-3, 1.8, 0]} />
<YataiTarget position={[0, 1.8, 0]} />
<YataiTarget position={[3, 1.8, 0]} />
</Physics>
</Canvas>
</div>
);
});
84 changes: 84 additions & 0 deletions src/components/Yatai/YataiTarget.tsx
Original file line number Diff line number Diff line change
@@ -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<string>("");
const [target, setTarget] = useState<Target | undefined>(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 (
<mesh
ref={
ref as React.Ref<
Mesh<
BufferGeometry<NormalBufferAttributes>,
Material | Material[],
Object3DEventMap
>
>
}
{...props}
castShadow
receiveShadow
>
<boxGeometry args={[...args]} />
<meshStandardMaterial color={"yellow"} />
</mesh>
);
};
22 changes: 22 additions & 0 deletions src/hooks/useSocketReceiver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useSocketRefStore } from "../store";
import type { ActionSchema, HitSchema, PointerSchema } from "../type/shooting";

export const useSocketReceiver = () => {
const socketRef = useSocketRefStore((state) => state.socketRef);

const onMessage = (
handler: (data: PointerSchema | ActionSchema | HitSchema) => void,
) => {
const onMessage = (event: MessageEvent) => {
const data = JSON.parse(event.data);
handler(data);
};
const currentSocketRef = socketRef?.current;
currentSocketRef?.addEventListener("message", onMessage);
return () => {
currentSocketRef?.removeEventListener("message", onMessage);
};
};

return { onMessage };
};
Loading

0 comments on commit 3207409

Please sign in to comment.