171 lines
5.3 KiB
TypeScript
171 lines
5.3 KiB
TypeScript
"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 => ({
|
|
loaderUrl: `${path}.loader.js`,
|
|
dataUrl: `${path}.data`,
|
|
frameworkUrl: `${path}.framework.js`,
|
|
codeUrl: `${path}.wasm`,
|
|
streamingAssetsUrl: "StreamingAssets",
|
|
companyName: "DTOL",
|
|
productName: "3d-test",
|
|
productVersion: "0.1",
|
|
});
|
|
|
|
export default function UnityComponent({
|
|
option = [],
|
|
ohg = [],
|
|
frame = [],
|
|
}: {
|
|
option: {
|
|
value: string;
|
|
name: string;
|
|
description: string;
|
|
}[];
|
|
ohg: ObjectType[];
|
|
frame: ObjectType[];
|
|
}) {
|
|
const params = useSearchParams();
|
|
const p = params.get("p");
|
|
const [selectedIndex, setSelectedIndex] = useState({ ohg: 0, frame: 0 });
|
|
type SelectedIndexType = typeof selectedIndex;
|
|
const [match, setMatch] = useState(100);
|
|
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 handleMatchUpdate = useCallback(
|
|
async (newSelectedIndex: SelectedIndexType) => {
|
|
const res = await fetch(
|
|
`/fit?o=${newSelectedIndex.ohg}&f=${newSelectedIndex.frame}`,
|
|
{
|
|
method: "GET",
|
|
},
|
|
);
|
|
console.log(res);
|
|
const json = (await res.json()) as { match: number };
|
|
if (isNaN(Number(json.match)) === false) setMatch(json.match);
|
|
return json.match;
|
|
},
|
|
[],
|
|
);
|
|
|
|
// 최초 1회 조회
|
|
useEffect(() => {
|
|
handleMatchUpdate({ ohg: 0, frame: 0 });
|
|
}, [handleMatchUpdate]);
|
|
|
|
// ohg나 frame이 바뀔 때마다 호출
|
|
const handleSelectIndex = useCallback(
|
|
// objectName: ohg | frame, newValue: "0", "1", ...
|
|
(
|
|
currentSelectedIndex: SelectedIndexType,
|
|
objectName: keyof SelectedIndexType,
|
|
newValue: string,
|
|
) => {
|
|
// 화면상의 select 컴포넌트 업데이트
|
|
const newIndex = Number(newValue);
|
|
const newSelectedIndex = {
|
|
...currentSelectedIndex,
|
|
[objectName]: newIndex,
|
|
};
|
|
setSelectedIndex(newSelectedIndex);
|
|
|
|
// 유니티 스크립트 호출 - objectName 및 함수 이름은 유니티의 것과 동일해야 함
|
|
UNSAFE__unityInstance?.SendMessage(objectName, "HandleSelect", newIndex);
|
|
|
|
handleMatchUpdate(newSelectedIndex)
|
|
// 매치율 이용하여 컴포넌트 업데이트
|
|
.then((match) => {
|
|
//
|
|
})
|
|
.catch((err) => {
|
|
// 에러처리
|
|
});
|
|
},
|
|
[UNSAFE__unityInstance, handleMatchUpdate],
|
|
);
|
|
|
|
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(selectedIndex, "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(selectedIndex, "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>
|
|
</>
|
|
);
|
|
}
|