add match percentage in frontend

This commit is contained in:
sjwie 2024-05-03 15:27:30 +09:00
parent 2ea26c9bd4
commit 7fb8601dd3
8 changed files with 298 additions and 178 deletions

3
.env
View File

@ -1 +1,2 @@
# placing my env here
# placing my env here
DOMAIN=http://localhost:3000

2
.env.production Normal file
View File

@ -0,0 +1,2 @@
# placing my env here
DOMAIN=https://nextjs-test-rnd.vercel.app

81
app/constant/index.ts Normal file
View File

@ -0,0 +1,81 @@
import { ObjectType } from "../types";
export const option = [
{
value: "/unity/3d-test/Build/3d-test",
name: "3d-test",
description:
"마우스 오른쪽 키다운으로 카메라 각도 조절, WASD로 이동, Shift로 가속하는 것까지 구현되었습니다.",
},
{
value: "/unity/kartrider/Build/kartrider",
name: "kartrider",
description: "튜토리얼을 진행하며 만든 카트라이더 게임입니다.",
},
{
value: "/unity/test-project/build",
name: "test-project",
description: "처음 만든 프로젝트입니다.",
},
];
// 가상의 가로, 세로, 대각선
export const ohg: ObjectType[] = [
{
value: 0,
label: "ohg1",
length: {
horizontal: 10,
vertical: 10,
diagonal: 14,
},
},
{
value: 1,
label: "ohg2",
length: {
horizontal: 12,
vertical: 16,
diagonal: 20,
},
},
{
value: 2,
label: "ohg3",
length: {
horizontal: 10,
vertical: 24,
diagonal: 26,
},
},
];
export const frame: ObjectType[] = [
{
value: 0,
label: "frame1",
length: {
horizontal: 10,
vertical: 12,
diagonal: 14,
},
},
{
value: 1,
label: "frame2",
length: {
horizontal: 12,
vertical: 14,
diagonal: 16,
},
},
{
value: 2,
label: "frame3",
length: {
horizontal: 13,
vertical: 15,
diagonal: 17,
},
},
];

31
app/fit/route.ts Normal file
View File

@ -0,0 +1,31 @@
import { NextRequest } from "next/server";
import { frame, ohg } from "../constant";
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const o = searchParams.get("o");
const f = searchParams.get("f");
const ohgObject = ohg.find((obj) => obj.value === Number(o));
const frameObject = frame.find((obj) => obj.value === Number(f));
let match = 0;
if (!ohgObject || !frameObject) return Response.json({ match });
// console.log(ohgObject);
// console.log(frameObject);
// 임시로 만든건데 rms 0~10을 매치 100~0으로 맞추자. (10이 넘어가면 매치는 0)
const rms =
(Object.keys(frameObject.length).reduce(
(prev, curr) =>
prev +
(ohgObject.length[curr as keyof typeof ohgObject.length] -
frameObject.length[curr as keyof typeof frameObject.length]) **
2,
0,
) /
3) **
0.5;
if (rms < 10) match = 100 - rms * 10;
return Response.json({ match });
}

11
app/types/index.ts Normal file
View File

@ -0,0 +1,11 @@
export type LengthType = {
horizontal: number;
vertical: number;
diagonal: number;
};
export type ObjectType = {
value: number;
label: string;
length: LengthType;
};

View File

@ -1,181 +1,30 @@
"use client";
import Image from "next/image";
import { useSearchParams } from "next/navigation";
import { Suspense, useCallback, useState } from "react";
import { Unity, UnityConfig, useUnityContext } from "react-unity-webgl";
const unityConfigBuilder: (path: string) => UnityConfig = (path) => ({
loaderUrl: `${path}.loader.js`,
dataUrl: `${path}.data`,
frameworkUrl: `${path}.framework.js`,
codeUrl: `${path}.wasm`,
streamingAssetsUrl: "StreamingAssets",
companyName: "DTOL",
productName: "3d-test",
productVersion: "0.1",
});
import { Suspense } from "react";
import { frame, ohg, option } from "../constant";
import Unity from "./unity";
const option = [
{
value: "/unity/3d-test/Build/3d-test",
name: "3d-test",
description:
"마우스 오른쪽 키다운으로 카메라 각도 조절, WASD로 이동, Shift로 가속하는 것까지 구현되었습니다.",
},
{
value: "/unity/kartrider/Build/kartrider",
name: "kartrider",
description: "튜토리얼을 진행하며 만든 카트라이더 게임입니다.",
},
{
value: "/unity/test-project/build",
name: "test-project",
description: "처음 만든 프로젝트입니다.",
},
];
const getItem = async () => {
return await Promise.resolve({ option, ohg, frame });
};
const ohgOption = [
{
value: 0,
label: "ohg1",
},
{
value: 1,
label: "ohg2",
},
{
value: 2,
label: "ohg3",
},
];
const getInitMatch = async () => {
return await fetch(`${process.env.DOMAIN}/fit?o=0&f=0`, {
method: "GET",
});
};
const frameOption = [
{
value: 0,
label: "frame1",
},
{
value: 1,
label: "frame2",
},
{
value: 2,
label: "frame3",
},
];
export default async function UnityComponent() {
const { option, ohg, frame } = await getItem();
const res = await getInitMatch();
const initMatch = (await res.json()) as { match: number };
function UnityComponent() {
const params = useSearchParams();
const p = params.get("p");
const [selectedIndex, setSelectedIndex] = useState({ ohg: 0, frame: 0 });
if (!p || isNaN(Number(p))) location.href = "/unity?p=0";
const {
unityProvider,
isLoaded,
loadingProgression,
initialisationError,
UNSAFE__unityInstance,
} = useUnityContext(unityConfigBuilder(option[Number(p)].value));
const loadingPercentage = Math.round(loadingProgression * 100);
const handleSelectIndex = useCallback(
// objectName: ohg | frame, newValue: "0", "1", ...
(objectName: keyof typeof selectedIndex, newValue: string) => {
const newIndex = Number(newValue);
// 화면상의 select 컴포넌트 업데이트
setSelectedIndex((prev) => {
if (objectName === "ohg") {
return {
...prev,
ohg: newIndex,
};
} else {
return {
...prev,
frame: newIndex,
};
}
});
// 유니티 스크립트 호출 - objectName 및 함수 이름은 유니티의 것과 동일해야 함
UNSAFE__unityInstance?.SendMessage(objectName, "HandleSelect", newIndex);
},
[UNSAFE__unityInstance],
);
return (
<>
<div className="mb-5 mt-5">
Unity , WebGL로
react-unity-webgl .
</div>
{option?.[Number(p)] && (
<div className="mb-5">{option[Number(p)].description}</div>
)}
<div className="flex h-[650px] w-[800px] flex-col">
{isLoaded === false && (
<div className="absolute z-10 flex h-[600px] w-[800px] items-center justify-center bg-gray-500">
<p>Loading... ({loadingPercentage}%)</p>
</div>
)}
<Unity className="h-[600px] w-[800px]" unityProvider={unityProvider} />
<div className="mt-auto flex">
<select
value={Number(p)}
onChange={(e) => (location.href = `/unity?p=${e.target.value}`)}
className="mt-5"
>
{option.map((o, i) => (
<option key={`${o}-${i}`} value={i}>
{o.name}
</option>
))}
</select>
{Number(p) == 0 && (
<>
<select
value={selectedIndex.ohg}
onChange={(e) => handleSelectIndex("ohg", e.target.value)}
className="ml-5 mt-5"
>
{ohgOption.map((o, i) => (
<option key={`${o.label}-${i}`} value={o.value}>
{o.label}
</option>
))}
</select>
<select
value={selectedIndex.frame}
onChange={(e) => handleSelectIndex("frame", e.target.value)}
className="ml-5 mt-5"
>
{frameOption.map((o, i) => (
<option key={`${o.label}-${i}`} value={o.value}>
{o.label}
</option>
))}
</select>
</>
)}
<Image
src={"/unity/3d-test/TemplateData/fullscreen-button.png"}
alt="전체 화면 버튼"
title="전체 화면으로 보기"
width={38}
height={38}
className="ml-auto mt-2 cursor-pointer"
onClick={() => UNSAFE__unityInstance?.SetFullscreen(1)}
/>
</div>
</div>
</>
);
}
export default function UnityWrapper() {
return (
<Suspense>
<UnityComponent />
<Unity
option={option}
ohg={ohg}
frame={frame}
initMatch={initMatch?.match ?? 0}
/>
</Suspense>
);
}

View File

@ -1,7 +1,152 @@
"use client";
import Image from "next/image";
import { useSearchParams } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { Unity, UnityConfig, useUnityContext } from "react-unity-webgl";
import { ObjectType } from "../types";
const unityConfigBuilder: (path: string) => UnityConfig = (path) => ({
loaderUrl: `${path}.loader.js`,
dataUrl: `${path}.data`,
frameworkUrl: `${path}.framework.js`,
codeUrl: `${path}.wasm`,
streamingAssetsUrl: "StreamingAssets",
companyName: "DTOL",
productName: "3d-test",
productVersion: "0.1",
});
const Unity = () => {
return <></>;
};
export default function UnityComponent({
initMatch,
option = [],
ohg = [],
frame = [],
}: {
option: {
value: string;
name: string;
description: string;
}[];
ohg: ObjectType[];
frame: ObjectType[];
initMatch: number;
}) {
const params = useSearchParams();
const p = params.get("p");
const [selectedIndex, setSelectedIndex] = useState({ ohg: 0, frame: 0 });
const [match, setMatch] = useState(initMatch);
if (!p || isNaN(Number(p))) location.href = "/unity?p=0";
export default Unity;
const { unityProvider, isLoaded, loadingProgression, UNSAFE__unityInstance } =
useUnityContext(unityConfigBuilder(option[Number(p)].value));
const loadingPercentage = Math.round(loadingProgression * 100);
const handleSelectIndex = useCallback(
// objectName: ohg | frame, newValue: "0", "1", ...
(objectName: keyof typeof selectedIndex, newValue: string) => {
const newIndex = Number(newValue);
// 화면상의 select 컴포넌트 업데이트
setSelectedIndex((prev) => {
if (objectName === "ohg") {
return {
...prev,
ohg: newIndex,
};
} else {
return {
...prev,
frame: newIndex,
};
}
});
// 유니티 스크립트 호출 - objectName 및 함수 이름은 유니티의 것과 동일해야 함
UNSAFE__unityInstance?.SendMessage(objectName, "HandleSelect", newIndex);
},
[UNSAFE__unityInstance],
);
useEffect(() => {
(async () => {
const res = await fetch(
`/fit?o=${selectedIndex.ohg}&f=${selectedIndex.frame}`,
{
method: "GET",
},
);
console.log(res);
const json = (await res.json()) as { match: number };
if (isNaN(Number(json.match)) === false) setMatch(json.match);
})();
}, [selectedIndex.frame, selectedIndex.ohg]);
return (
<>
<div className="mb-5 mt-5">
Unity , WebGL로
react-unity-webgl .
</div>
{option?.[Number(p)] && (
<div className="mb-5">{option[Number(p)].description}</div>
)}
<div className="flex h-[650px] w-[800px] flex-col">
{isLoaded === false && (
<div className="absolute z-10 flex h-[600px] w-[800px] items-center justify-center bg-gray-500">
<p>Loading... ({loadingPercentage}%)</p>
</div>
)}
<Unity className="h-[600px] w-[800px]" unityProvider={unityProvider} />
<div className="mt-auto flex">
<select
value={Number(p)}
onChange={(e) => (location.href = `/unity?p=${e.target.value}`)}
className="mt-5"
>
{option.map((o, i) => (
<option key={`${o}-${i}`} value={i}>
{o.name}
</option>
))}
</select>
{Number(p) == 0 && (
<>
<select
value={selectedIndex.ohg}
onChange={(e) => handleSelectIndex("ohg", e.target.value)}
className="ml-5 mt-5"
>
{ohg?.map((o, i) => (
<option key={`${o.label}-${i}`} value={o.value}>
{o.label}
</option>
))}
</select>
<select
value={selectedIndex.frame}
onChange={(e) => handleSelectIndex("frame", e.target.value)}
className="ml-5 mt-5"
>
{frame.map((o, i) => (
<option key={`${o.label}-${i}`} value={o.value}>
{o.label}
</option>
))}
</select>
<div className="ml-5 mt-5">
: {`${match?.toFixed?.(2) ?? 100}`}%
</div>
</>
)}
<Image
src={"/unity/3d-test/TemplateData/fullscreen-button.png"}
alt="전체 화면 버튼"
title="전체 화면으로 보기"
width={38}
height={38}
className="ml-auto mt-2 cursor-pointer"
onClick={() => UNSAFE__unityInstance?.SetFullscreen(1)}
/>
</div>
</div>
</>
);
}

View File

@ -1,6 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
// output: "standalone",
};
export default nextConfig;