add match percentage in frontend
This commit is contained in:
parent
2ea26c9bd4
commit
7fb8601dd3
1
.env
1
.env
|
|
@ -1 +1,2 @@
|
||||||
# placing my env here
|
# placing my env here
|
||||||
|
DOMAIN=http://localhost:3000
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# placing my env here
|
||||||
|
DOMAIN=https://nextjs-test-rnd.vercel.app
|
||||||
|
|
@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
@ -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 });
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
export type LengthType = {
|
||||||
|
horizontal: number;
|
||||||
|
vertical: number;
|
||||||
|
diagonal: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ObjectType = {
|
||||||
|
value: number;
|
||||||
|
label: string;
|
||||||
|
length: LengthType;
|
||||||
|
};
|
||||||
|
|
@ -1,181 +1,30 @@
|
||||||
"use client";
|
import { Suspense } from "react";
|
||||||
import Image from "next/image";
|
import { frame, ohg, option } from "../constant";
|
||||||
import { useSearchParams } from "next/navigation";
|
import Unity from "./unity";
|
||||||
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",
|
|
||||||
});
|
|
||||||
|
|
||||||
const option = [
|
const getItem = async () => {
|
||||||
{
|
return await Promise.resolve({ option, ohg, frame });
|
||||||
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 ohgOption = [
|
|
||||||
{
|
|
||||||
value: 0,
|
|
||||||
label: "ohg1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: "ohg2",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: "ohg3",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const frameOption = [
|
|
||||||
{
|
|
||||||
value: 0,
|
|
||||||
label: "frame1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: "frame2",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: "frame3",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
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 {
|
const getInitMatch = async () => {
|
||||||
...prev,
|
return await fetch(`${process.env.DOMAIN}/fit?o=0&f=0`, {
|
||||||
frame: newIndex,
|
method: "GET",
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// 유니티 스크립트 호출 - objectName 및 함수 이름은 유니티의 것과 동일해야 함
|
};
|
||||||
UNSAFE__unityInstance?.SendMessage(objectName, "HandleSelect", newIndex);
|
|
||||||
},
|
|
||||||
[UNSAFE__unityInstance],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
export default async function UnityComponent() {
|
||||||
<>
|
const { option, ohg, frame } = await getItem();
|
||||||
<div className="mb-5 mt-5">
|
const res = await getInitMatch();
|
||||||
새 Unity 프로젝트를 생성하고, WebGL로 빌드 후 빌드 산출물을
|
const initMatch = (await res.json()) as { match: number };
|
||||||
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 (
|
return (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<UnityComponent />
|
<Unity
|
||||||
|
option={option}
|
||||||
|
ohg={ohg}
|
||||||
|
frame={frame}
|
||||||
|
initMatch={initMatch?.match ?? 0}
|
||||||
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,152 @@
|
||||||
"use client";
|
"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 = () => {
|
export default function UnityComponent({
|
||||||
return <></>;
|
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";
|
||||||
|
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
export default Unity;
|
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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: "standalone",
|
// output: "standalone",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue