{"id":13455468,"url":"https://github.com/PhilippMolitor/react-unity-renderer","last_synced_at":"2025-03-24T08:32:43.613Z","repository":{"id":42664390,"uuid":"342903594","full_name":"PhilippMolitor/react-unity-renderer","owner":"PhilippMolitor","description":"React Unity Renderer allows to interactively embed Unity WebGL builds into a React powered project.","archived":false,"fork":false,"pushed_at":"2023-03-05T21:39:54.000Z","size":1974,"stargazers_count":10,"open_issues_count":3,"forks_count":2,"subscribers_count":2,"default_branch":"dev","last_synced_at":"2024-09-18T05:08:20.497Z","etag":null,"topics":["react","react-unity","unity","unity-webgl","unity-webgl-template","unity3d","webgl","webgl2"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/react-unity-renderer","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PhilippMolitor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-27T16:30:46.000Z","updated_at":"2023-11-15T10:02:21.000Z","dependencies_parsed_at":"2023-02-08T02:18:05.033Z","dependency_job_id":null,"html_url":"https://github.com/PhilippMolitor/react-unity-renderer","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhilippMolitor%2Freact-unity-renderer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhilippMolitor%2Freact-unity-renderer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhilippMolitor%2Freact-unity-renderer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhilippMolitor%2Freact-unity-renderer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PhilippMolitor","download_url":"https://codeload.github.com/PhilippMolitor/react-unity-renderer/tar.gz/refs/heads/dev","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221834011,"owners_count":16888554,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["react","react-unity","unity","unity-webgl","unity-webgl-template","unity3d","webgl","webgl2"],"created_at":"2024-07-31T08:01:05.837Z","updated_at":"2024-10-28T22:31:10.874Z","avatar_url":"https://github.com/PhilippMolitor.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# React Unity Renderer\n\n\u003cp align=\"center\"\u003e\n\n\u003ca href=\"https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/ci-dev.yaml\"\u003e\n  \u003cimg src=\"https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/ci-dev.yaml/badge.svg?branch=dev\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/release-npmjs.yaml\"\u003e\n  \u003cimg src=\"https://github.com/PhilippMolitor/react-unity-renderer/actions/workflows/release-npmjs.yaml/badge.svg\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://codecov.io/gh/PhilippMolitor/react-unity-renderer\"\u003e\n  \u003cimg src=\"https://codecov.io/gh/PhilippMolitor/react-unity-renderer/branch/dev/graph/badge.svg?token=4D72B9VWYK\"/\u003e\n\u003c/a\u003e\n\n\u003cimg src=\"https://img.shields.io/npm/l/react-unity-renderer\"\u003e\n\u003cimg src=\"https://img.shields.io/github/stars/PhilippMolitor/react-unity-renderer\"\u003e\n\n\u003ca href=\"https://npmjs.com/package/react-unity-renderer\"\u003e\n  \u003cimg src=\"https://img.shields.io/npm/dw/react-unity-renderer\"\u003e\n  \u003cimg src=\"https://img.shields.io/npm/v/react-unity-renderer\"\u003e\n  \u003cimg src=\"https://img.shields.io/bundlephobia/minzip/react-unity-renderer\"\u003e\n\u003c/a\u003e\n\n\u003c/p\u003e\n\n\u003e This project is heavily inspired by [react-unity-webgl](https://github.com/elraccoone/react-unity-webgl) made by Jeffrey Lanters. This implementation uses function components + hooks, is getting tested continously and has strict linting and formatting rules which are always enforced.\n\n## Installation\n\nNPM\n\n```\nnpm install --save react-unity-renderer\n```\n\nYarn\n\n```\nyarn add react-unity-renderer\n```\n\n**Version compatability**\n\n| Unity version     | NPM version |\n| ----------------- | ----------- |\n| `2020`            | `2020.*`    |\n| `2021`            | `2020.*`    |\n\n## Example usage\n\nTypeScript\n\n```tsx\nimport { VFC, useState } from 'react';\nimport {\n  UnityContext,\n  UnityRenderer,\n  UnityLoaderConfig,\n} from 'react-unity-renderer';\n\n// get those URLs from your Unity WebGL build.\n// you *could* put a JSON in your WebGL template containing this information\n// and load that with fetch or axios to assemble your config.\nconst config: UnityLoaderConfig = {\n  loaderUrl: '...',\n  frameworkUrl: '...',\n  codeUrl: '...',\n  dataUrl: '...',\n  // everything from here on is optional\n  memoryUrl: '',\n  symbolsUrl: '',\n  streamingAssetsUrl: '',\n  companyName: '',\n  productName: '',\n  productVersion: '',\n};\n\nexport const UnityGameComponent: VFC = (): JSX.Element =\u003e {\n  // You need to construct a config or pass it from the props:\n  const [ctx] = useState\u003cUnityContext\u003e(new UnityContext(config));\n\n  // Keep track of the game progress and ready state like this:\n  const [progress, setProgress] = useState\u003cnumber\u003e(0);\n  const [ready, setReady] = useState\u003cboolean\u003e(false);\n\n  return (\n    \u003cUnityRenderer\n      context={ctx}\n      // optional state information callbacks\n      onUnityProgressChange={(p) =\u003e setProgress(p)}\n      onUnityReadyStateChange={(s) =\u003e setReady(s)}\n      onUnityError={(e) =\u003e console.error(e)}\n      // \u003cUnityRenderer\u003e has every prop (except ref) from HTMLCanvasElement.\n      // This means you can use something like style!\n      // Also it works perfectly with styled-components.\n      style={{ width: '100%', height: '100%' }} // optional, but a good idea.\n    /\u003e\n  );\n};\n```\n\n:warning: It is recommended to store the `UnityContext`, as well as the progress, ready and error states in a global state. This way you can keep track of the game state in every part of your application. Consider [zustand](https://github.com/pmndrs/zustand) as a lightweight alternative to Redux, MobX \u0026 co., as it has every feature needed for this use case and takes way less effort to implement.\n\n## Mitigating the \"keyboard capturing issue\"\n\nBy default, Unity WebGL builds capture the keyboard as soon as they are loaded. This means that all keyboard input on the website is captured by the game, and rendering all `\u003cinput\u003e`, `\u003ctextarea\u003e` and similar input methods useless.\n\nTo solve this problem, two changes have to be made:\n\n1. Inside your Unity project, add the following code at some point that gets called early in your game:\n\n```cs\n#if !UNITY_EDITOR \u0026\u0026 UNITY_WEBGL\nWebGLInput.captureAllKeyboardInput = false;\n#endif\n```\n\n2. Set the prop `tabIndex={1}` (may need an ESLint ignore rule) on the `\u003cUnityRenderer\u003e` component to enable focus on click.\n\n3. Now clicking the game enables game keyboard input, and clicking the website enables keyboard input on the website.\n\nFor more details on the issue, see [this Stack Overflow answer](https://stackoverflow.com/a/60854680).\n\n## Creating a fetchable config from a Unity WebGL template\n\nIn order to create a fetchable build config that contains all required keys for `UnityLoaderConfig`, you could add the following to a Unity WebGL template and upload it to a `CORS`-enabled web host (for example Amazon AWS S3).\n\n`build.json`\n\n```json\n{\n  \"loaderUrl\": \"Build/{{{ LOADER_FILENAME }}}\",\n  \"frameworkUrl\": \"Build/{{{ FRAMEWORK_FILENAME }}}\",\n  \"codeUrl\": \"Build/{{{ CODE_FILENAME }}}\",\n#if MEMORY_FILENAME\n  \"memoryUrl\": \"Build/{{{ MEMORY_FILENAME }}}\",\n#endif\n#if SYMBOLS_FILENAME\n  \"symbolsUrl\": \"Build/{{{ SYMBOLS_FILENAME }}}\",\n#endif\n  \"dataUrl\": \"Build/{{{ DATA_FILENAME }}}\",\n  \"streamingAssetsUrl\": \"StreamingAssets\",\n  \"companyName\": \"{{{ COMPANY_NAME }}}\",\n  \"productName\": \"{{{ PRODUCT_NAME }}}\",\n  \"productVersion\": \"{{{ PRODUCT_VERSION }}}\"\n}\n\n```\n\nTake the following example using `fetch`:\n\n`unity-api.ts`\n\n```ts\nimport { UnityLoaderConfig } from 'react-unity-renderer';\n\nexport async function fetchLoaderConfig(\n  baseUrl: string\n): Promise\u003cUnityLoaderConfig\u003e {\n  // set the URL of where we expect the loader config to be\n  const url = `${baseUrl}/build.json`;\n\n  let response: Response | undefined;\n\n  // network or request error\n  try {\n    response = await window.fetch(url, { method: 'GET' });\n  } catch (ex) {\n    throw new Error('unable to load build info');\n  }\n\n  // invalid response\n  if (!response || !response.ok) throw new Error('unable to load build info');\n\n  // force the type we expect\n  const data = (await response.json()) as UnityLoaderConfig;\n\n  return {\n    loaderUrl: `${baseUrl}/${data.loaderUrl}`,\n    frameworkUrl: `${baseUrl}/${data.frameworkUrl}`,\n    codeUrl: `${baseUrl}/${data.codeUrl}`,\n    dataUrl: `${baseUrl}/${data.dataUrl}`,\n    memoryUrl: `${baseUrl}/${data.memoryUrl}`,\n    symbolsUrl: `${baseUrl}/${data.symbolsUrl}`,\n    streamingAssetsUrl: `${baseUrl}/${data.streamingAssetsUrl}`,\n    companyName: `${data.companyName}`,\n    productName: `${data.productName}`,\n    productVersion: `${data.productVersion}`,\n  };\n}\n```\n\nYou can then use it to construct a `UnityContext` and pass this context to your `UnityRenderer` via the `context` prop.\n\n## Receiving events from Unity\n\n### On the Unity side\n\nIn order to send events from Unity to the React application, use the global method for that in your `*.jslib` mapping file:\n\n```javascript\nmergeInto(LibraryManager.library, {\n  RunSomeActionInJavaScript: function (message, counter) {\n    // surround with try/catch to make unity not crash in case the method is\n    // not defined in the global scope yet\n    try {\n      const messageString = Pointer_stringify(message);\n\n      // UnityBridge(event: string) returns a callback that calls\n      // every registered event handler with the provided arguments.\n      // It also handles unregistered events with a warning!\n      window.UnityBridge('event-name')(messageString, number);\n    } catch (e) {}\n  },\n});\n```\n\nIf the event name has no registered event handlers, the `UnityBridge(event: string)` function will log a warning via `console.warn(...)`.\n\n:warning: Please note that returning values from the `UnityBridge()` method is not supported, as it may call multiple event handlers internally from different `UnityContext`s that are listening for a certain event, e.g. when having two or more renderers in your application. The preferred way to handle this is to emit a message to the correct Unity instance, which this library also supports. This also helps making the communication paths simpler: **Events only go from Unity to JavaScript, Messages only go from JavaScript to Unity.**\n\n### On the React side\n\n```tsx\nimport { VFC, useState, useEffect } from 'react';\nimport { UnityContext, UnityRenderer } from 'react-unity-renderer';\n\nexport const UnityGameComponent: VFC = (): JSX.Element =\u003e {\n  const [ctx] = useState\u003cUnityContext\u003e(new UnityContext({ ... }));\n\n  // Register your handlers (make sure your context is valid!)\n  useEffect(() =\u003e {\n    // No context, no handlers!\n    if(!ctx) return;\n\n    ctx.on('message', (m: string) =\u003e console.log(message));\n    ctx.on('other-message', (n: number) =\u003e console.log(message));\n\n    // You can also unregister event handlers again!\n    ctx.off('other-message');\n  }, [ctx]);\n\n  return (\n    \u003cUnityRenderer context={ctx} /\u003e\n  );\n};\n\n```\n\n## Emitting messages to Unity\n\nWhile events are a way to handle actions that were initiated in the Unity game,\nmessages are a way to communicate the other way, from JavaScript to Unity.\n\nMessages are emitted from the `UnityContext`, the API for emitting then is the same as in the Unity WebGL documentation:\n\n```tsx\nimport { VFC, useState, useEffect } from 'react';\nimport { UnityContext, UnityRenderer } from 'react-unity-renderer';\n\nexport const UnityGameComponent: VFC = (): JSX.Element =\u003e {\n  const [ctx] = useState\u003cUnityContext\u003e(new UnityContext({ ... }));\n\n  const [ready, setReady] = useState\u003cboolean\u003e(false);\n\n  // Listen for the Unity instance to be ready\n  useEffect(() =\u003e {\n    if(ready === true) {\n      ctx.emit('GameObjectName', 'ScriptMethodName', 'StringOrNumberArgument');\n    }\n  }, [ready]);\n\n  return (\n    \u003cUnityRenderer\n      context={ctx}\n      onUnityReadyStateChange={(s) =\u003e setReady(s)}\n    /\u003e\n  );\n};\n```\n\n## Module augmentation\n\nTake the following example:\n\n```typescript\n// create some context\nconst ctx = new UnityContext({ ... });\n\n// handles some \"info\" event with one parameter of type string\nctx.on('info', (message: string) =\u003e {\n  console.log(message);\n});\n```\n\nThe parameter `message` has to be explicitly defined as `string` each time a handler of for the event name `info` would be registered.\nIn order to make use of TypeScript to its fullest extent, you can augment an Interface of the library to get autocompletion and type-safety features here.\n\nPut this either in a file importing `react-unity-renderer` or create a new `unity.d.ts` somewhere in your `src` or (if you have that) `typings` directory:\n\n```typescript\n// The \"{} from\" part just imports the TypeScript definitions, so\n// we do not re-define the whole module, but just augment it.\nimport {} from 'react-unity-renderer';\n\n// module augmentation\ndeclare module 'react-unity-renderer' {\n  // this is the interface providing autocompletion\n  interface EventSignatures {\n    // \"info\" is the event name\n    // The type on the right side is anything that would match TypeScript's\n    // Parameters\u003c\u003e helper type.\n    info: [message: string];\n\n    // Note that all parameter names are just labels, so they are fully optional.\n    // Though, they are displayed when autocompleting, so labels are quite helpful here.\n    'some-event': [number, string];\n\n    // If you want no parameters at all, just supply an empty tuple:\n    'parameterless-event': [];\n  }\n}\n```\n\nNow, any defined event will be auto-completed with its types for `UnityContext.on(...)`:\n\n```typescript\n// create some context\nconst ctx = new UnityContext({ ... });\n\n// \"info\" will be suggested by your IDE\n// \"message\" is now of type string\nctx.on('info', (message) =\u003e {\n  console.log(message);\n});\n```\n\n## API\n\n### `UnityRenderer`\n\n```tsx\n\u003cUnityRenderer\n  context={ ... }\n  onUnityProgressChange={ ... }\n  onUnityReadyStateChange={ ... }\n  onUnityError={ ... }\n  { ...HTMLAttributes }\n/\u003e\n```\n\n| | | |\n|---|---|---|\n| context                 | The context of the game build, which handles loading and event I/O.                                                                                                      | `UnityContext`                                  | `undefined` |\n| onUnityProgressChange   | Callback to execute when the loading progress of the game changes. Ranges from `0.0` to `1.0`.                                                                           | `(progress: number) =\u003e void`                    | `undefined` |\n| onUnityReadyStateChange | Callback to execute when the game build finished loading and begins to render.                                                                                           | `(ready: boolean) =\u003e void`                      | `undefined` |\n| onUnityError            | Callback which executes when an error occurs while loading the game. Currently Unity limits what errors can be cought, so some errors still appear via `window.alert()`. | `(error: Error) =\u003e void`                        | `undefined` |\n| `{...HTMLAttributes}`   | All default attributes of a `\u003ccanvas\u003e` element are supported to allow for an atomic component, supporting custom styling and libraries like `styled-components`.         | `Omit\u003cHTMLAttributes\u003cHtmlCanvasElement\u003e, 'ref' | 'id'` |             |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPhilippMolitor%2Freact-unity-renderer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FPhilippMolitor%2Freact-unity-renderer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPhilippMolitor%2Freact-unity-renderer/lists"}