Unity 与 Web 数据交互

考虑到后面或许会有UnityWeb一同使用的场景,研究了一下 Unity 如何与 Web 进行交互,包含 Unity 调用 Web 方法,Web 调用 Unity 方法。

实现方式

  1. Unity 端实现一个jslib文件预定义函数作为桥接。
  2. C#脚本使用DllImport引入和调用预定义的函数。
  3. Web 端使用createUnityInstance加载 Unity,然后通过SendMessage调用 Unity 端的函数。

Unity 端实现

创建 Unity 项目

Hierarchy 中创建一个Cube,和一个Canvas,然后在Canvas下创建一个Button

Scene

创建 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)。

WebGL

点击左下角的Player Settings,在Resolution and PresentationWebGL Template选择Minimal,在Publishing SettingsCompression Format选择Disabled

Minimal

Disabled

点击Build,选择一个目录,等待打包完成,会自动打开目录,我们只需要Build文件夹即可,创建canvas自己在Web端完成。

Build

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>

注意有坑

  1. 用于接收 Web 端调用的函数必须为public,否则会报错。
  2. 用于 Unity 调用 Web 端的函数必须挂在 UnityInstance 所在的 window 上,否则会报错。
  3. canvas 的style中必须设置widthheight,否则在移动端和Mac(我要是没有Mac还真发现不了了😡)上会出现显示问题。

官方文档

https://docs.unity3d.com/cn/2020.3/Manual/webgl-interactingwithbrowserscripting.html

最终效果展示