Unity 与 Web 数据交互
考虑到后面或许会有Unity
与Web
一同使用的场景,研究了一下 Unity 如何与 Web 进行交互,包含 Unity 调用 Web 方法,Web 调用 Unity 方法。
实现方式
- Unity 端实现一个
jslib
文件预定义函数作为桥接。 C#
脚本使用DllImport
引入和调用预定义的函数。- Web 端使用
createUnityInstance
加载 Unity,然后通过SendMessage
调用 Unity 端的函数。
Unity 端实现
创建 Unity 项目
Hierarchy 中创建一个Cube
,和一个Canvas
,然后在Canvas
下创建一个Button
:
创建 jslib 文件
在 Unity 项目的Assets
目录下创建Plugins
文件夹,然后在Plugins
文件夹下创建WebBridge.jslib
文件(名称自定义),代码如下:
mergeInto(LibraryManager.library, { // 用于接收 Web 端调用的函数 InvokeWebMethod: function (str) { _str = UTF8ToString(str) // 将 c# 字符串 转换为 js 字符串 WebMethod(_str) // 调用 Web 端定义的函数 }, })
创建调用 jslib 的 C# 脚本
在 Unity 项目的Assets
目录下创建Scripts
文件夹,然后在Scripts
文件夹下创建BtnCtrl.cs
文件,将其挂载到Button
上,代码如下:
using UnityEngine; using UnityEngine.UI; using System.Runtime.InteropServices; public class BtnCtrl : MonoBehaviour { void Start() { GetComponent<Button>().onClick.AddListener(() => { InvokeWebMethod("Hello World"); }); } [DllImport("__Internal")] private static extern void InvokeWebMethod(string str); }
创建用于 Web 端调用的 C# 脚本
在 Unity 项目的Assets
目录下创建Scripts
文件夹,然后在Scripts
文件夹下创建CubeCtrl.cs
文件,将其挂载到Cube
上,代码如下:
using UnityEngine; public class CubeCtrl : MonoBehaviour { public void RotateX(float x) { transform.Rotate(x, 0, 0); } }
编译项目
在 Unity 项目的File
菜单中选择Build Settings
,然后选择WebGL
,点击Switch Platform
(我这里已经切换完成了就会显示Build)。
点击左下角的Player Settings
,在Resolution and Presentation
中WebGL Template
选择Minimal
,在Publishing Settings
中Compression Format
选择Disabled
。
点击Build
,选择一个目录,等待打包完成,会自动打开目录,我们只需要Build文件夹
即可,创建canvas自己在Web端完成。
Web 端实现
固定写法,都是JS在啥框架都一样,下面这是写在vue里面了:
<template> <div full overflow-hidden> <canvas id="unity-canvas" style="width: 1920px; height: 1080px;" /> <button id="btn"> 旋转Cube </button> </div> </template> <script setup lang="ts"> // UnityInstance 用于存储 Unity 实例 let UnityInstance: any = null // buildUrl 为 Unity 打包后的文件夹路径,我改为了unity,就是上面打包的Build文件夹 const buildUrl = './unity' const config = { dataUrl: `${buildUrl}/Builds.data`, frameworkUrl: `${buildUrl}/Builds.framework.js`, codeUrl: `${buildUrl}/Builds.wasm`, streamingAssetsUrl: 'StreamingAssets', companyName: 'DefaultCompany', productName: 'WebGL', productVersion: '0.1', } // 这是 Unity 调用 Web 端的方法,在 jslib 文件中定义的函数 window.WebMethod = function (str: string) { alert(str) } onMounted(() => { // 设置 canvas 的宽高 const canvas = document.querySelector<HTMLCanvasElement>('#unity-canvas') canvas!.style.width = `${window.innerWidth}px` canvas!.style.height = `${window.innerHeight}px` window.addEventListener('resize', () => { canvas!.style.width = `${window.innerWidth}px` canvas!.style.height = `${window.innerHeight}px` }) // 加载 Unity const script = document.createElement('script') script.src = `${buildUrl}/Builds.loader.js` document.body.appendChild(script) script.onload = () => { createUnityInstance( canvas, config, (progress: number) => { console.log(`加载中:${progress * 100}%`,) } ) .then((unityInstance) => { // 加载完成后,将 UnityInstance 赋值给全局变量 UnityInstance = unityInstance }) .catch((message) => { console.log(message) }) } // 前端页面向unity页面传值需用到UnityInstance.SendMessage()函数,调用格式如下: // SendMessage(unityObject,unityMethodName,value) // unityObject——unity脚本挂载对象名 // unityMethodName——unity脚本内调用方法名(需为public方法) // value——前端需要传出的值 const btn = document.getElementById('btn') btn!.onclick = function () { UnityInstance.SendMessage('Cube', 'RotateX', 20) } }) </script>
注意有坑
- 用于接收 Web 端调用的函数必须为
public
,否则会报错。- 用于 Unity 调用 Web 端的函数必须挂在 UnityInstance 所在的
window
上,否则会报错。- canvas 的style中必须设置
width
和height
,否则在移动端和Mac(我要是没有Mac还真发现不了了😡)上会出现显示问题。
官方文档
https://docs.unity3d.com/cn/2020.3/Manual/webgl-interactingwithbrowserscripting.html