自己虽然最开始弄的就是webgl但是一直比ios和安卓记录的都要晚,因为一直没想到,所以这里结合某个博客加上自己的经历记录一下

关于Unity在WebGL平台的特性和交互 可以参考官方文档
以及 Unity(WebGL)与JS通讯2022最新姿势

一.老版方法

unity发布webplayer平台后会输出html和unity3d文件。我们的程序主要被打包在unity3d文件里,而html则是网页的界面显示,webPlayer已经被弃用了,鉴于一些公司可能旧项目依然使用这个平台,这里就保留这部分解释。

1.Unity发送消息给JS

unity想要和js交互,提供了一个函数:Application.ExternalCall();此函数仅限于webplayer平台下。我们编辑发布的html文件,在里面加入我们的js脚本如下:

function GetID(id)
{
   alert("传入id:"+id);	
}

在unity里我们在start函数里调用

Application.ExternalCall("GetID","吴彦祖");

使用浏览器打开html文件,就会出现如下弹窗:

在这里插入图片描述

2.JS发送消息给unity

我们在刚才的js函数里添加一句代码:

	function GetID(id)
	{
		alert("传入id:"+id);
//发送消息给unity 第一个参数:挂在脚本的物体 第二个参数:unity被调用的函数 第三个参数:函数传入的参数
		u.getUnity().SendMessage("Main Camera","GetIDInfo","JayW")
	}

在unity里创建一个函数用来接收处理:

public void GetIDInfo(string id)
    {
        text.text = id;
    }

运行效果如下:
在这里插入图片描述

二.新版方法

最近在使用webgl方面,2018.2.9版本弃用了Application.ExternalCall方法。选择添加一个jslb文件作为中间层。我们搜官方文档可以看到使用方式如下,官方文档 WebGL:与浏览器脚本交互

Unity主动发起调用js函数

首先创建一个文本,文件格式保持为.jslib。这个文件存放的是c#主动通信前端的中间方法,我们在里面增加我们要接受c#传来的数据和js要调用的函数。 代码如下:

mergeInto(LibraryManager.library, {
 //这里是代码1
  JSLog: function (str) {
  var strs=Pointer_stringify(str);
  //这个Log方法是前端那边写的方法
    Log(strs);
  },

	//这里可以添加若干个方法,方法之间记得用逗号隔开,
	//否则WebGL平台打包的时候会报错
});

注意:这里一定要用unity内置函数Pointer_stringify将传进来的str转换成字符串。如果不转换str存储的是内存地址位。传到js就是一堆数字。最后保存文件,将文件放到unity工程下的Plugins文件夹内。

然后在c#代码块里,我们加上我们要调用的方法:


[DllImport("__Internal")]
private static extern void JSLog(string str);//此处传入的类型和调用时候传入的数据类型一定要一致


void Start()
{
	//如果有返回值,可以定义变量接收返回值
    JSLog("666");
    
}

这里我们在C#里面调用了工程中的jslib文件的函数。

最后使我们打包出来的html文件里,在scrip块内加入jslib的JSLog里面被调用的函数Log:

 function Log(str)
	  {
		  console.log(str);;
		  gameInstance.SendMessage('Main Camera','GetStr',String(str));
	  }

在jslib的方法里面,如果调用前端的方法后有字符串返回 则需要这样写,字符串才能返回到c#里面

//获取服务器访问的认证 
	getServerAuth: function(){
		var token = window.THREE_TOKEN();
    	console.log("jslib getServerAuth token " + token);
        //这样写
    	var bufferSize = lengthBytesUTF8(token) + 1;
        var buffer = _malloc(bufferSize);
        stringToUTF8(token, buffer, bufferSize);
        return buffer;
    },

js主动发起Unity函数调用

前端调用的时候是这样的

this.gameInstance.SendMessage('OutSideInteractor', 'ReceiveDlteAddedProduct ', id)

其中 this.gameInstance是unity webgl平台出包后的文件夹里面的index.html的gameInstance对象.

SendMessage方法第一个是场景里面的物体名字,第二个是物体所挂脚本里面的某个方法名字,第三个参数是参数,要注意的是,即使前两个写对了,如果第三个参数没传对,也会报错说找不到方法

下面是index.html里面定义的内容,前端调用Unity的代码也可以写到这里,但更多是写到前端自己的某个脚本里面


var gameInstance = UnityLoader.instantiate("gameContainer", "Build/CPWEBGL.json", { onProgress: UnityProgress })
		window.ReceiveAddSceneItemStr = function (e) {
        gameInstance.SendMessage('OutSideInteractor', 'ReceiveAddSceneItemStr', e)
}
    

这里的CPWEBGL是我打webgl包的最后一个文件夹的名字,生成的json和unityLoad文件的文件名字与这个名字相同,unityLoad文件是Unity的主要内容的存放文件,这个名字一般要和前端约定好,因为前端加载Unity文件的时候会写死一个名字,双方的名字不同的时候会出现问题。具体参考 Unity WebGL错误集锦

因为这里使用的2021.3.8f1c1,后面的unity打包的index出入比较大,原来的gameInstance已经找不到了。 也可以在index.html的onLoad方法里面加上回调的语句,onLoad这个方法在生成好的工程的index.html里面是有的。这里的unityInstance由回调函数传入。
在这里插入图片描述

 script.onload = () => {
        createUnityInstance(canvas, config, (progress) => {
          progressBarFull.style.width = 100 * progress + "%";
        }).then((unityInstance) => {
          loadingBar.style.display = "none";
          fullscreenButton.onclick = () => {
            unityInstance.SetFullscreen(1);
          
          };
            unityInstance.SendMessage('Main Camera', 'ReceieveFromJS', "456");
        }).catch((message) => {
          alert(message);
        });
      };

经过测试是可以的。

要注意的一点是这时虽然能够传输信息,但是传输过去的时候对于大多数的脚本,其可能awake都还没有执行。所以这时进行变量的存储的比较必要的。有些变量在awake之后才进行使用。

index.html可以在unity的编辑器的Aseet文件夹下面的WebGLTemplates里面定义好,用于自定义WebGL加载画面
具体过程可以百度WebGLTemplates.

或者这里有一篇比较好的参考 【Unity3D日常开发】Unity3D模板 WEBGL模板 自定义Templates 使用教程

另外还要注意的一个点是,在onLoad刚刚结束的时候,可能Unity的生命周期还没开始。
此时往Unity里面发送消息完全是靠 运气的,有时能接收到,有时不能接收到。

所以做法是延迟一段时间,再往里面发送消息。
上面的

   unityInstance.SendMessage('Main Camera', 'ReceieveFromJS', "456");

改成

  setTimeout(function(){
    // 延迟调用的方法
    unityInstance.SendMessage('ReceiveFromJSToken',
            'ReceiveFromJSMethod1', window.location.href);
  }, 0.7);

报错

  1. 在打包的时候,jslib里面不能够有中文。注释也是不行的。否则就会报这个错。
Building Library\Bee\artifacts\WebGL\build\debug_WebGL_wasm\build.js failed with output:
Traceback (most recent call last):
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emcc2.py", line 3571, in <module>
    sys.exit(main(sys.argv))
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emcc2.py", line 3564, in main
    ret = run(args)
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emcc2.py", line 1082, in run
    phase_post_link(options, wasm_target, wasm_target, target)
  File "D:\obj\windows-release\37amd64_Release\msi_python\zip_amd64\contextlib.py", line 74, in inner
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emcc2.py", line 2400, in phase_post_link
    phase_emscript(options, in_wasm, wasm_target, memfile)
  File "D:\obj\windows-release\37amd64_Release\msi_python\zip_amd64\contextlib.py", line 74, in inner
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emcc2.py", line 2428, in phase_emscript
    emscripten.run(in_wasm, wasm_target, final_js, memfile)
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emscripten.py", line 830, in run
    emscript(in_wasm, out_wasm, outfile_js, memfile, shared.DEBUG)
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emscripten.py", line 308, in emscript
    glue, forwarded_data = compile_settings()
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emscripten.py", line 178, in compile_settings
    cwd=path_from_root('src'), env=env)
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\tools\shared.py", line 216, in run_js_tool
    return check_call(command, *args, **kw).stdout
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\tools\shared.py", line 202, in check_call
    return run_process(cmd, *args, **kw)
  File "C:\Program Files\Unity\Hub\Editor\2021.3.8f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\tools\shared.py", line 97, in run_process
    ret = subprocess.run(cmd, check=check, input=input, *args, **kw)
  File "D:\obj\windows-release\37amd64_Release\msi_python\zip_amd64\subprocess.py", line 474, in run
  File "D:\obj\windows-release\37amd64_Release\msi_python\zip_amd64\subprocess.py", line 926, in communicate
UnicodeDecodeError: 'gbk' codec can't decode byte 0x99 in position 393654: illegal multibyte sequence
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

工程示例

Unity WebGL 前端js互相交互示例工程

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐