Unity模块嵌入React项目
本文旨在说明怎样将Unity项目打包成WebGL资源引入React项目并实现通信
Unity打包为WebGL
在Unity的主菜单中选择File -> Build Settings... 打开打包弹窗????
Unity 默认不安装打包为 WebGL 的插件,需要自己在 Unity Hub 按照提示安装插件
点击????弹窗左下角的 Player Settings... 按钮,打开打包配置弹窗,这一步最重要的就是将 Publishing Settings 配置中的 Compression Format 压缩方式改为 Disabled 不压缩,其他配置可参考文档进行配置也可直接使用默认条件
点击 Build Settings 弹窗右下角的 Build / Build And Run 按钮,区别在于 Build And Run 按钮不仅会打包还会自动开启一个本地服务器运行打包好的 WebGl 项目。这一步需要注意的是,点击打包后需要选择打包项目存放的文件夹,一定要放在本项目的根目录下,即与Assets文件夹同级。
打包得到index.html入口文件和Build、TemplateData两个文件夹,index.html对后续引入无用。
React项目引入
本项目使用 create-react-app 创建一个单页的React项目,官方文档:zh-hans.reactjs.org/docs/create…
本项目使用第三方插件 react-unity-webgl 在 React 项目中加载由 Unity 项目打包得到的 WebGL 资源,官方文档:github.com/jeffreylant…
插件安装
$ npm install react-unity-webgl@8.x # For Unity 2020 and 2021 (Current) 复制代码
使用npm安装,因本项目使用的Unity版本为2020,因此需安装8.x版本
模块引入与显示
将打包得到的 Build、TemplateData 文件放在React项目的public文件夹下
在组件中使用资源,示例代码:
// App.js import React from "react"; import Unity, { UnityContext } from "react-unity-webgl"; const unityContext = new UnityContext({ loaderUrl: "Build/beidou3Dweb.loader.js", // public下目录 dataUrl: "Build/beidou3Dweb.data", frameworkUrl: "Build/beidou3Dweb.framework.js", codeUrl: "Build/beidou3Dweb.wasm", }); function App() { // 一定要给Unity组件设置width和height属性,否则Canvas将无限增大最终导致浏览器卡死 return <Unity style={{'width': '100%', 'height': '100%'}} unityContext={unityContext} />; } export default App; 复制代码
此时页面中会显示一个Canvas,Canvas中即是Unity项目的Game页面。
React到Unity的通信
通信是通过调用 UnityContext 实例的 send 方法实现的,该 send 方法可以指定 Unity 项目中需要调用的 GameObject 名称以及该 GameObject 上绑定的需要执行函数的名称,此外还可以传递一个 number / string / boolean 类型的参数,注意只能传一个(或不传)。在 React 触发 send 后,Unity 中的对应函数将执行,从而完成一次通信。
React中的代码:
// React App.js import React, { useState, useEffect } from "react"; import Unity, { UnityContext } from "react-unity-webgl"; import { Button } from 'antd'; import './index.less'; const unityContext = new UnityContext({ loaderUrl: "Build/beidou3Dweb.loader.js", dataUrl: "Build/beidou3Dweb.data", frameworkUrl: "Build/beidou3Dweb.framework.js", codeUrl: "Build/beidou3Dweb.wasm", }); function App() { function spawnEnemies() { unityContext.send("ConnectObject", "SpawnEnemies", "Some Message"); // 调用send函数 // ConnectObject为游戏对象名称(全局唯一) // SpawnEnemies为ConnectObject上的回调函数名 // "Some Message"为需要传递的参数,此处为string类型 return ( <div className="container"> <Unity style={{'width': '100%', 'height': '100%'}} unityContext={unityContext} /> <Button type="primary" onClick={spawnEnemies}>To Unity</Button> </div> ); } export default App; 复制代码
Unity中的代码:
// Unity中的 CummunicateTest.cs 文件,一般放在Script文件夹下 // 绑定在名为 ConnectObject 的 Panel 游戏对象上 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class CummunicateTest : MonoBehaviour { public Text text; // 绑定为Panel下的一个Text public void SpawnEnemies (string amount) { // 该方法必须是公共的 text.text = amount; // 根据传参改变Text的文字 } } 复制代码
Unity到React的通信
通信是通过在Unity中的C#文件引入并调用中间层.jslib文件中的函数触发事件并携带需要传递的参数,而后在React中的UnityContext实例上通过on监听该事件并执行回调函数拿到传递参数实现的。
需要注意的是.jslib文件必须放在 Assets/Plugins/WebGL 目录下,如果默认没有这个目录就自己建!
关于怎么创建.jslib,使用mac的同学直接改后缀即可,使用Windows的同学就自己加油Google吧~
Unity 中的文件
// MyPlugin.jslib mergeInto(LibraryManager.library, { GameOver: function (userName, score) { ReactUnityWebGL.GameOver(Pointer_stringify(userName), score); }, }); 复制代码
// Unity中的 CummunicateTest.cs 文件,一般放在Script文件夹下 // 绑定在名为 ConnectObject 的 Panel 游戏对象上 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using System.Runtime.InteropServices; public class CummunicateTest : MonoBehaviour { public Button btn; public Text text; [DllImport("__Internal")] // 引入并声明.jslib中定义的GameOver函数 private static extern void GameOver(string userName, int score); // Start is called before the first frame update void Start() { btn.onClick.AddListener(() => { // 点击按钮时调用GameOver函数传参 GameOver("xinghui", 100); }); } public void SpawnEnemies (string amount) { text.text = amount; } } 复制代码
React中的文件
import React, { useState, useEffect } from "react"; import Unity, { UnityContext } from "react-unity-webgl"; import { Button } from 'antd'; import './index.less'; import fullScreenIconImage from '../../images/threeShow/fullScreenIcon.png'; const unityContext = new UnityContext({ loaderUrl: "Build/beidou3Dweb.loader.js", dataUrl: "Build/beidou3Dweb.data", frameworkUrl: "Build/beidou3Dweb.framework.js", codeUrl: "Build/beidou3Dweb.wasm", }); function App() { const [isGameOver, setIsGameOver] = useState(false); const [userName, setUserName] = useState(""); const [score, setScore] = useState(0); useEffect(function () { unityContext.on("GameOver", function (userName, score) { // 监听GameOver事件 setIsGameOver(true); setUserName(userName); setScore(score); }); }, []); function spawnEnemies() { unityContext.send("ConnectObject", "SpawnEnemies", "Some Message"); } function setUnityFullScreen() { unityContext.setFullscreen(true); // 设置Unity的Canvas全屏展示 } return ( <div className="three-show-container"> <img className="full-screen-icon" src={fullScreenIconImage} alt="全屏" onClick={setUnityFullScreen} /> <Unity style={{'width': '100%', 'height': '100%'}} unityContext={unityContext} /> {isGameOver === true && <p>{`From Unity ${userName} ${score}`}</p>} <Button type="primary" onClick={spawnEnemies}>To Unity</Button> </div> ); } export default App; 复制代码
最终实现效果是点击React中的按钮,Unity中的文字变为“Some Message”;点击Unity中的按钮,React中显示“From Unity xinghui 100”,双向通信实现。
作者:星惠-CG
链接:https://juejin.cn/post/7028100780364152846