New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@futureverse/react-unity-viewer

Package Overview
Dependencies
Maintainers
5
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@futureverse/react-unity-viewer

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.

  • 0.1.2
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
0
decreased by-100%
Maintainers
5
Weekly downloads
 
Created
Source

@futureverse/react-unity-viewer

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.

Unity Build

Please use the MessageHandler script available on Futureverse - Asset Viewer package for Unity in order to receive messages from React.

Installation

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

Examples

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.

Basic usage:

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.

Common context hooks:

const {
  openUnity,
  closeUnity,
  isLoaded,
  isReady,
  loadingProgression,
  dispatchPlayerData,
} = useUnityViewerContext();

Basic usage example:

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.

Basic usage example for opening the unity application

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>
  );
};

Basic usage example with default preloader and controls:

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>;

Basic usage example for Gods and Goblins project with default preloader and controls:

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>;

Custom preloader example:

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>
  );
};

Custom controls example:

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>
  );
};

Sending mouse position to unity example:

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>
  );
};

Sending container (div) position to unity example:

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>
  );
};

Updating NPM Package

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 development
  • build: Run Vite to build a production release distributable

Development

Vite 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:

  • From your library: run npm link or yarn link command to register the package
  • From your app: run npm link "@futureverse/react-unity-viewer" or yarn link "@futureverse/react-unity-viewer" command to use the library inside your app during development

Development Cleanup

Once development completes, unlink both your library and test app projects.

  • From your app: run npm link "@futureverse/react-unity-viewer" or yarn link "@futureverse/react-unity-viewer" command to use the library inside your app during development
  • From your library: run npm unlink or yarn unlink command to register the package

Release Publishing

Update 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 package

Assure 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

Keywords

FAQs

Package last updated on 29 May 2023

Did you know?

Socket

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.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc