UNPKG

react-unity-webgl

Version:

React Unity WebGL 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.

236 lines (235 loc) 14.8 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Unity = void 0; var react_1 = require("react"); var use_canvas_identifier_1 = require("../hooks/use-canvas-identifier"); var use_unity_loader_1 = require("../hooks/use-unity-loader"); var Unity = (0, react_1.forwardRef)( /** * @param unityProps The Unity props provided the the Unity component. * @param forwardedRef The forwarded ref to the Unity component. * @returns The Unity canvas renderer. */ function (props, forwardedRef) { // State to hold the canvas reference and Unity instance. // The canvas reference is used to render the Unity instance. var _a = (0, react_1.useState)(null), canvasRef = _a[0], setCanvasRef = _a[1]; var _b = (0, react_1.useState)(null), unityInstance = _b[0], setUnityInstance = _b[1]; // Use a custom hook to generate a unique canvas ID or use the provided one. // This ensures that each Unity instance has a unique canvas ID. // This is important for multiple Unity instances on the same page. // The hook also provides a function to refresh the canvas ID if needed. var _c = (0, use_canvas_identifier_1.useCanvasIdentifier)(props.id), canvasId = _c[0], refreshCanvasId = _c[1]; // Use a custom hook to load the Unity loader script. // This hook returns the status of the loader, which can be used to // determine if the Unity instance is ready to be initialized. var unityLoaderStatus = (0, use_unity_loader_1.useUnityLoader)(props.unityProvider.loaderUrl); /** * Callback function to handle the Unity loading progression. * This function is called by the Unity loader to update the loading * progression of the Unity instance. * @param progress The loading progression of the Unity instance. */ var onUnityProgress = (0, react_1.useCallback)(function (progress) { // This function is called to update the loading progression of the Unity // instance. props.unityProvider.setLoadingProgression(progress); if (progress === 1) { // If the loading progression reaches 100%, we can set the isLoaded state // to true. props.unityProvider.setIsLoaded(true); } }, [props.unityProvider]); // Effect to initialize the Unity instance when the component mounts or // when the canvas reference or Unity loader status changes. (0, react_1.useEffect)(function () { // Function to initialize the Unity instance. // This function is called when the component mounts or when the // canvas reference or Unity loader status changes. var initializeUnity = function () { return __awaiter(void 0, void 0, void 0, function () { var unityArguments, unityInstance_1, error_1; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!canvasRef || unityInstance || unityLoaderStatus !== "Loaded") { // If there is no canvas reference, or if the Unity instance is already // initialized, or if the Unity loader is not ready yet, we simply return. // This prevents unnecessary re-initialization of the Unity instance. return [2 /*return*/]; } console.log("React Unity WebGL: Initializing Unity instance..."); // Remove the reference to the previous Unity instance if it exists and // set the Unity instance in the Unity provider to null. props.unityProvider.setUnityInstance(null); setUnityInstance(null); // Reset the loading progression and isLoaded state to their initial // values. This is important to ensure that the Unity context is // properly reset when the Unity instance is detached. props.unityProvider.setLoadingProgression(0); (_b = (_a = props.unityProvider).setIsLoaded) === null || _b === void 0 ? void 0 : _b.call(_a, false); props.unityProvider.setInitialisationError(undefined); // Create a new canvas element with the unique ID. // This ensures that the Unity instance is rendered in the correct canvas. // The canvas element is created with the ID provided in the props or a // unique ID generated by the useCanvasIdentifier hook. refreshCanvasId(); unityArguments = { companyName: props.unityProvider.companyName, productName: props.unityProvider.productName, productVersion: props.unityProvider.productVersion, dataUrl: props.unityProvider.dataUrl, frameworkUrl: props.unityProvider.frameworkUrl, codeUrl: props.unityProvider.codeUrl, workerUrl: props.unityProvider.workerUrl, memoryUrl: props.unityProvider.memoryUrl, symbolsUrl: props.unityProvider.symbolsUrl, streamingAssetsUrl: props.unityProvider.streamingAssetsUrl, devicePixelRatio: props.devicePixelRatio, webglContextAttributes: props.unityProvider.webglContextAttributes, cacheControl: props.unityProvider.cacheControl, autoSyncPersistentDataPath: props.unityProvider.autoSyncPersistentDataPath, matchWebGLToCanvasSize: props.matchWebGLToCanvasSize, disabledCanvasEvents: props.disabledCanvasEvents, }; // Remove properties that are null or undefined. This is important to // avoid passing undefined properties to the Unity instance, which can // cause errors during initialization. Object.keys(unityArguments).forEach(function (key) { if (unityArguments[key] === null || unityArguments[key] === undefined) { delete unityArguments[key]; } }); _c.label = 1; case 1: _c.trys.push([1, 3, , 4]); return [4 /*yield*/, window.createUnityInstance(canvasRef, unityArguments, onUnityProgress)]; case 2: unityInstance_1 = _c.sent(); // If the Unity instance is successfully created, we set it in the state. // This allows us to use the Unity instance in the component. setUnityInstance(unityInstance_1); // We also set the Unity instance in the Unity provider. // This allows the Unity provider to access the Unity instance and // call its internal methods. props.unityProvider.setUnityInstance(unityInstance_1); return [3 /*break*/, 4]; case 3: error_1 = _c.sent(); // If there is an error during the initialization, we log it to the console. // This is important for debugging purposes. console.error("React Unity WebGL: Error initializing Unity instance:", error_1); // We also set the initialisation error in the Unity provider. // This allows the parent component to handle the error if needed. // The initialisation error can be used to display an error message or // take other actions based on the error. props.unityProvider.setInitialisationError(error_1); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; // Function to detach the Unity instance and clean up the canvas. // This function is called when the component unmounts or when the // Unity instance is no longer needed. var detachUnity = function () { return __awaiter(void 0, void 0, void 0, function () { var cleanupCanvasRef; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!unityInstance || !canvasRef) { // If there is no Unity instance or canvas reference available, // we simply return to avoid any errors. return [2 /*return*/]; } console.log("React Unity WebGL: Detaching Unity instance..."); // Remove the reference to the previous Unity instance if it exists and // set the Unity instance in the Unity provider to null. props.unityProvider.setUnityInstance(null); setUnityInstance(null); // Reset the loading progression and isLoaded state to their initial // values. This is important to ensure that the Unity context is // properly reset when the Unity instance is detached. props.unityProvider.setLoadingProgression(0); (_b = (_a = props.unityProvider).setIsLoaded) === null || _b === void 0 ? void 0 : _b.call(_a, false); props.unityProvider.setInitialisationError(undefined); cleanupCanvasRef = document.createElement("canvas"); cleanupCanvasRef.id = canvasRef.id; cleanupCanvasRef.setAttribute("react-unity-webgl-role", "cleanup"); cleanupCanvasRef.style.display = "none"; document.body.appendChild(cleanupCanvasRef); unityInstance.Module.canvas = cleanupCanvasRef; setUnityInstance(null); return [4 /*yield*/, unityInstance.Quit()]; case 1: _c.sent(); document.body.removeChild(cleanupCanvasRef); return [2 /*return*/]; } }); }); }; // Initialize the Unity instance when the component mounts or when the // canvas reference or Unity loader status changes. initializeUnity(); return function () { // Cleanup the Unity instance and canvas when the component unmounts. // This ensures that the Unity instance is properly disposed of and // the canvas is removed from the DOM. detachUnity(); }; }, [canvasRef, unityInstance, unityLoaderStatus, props.unityProvider]); // Use the forwarded ref to expose the canvas reference to the parent // component. This allows the parent component to access the canvas element // directly if needed, for example, to manipulate the canvas or pass it to // other libraries. (0, react_1.useImperativeHandle)(forwardedRef, function () { return canvasRef; }); // If the Unity instance is not ready yet, we return a placeholder canvas // element with the unique ID. This canvas will be replaced by the Unity // instance once it is initialized. return (0, react_1.createElement)("canvas", { ref: setCanvasRef, id: canvasId, style: props.style, className: props.className, tabIndex: props.tabIndex, }); }); exports.Unity = Unity;