本文旨在说明怎样将Unity项目打包成WebGL资源引入React项目并实现通信
本文使用 Unity2020.3
本文使用 create-react-app 创建React项目,React版本为16.4.0 本文使用 react-unity-webgl 插件的8.x版本 github.com/jeffreylant…
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”,双向通信实现。
其他插件功能可参考官方文档github.com/jeffreylant…尝试。