Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
@futureverse/react-unity-viewer
Advanced tools
Futureverse React Unity Viewer provides a modern solution for embedding Unity WebGL builds in your React Application while providing advanced APIs for two way communication and interaction between Unity and React.
When bringing your Unity Application to the web, you might need to communicate with Components on a webpage, build interactive interfaces or might want to implement functionality using Web APIs which Unity does not expose. Combining Unity with React is a great way to achieve these goals. Unity Viewer provides a modern solution for embedding Unity WebGL builds in your React Application while providing advanced APIs for two way communication and interaction between Unity and React.
Please use the MessageHandler
script available on Futureverse - Asset Viewer
package for Unity in order to receive messages from React.
Get started by installing Unity Viewer using the Node Package Manager or Yarn in your JavaScript or TypeScript React project.
npm install @futureverse/react-unity-viewer
# or
yarn install @futureverse/react-unity-viewer
# or
pnpm install @futureverse/react-unity-viewer
The following examples are also available on /examples
folder.
First, run the development server:
npm run dev
# or
yarn dev
# or
pnpm dev
Open http://localhost:3000 with your browser to see the result.
Import the Unity Viewer Styles to your project using the command below on your css file.
@import url("./node_modules/@futureverse/react-unity-viewer/dist/style.css");
In order to use the unity viewer you will need the context <UnityViewerContextProvider>
and the viewer <UnityViewer>
, viewer requires the context to be able to control the state of the application such as loading, open and close states and also to receive react messages.
const {
openUnity,
closeUnity,
isLoaded,
isReady,
loadingProgression,
dispatchPlayerData,
} = useUnityViewerContext();
import {
UnityViewerContextProvider,
UnityViewer,
} from "@futureverse/react-unity-viewer";
<UnityViewerContextProvider>
<UnityViewer unityHostURL="UNITY_HOST_URL" unityFileName="UNITY_FILE_NAME" />
</UnityViewerContextProvider>;
by default the application won't load the unity player straight away, to load the unity player you need to use the openUnity
hook.
import {
UnityViewerContextProvider,
UnityViewer,
UnityViewerControls,
UnityViewerPreloader,
useUnityViewerContext,
} from "@futureverse/react-unity-viewer";
const CustomViewerStarter = () => {
const { openUnity } = useUnityViewerContext();
const handleLoadUnity = () => {
openUnity({
initialMessageFunctionName: "INITIAL_FUNCTION_NAME_CALLED_ON_UNITY",
initialMessageData: "INITIAL_FUNCTION_DATA_CALLED_ON_UNITY",
});
};
return (
<div className="absolute inset-0 flex items-center justify-center">
<button
onClick={handleLoadUnity}
className="bg-black text-white rounded-md px-3 py-1.5 border border-white"
>
Click here to load Unity Application
</button>
</div>
);
};
const UnityViewerWithCustomUnityStarter = () => {
return (
<main className="absolute inset-0 bg-gray-50">
<UnityViewerContextProvider>
<CustomViewerStarter />
<UnityViewer
unityHostURL="https://fv-goblins.s3.us-west-2.amazonaws.com/gag-viewer-dev"
unityFileName="gag"
/>
<UnityViewerControls />
<UnityViewerPreloader />
</UnityViewerContextProvider>
</main>
);
};
import {
UnityViewerContextProvider,
UnityViewerStarter,
UnityViewer,
UnityViewerControls,
UnityViewerPreloader,
} from "@futureverse/react-unity-viewer";
<UnityViewerContextProvider>
<UnityViewerStarter
initialMessageFunctionName="INITIAL_FUNCTION_NAME_CALLED_ON_UNITY"
initialMessageData="INITIAL_FUNCTION_DATA_CALLED_ON_UNITY"
/>
<UnityViewer unityHostURL="UNITY_HOST_URL" unityFileName="UNITY_FILE_NAME" />
<UnityViewerControls />
<UnityViewerPreloader />
</UnityViewerContextProvider>;
import {
UnityViewerContextProvider,
UnityViewerStarter,
UnityViewer,
UnityViewerControls,
UnityViewerPreloader,
} from "@futureverse/react-unity-viewer";
<UnityViewerContextProvider>
<UnityViewerStarter
initialMessageFunctionName="update_model"
initialMessageData={{
URL: "https://fv-goblins.s3.us-west-2.amazonaws.com/goblins/000-34_050-18__010-5078_1V0-4922__0C1-0/glb/000-34_050-18__010-5078_1V0-4922__0C1-0.glb",
}}
/>
<UnityViewer
unityHostURL="https://fv-goblins.s3.us-west-2.amazonaws.com/gag-viewer-dev"
unityFileName="gag"
/>
<UnityViewerControls />
<UnityViewerPreloader />
</UnityViewerContextProvider>;
import { IconFutureVerse } from "@/icons/IconFutureVerse";
import { IconGodsAndGoblins } from "@/icons/IconGodsAndGoblins";
import {
UnityViewerContextProvider,
UnityViewerStarter,
UnityViewer,
UnityViewerControls,
useUnityViewerContext,
} from "@futureverse/react-unity-viewer";
const CustomViewerPreloader = () => {
const { isUnityOpened, isLoaded, loadingProgression } =
useUnityViewerContext();
return (
<>
{isUnityOpened && (
<>
{!isLoaded && (
<div className="absolute inset-0 flex items-center justify-center bg-black text-xs text-white">
<div className="flex w-full max-w-[500px] flex-col gap-10 px-10">
<div className="flex justify-center">
<IconGodsAndGoblins />
</div>
<div className="w-full overflow-hidden">
<div className="relative w-full overflow-hidden">
<div className="relative h-[3px] w-full bg-white opacity-10"></div>
<div
className="absolute left-0 top-0 w-full h-full transform bg-white"
style={{
width: `${loadingProgression * 100}%`,
}}
></div>
</div>
</div>
<div className="flex justify-center">
<a
href="https://www.futureverse.com/"
target="_blank"
className="flex items-center gap-1.5"
rel="noreferrer"
>
Powered by
<IconFutureVerse />
</a>
</div>
</div>
</div>
)}
</>
)}
</>
);
};
const UnityViewerWithCustomPreloader = () => {
return (
<main className="absolute inset-0 bg-gray-50">
<UnityViewerContextProvider>
<UnityViewerStarter
initialMessageFunctionName="update_model"
initialMessageData={{
URL: "https://fv-goblins.s3.us-west-2.amazonaws.com/goblins/000-34_050-18__010-5078_1V0-4922__0C1-0/glb/000-34_050-18__010-5078_1V0-4922__0C1-0.glb",
}}
/>
<UnityViewer
unityHostURL="https://fv-goblins.s3.us-west-2.amazonaws.com/gag-viewer-dev"
unityFileName="gag"
/>
<UnityViewerControls />
<CustomViewerPreloader />
</UnityViewerContextProvider>
</main>
);
};
import {
UnityViewerContextProvider,
UnityViewerStarter,
UnityViewer,
UnityViewerPreloader,
useUnityViewerContext,
} from "@futureverse/react-unity-viewer";
const CustomViewerControls = () => {
const { isReady, closeUnity, dispatchPlayerData } = useUnityViewerContext();
const handleFullBody = () => {
dispatchPlayerData("camera_composition_default", {});
};
const handleUpperBody = () => {
dispatchPlayerData("camera_composition_key", {
CompositionKey: "head",
});
};
return (
<>
{isReady && (
<div className="absolute left-0 right-0 bottom-0 p-2">
<div className="flex items-center justify-center gap-2 text-sm">
<button
className="bg-black text-white rounded-md px-3 py-1.5 border border-white"
onClick={closeUnity}
>
Exit
</button>
<button
className="bg-black text-white rounded-md px-3 py-1.5 border border-white"
onClick={handleFullBody}
>
Full body
</button>
<button
className="bg-black text-white rounded-md px-3 py-1.5 border border-white"
onClick={handleUpperBody}
>
Upper body
</button>
</div>
</div>
)}
</>
);
};
const UnityViewerWithCustomControls = () => {
return (
<main className="absolute inset-0 bg-gray-50">
<UnityViewerContextProvider>
<UnityViewerStarter
initialMessageFunctionName="update_model"
initialMessageData={{
URL: "https://fv-goblins.s3.us-west-2.amazonaws.com/goblins/000-34_050-18__010-5078_1V0-4922__0C1-0/glb/000-34_050-18__010-5078_1V0-4922__0C1-0.glb",
}}
/>
<UnityViewer
unityHostURL="https://fv-goblins.s3.us-west-2.amazonaws.com/gag-viewer-dev"
unityFileName="gag"
/>
<CustomViewerControls />
<UnityViewerPreloader />
</UnityViewerContextProvider>
</main>
);
};
import { useEffect } from "react";
import {
UnityViewerContextProvider,
UnityViewerStarter,
UnityViewer,
UnityViewerPreloader,
useUnityViewerContext,
} from "@futureverse/react-unity-viewer";
const CustomMousePosition = () => {
const { dispatchPlayerData } = useUnityViewerContext();
useEffect(() => {
const onMouseMove = (event: MouseEvent) => {
const target = event.target as HTMLElement;
const localX = event.clientX - target.offsetLeft;
const localY = event.clientY - target.offsetTop;
const localMousePosition = { x: localX, y: localY };
const globalMousePosition = { x: event.clientX, y: event.clientY };
dispatchPlayerData(`mouse_move_position`, {
globalMousePosition,
localMousePosition,
});
};
window.addEventListener("mousemove", onMouseMove);
return () => {
window.removeEventListener("mousemove", onMouseMove);
};
}, [dispatchPlayerData]);
return <></>;
};
const CustomPanel = () => {
const { isReady } = useUnityViewerContext();
return <>{isReady && <CustomMousePosition />}</>;
};
const UnityViewerWithCustomControls = () => {
return (
<main className="absolute inset-0 bg-gray-50">
<UnityViewerContextProvider>
<UnityViewerStarter
initialMessageFunctionName="update_model"
initialMessageData={{
URL: "https://fv-goblins.s3.us-west-2.amazonaws.com/goblins/000-34_050-18__010-5078_1V0-4922__0C1-0/glb/000-34_050-18__010-5078_1V0-4922__0C1-0.glb",
}}
/>
<UnityViewer
unityHostURL="https://fv-goblins.s3.us-west-2.amazonaws.com/gag-viewer-dev"
unityFileName="gag"
/>
<CustomPanel />
<UnityViewerPreloader />
</UnityViewerContextProvider>
</main>
);
};
import { useEffect, useRef } from "react";
import {
UnityViewerContextProvider,
UnityViewerStarter,
UnityViewer,
UnityViewerPreloader,
useUnityViewerContext,
} from "@futureverse/react-unity-viewer";
const CustomContainerPosition = ({
children,
}: {
children: React.ReactNode | React.ReactNode[],
}) => {
const containerRef = useRef < HTMLDivElement > null;
const { dispatchPlayerData } = useUnityViewerContext();
useEffect(() => {
let animationRequest: number;
const getContainerRect = () => {
return containerRef.current?.getBoundingClientRect();
};
const onRender = () => {
const containerRect = getContainerRect();
if (containerRect) {
dispatchPlayerData(`div_position`, {
x: Math.round(containerRect.x) * window.devicePixelRatio,
y: Math.round(containerRect.y) * window.devicePixelRatio,
width: Math.round(containerRect.width) * window.devicePixelRatio,
height: Math.round(containerRect.height) * window.devicePixelRatio,
});
}
animationRequest = requestAnimationFrame(onRender);
};
onRender();
window.addEventListener("resize", onRender);
window.addEventListener("scroll", onRender);
return () => {
window.removeEventListener("resize", onRender);
window.removeEventListener("scroll", onRender);
cancelAnimationFrame(animationRequest);
};
}, [dispatchPlayerData]);
return (
<>
<div
ref={containerRef}
style={{
position: "relative",
width: "100%",
}}
>
<div>{children}</div>
</div>
</>
);
};
const CustomPanel = () => {
const { isReady } = useUnityViewerContext();
return (
<>
{isReady && (
<div className="absolute inset-0 overflow-auto h-[2000px] gap-6 flex flex-col justify-between">
<div className="bg-red-500 opacity-20 h-[200px]"></div>
<div className="bg-red-500 opacity-20 h-[200px]"></div>
<div className="bg-red-500 opacity-20 h-[200px]"></div>
<div className="bg-red-500 opacity-20 h-[200px]"></div>
<div className="bg-red-500 opacity-20 h-[200px]"></div>
<CustomContainerPosition>
<div className="bg-red-500 opacity-20 h-[200px]"></div>
</CustomContainerPosition>
</div>
)}
</>
);
};
const UnityViewerWithCustomControls = () => {
return (
<main className="absolute inset-0 bg-gray-50">
<UnityViewerContextProvider>
<UnityViewerStarter
initialMessageFunctionName="update_model"
initialMessageData={{
URL: "https://fv-goblins.s3.us-west-2.amazonaws.com/goblins/000-34_050-18__010-5078_1V0-4922__0C1-0/glb/000-34_050-18__010-5078_1V0-4922__0C1-0.glb",
}}
/>
<UnityViewer
unityHostURL="https://fv-goblins.s3.us-west-2.amazonaws.com/gag-viewer-dev"
unityFileName="gag"
/>
<UnityViewerPreloader />
<CustomPanel />
</UnityViewerContextProvider>
</main>
);
};
The following tasks are available for npm run
or yarn
:
dev
: Run Vite in host mode for a local development environment (not included in production build)watch
: Run Vite in watch mode to detect changes to files during developmentbuild
: Run Vite to build a production release distributableVite features a host mode to enables development with real time HMR updates directly from the library via the start
script.
To test your library from within an app:
npm link
or yarn link
command to register the packagenpm link "@futureverse/react-unity-viewer"
or yarn link "@futureverse/react-unity-viewer"
command to use the library inside your app during developmentOnce development completes, unlink
both your library and test app projects.
npm link "@futureverse/react-unity-viewer"
or yarn link "@futureverse/react-unity-viewer"
command to use the library inside your app during developmentnpm unlink
or yarn unlink
command to register the packageUpdate your package.json
to next version number, and remember to tag a release.
Once ready to submit your package to the NPM Registry, execute the following task via npm
(or yarn
):
npm run build
— Build the packageAssure the proper npm login:
npm login
Submit your package to the registry:
npm publish --access public
https://www.npmjs.com/package/@futureverse/react-unity-viewer
FAQs
Futureverse React Unity Viewer provides a modern solution for embedding Unity WebGL builds in your React Application while providing advanced APIs for two way communication and interaction between Unity and React.
The npm package @futureverse/react-unity-viewer receives a total of 0 weekly downloads. As such, @futureverse/react-unity-viewer popularity was classified as not popular.
We found that @futureverse/react-unity-viewer demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.