在Unity(WebGL)中从C#函数调用浏览器端JavaScript函数
如何在浏览器端与JavaScript链接
如果你了解过WebGL:Communicating with a browser script,好像主要有两种方法。
-
Application.External Call()/Send Message()---------不过第一个在unity2018某个版本后就被弃用了,
-
使用插件(.jslib)编写要在浏览器端执行的代码。
在此文章中,我们使用第二个插件格式描述有关互相调用的方法。
插件(.jslib)不能使用ES2015(或更高的版本)编写
这个就有点蛋疼,如果你使用ES2015(或更高的版本)编写,会出现语法错误,你将无法构建.
它是Emscipten规范嘛?
创建.jslib文件
创建一个路径为Assets/Plugins/WebGl的文件夹,放置浏览器要执行的JavaScript代码,插件扩展名为.jslib,将代码文件保存在此路径,当你发布时,这段代码(具体来说)将在build JS中扩展.
1 ----------.jslib------- 2 var HogePlugin = { 3 Fuga: function(int x, int y) { 4 return x + y; 5 }, 6 Piyo: function(arg) { 7 var str = Pointer_stringify(arg); 8 } 9 } 10 mergeInto(LibraryManager.library, HogePlugin);
插件函数在build JS中扩展,在函数名的开头加了一个"_"
当您开始发布打包时,插件的功能将在构建JS中扩展,并在函数名称的开头添加一个"_"
因此,从JS端调用这些函数时,请在函数前加上"_"
1 ----------.jslib------- 2 var HogePlugin = { 3 Fuga: function(int x, int y) { 4 return x + y; 5 }, 6 Piyo: function(arg) { 7 var str = Pointer_stringify(arg); 8 } 9 } 10 mergeInto(LibraryManager.library, HogePlugin);
1 ----------js------- 2 // ... 3 function _Fuga(int x, int y) { 4 return x + y; 5 }, 6 function _Piyo(arg) { 7 var str = Pointer_stringify(arg); 8 } 9 // ...
从Unity 5.6开始,它将在块中扩展
虽然是部署目标,但由于扩展到了全局作用域,一直到5.5,所以很容易从外部JS代码访问,但从5.6开始,它在块中拓展,因此无法从外部JS代码访问.
因此,为了使其可以从外部JS代码访问,您可以编写代码设置在插件中的全局对象的属性中.
从C#调用一个JS函数
可以通过阅读WebGL: Communicating with a browser script 来学习,不过暂时可以通过调用一个有返回值的函数来获取返回值.
调用JS函数,使用DllImport属性定义和调用要使用的函数,类似于DLL函数调用.
1 using System.Runtime.InteropServices; 2 class Hoge { 3 [DllImport("__Internal")] 4 public static extern int Fuga(int n); 5 6 public void CallFuga(n) { 7 var ret = Fuga(n); 8 Debug.Log(ret); 9 } 10 // .... 11 }
1 ----------.jslib------- 2 var HogePlugin = { 3 Fuga: function(t) { 4 return t + 10; 5 } 6 } 7 mergeInto(LibraryManager.library, HogePlugin);
不是DllImport的函数不会被扩展
如果你只为Fuga()编写DllImport并如下例所示构建它,她将正确检查并且Piyo()不会被扩展来构建JS.
1 ----------.jslib------- 2 var HogePlugin = { 3 Fuga: function(int x, int y) { 4 return x + y;
}, 5 Piyo: function(arg) { 6 var str = Pointer_stringify(arg); 7 } 8 } 9 mergeInto(LibraryManager.library, HogePlugin);
1 using System.Runtime.InteropServices; 2 class Hoge { 3 [DllImport("__Internal")] 4 public static extern string Fuga(string configuration); 5 }
1 ----------JS脚本中------- 2 // ... 3 function _Fuga(int x, int y) { 4 return x + y; 5 }, 6 // ...
如果想要在JS端定义函数、数组或对象,需添加$并执行autoAddDeps()
如果你在C#端有一个不(直接)使用(不要DllImport)但想扩展为JS的函数,或者你想单独定义数组或对象,你可以使用'$'插件中如下代码所示,通过用成员名描述并执行autoAddDeps(),在JS端扩展为函数名或变量名带'$'的JS。 因此,作为警告,当在 JS 端使用这些时,如“_”的情况,使用带有“$”的名称。
但是,似乎无法使用诸如布尔、数字和字符串之类的原始事物。 具体而言,在布尔值或数值的情况下,不展开,在字符串的情况下,由于某种原因,字符串成为变量名,并作为适用的函数输出(null,arguments ) 到这个变量。
1 ----------.jslib------- 2 var HogePlugin = { 3 $hpFunc: function () { 4 return null; 5 },
6 $hpBool: true, 7 $hpNum: 100, 8 $hpStr: "测试", 9 $hpArr: [1, 2, 3], 10 $hpObj: { 11 hoge: 'fuga', 12 piyo: 2 13 }, 14 TestFunc: function () { 15 return null; 16 }, 17 TestFuga: function () { 18 return null; 19 } 20 }; 21 autoAddDeps(HogePlugin, '$hpFunc'); 22 autoAddDeps(HogePlugin, '$hpBool'); 23 autoAddDeps(HogePlugin, '$hpNum'); 24 autoAddDeps(HogePlugin, '$hpStr'); 25 autoAddDeps(HogePlugin, '$hpArr'); 26 autoAddDeps(HogePlugin, '$hpObj'); 27 mergeInto(LibraryManager.library, HogePlugin);
1 ----------JS脚本中------- 2 // ... 3 var hpObj = { 4 hoge: "fuga", 5 piyo: 2 6 }; 7 var hpArr = [ 1, 2, 3 ];
8 function hpStr() { 9 return _\u30c6\u30b9\u30c8.apply(null, arguments); 10 } 11 // ...
将参数传递给JS函数时,将原样传递数字类型,但如果传递非数字字符串类型或数组,则传递指针.
bool类型作为0和1传递.
string使用Pointer_stringify()从指针中获取字符串.
传递数值类型(int,float等)数组时,除了数组本身,还需要传递数组的元素个数.
从指针获取数值数组是从HEAP8、HEAPU8、HEAP16、HEAPU16、HEAP32、HEAPU32、HEAPF32、HEAPF64执行的.
1 ----------.jslib------- 2 var HogePlugin = { 3 // 准备一个从指针返回各种数值类型数组(子数组)的函数 4 $arrFromPtr: function(ptr, size, heap) { 5 // 返回不生成数组的 HEAP 子数组(值复制) 6 var startIndex = ptr / heap.BYTES_PER_ELEMENT; 7 // 通过除以类型中的字节数来计算起始索引 8 return heap.subarray(startIndex, startIndex + size); 9 }, 10 Fuga: function(n, f, pStr, pShortArr, shortArrLen, pFloatArr, floatArrLen) { 11 console.log(n, f); // 可以按原样获取数值类型 12 str = Pointer_stringify(pStr); // 对于字符串类型,使用 Pointer_stringify() 从指针中获取字符串。 13 // 从 HEAPnn 获得的数值数组 14 var shortArr = arrFromPtr(pShortArr, shortArrLen, HEAP16); 15 var floatArr = arrFromPtr(pFloatArr, floatArrLen, HEAPF32); 16 } 17 } 18 autoAddDeps(HogePlugin, '$arrFromPtr'); 19 mergeInto(LibraryManager.library, HogePlugin);
当使用字符串类型作为参数时,传递 null 将导致""(空字符串)
如果将JS函数的参数类型设置为string并调用它(具体来说Pointer_stringify()的返回值会是)"".
1 using System.Runtime.InteropServices;
2 class Hoge : MonoBehaviour { 3 [DllImport("__Internal")] 4 public static extern void Fuga(string str);
5 void Start() { 6 Fuga(null); 7 } 8 }
1 ----------sample3.jslib------- 2 3 var HogePlugin = { 4 Fuga: function(arg) { 5 var str = Pinter_stringify(arg); 6 console.log('isNull:' + (str === null), 'isEmpty:' + (str === '')); // isNull:false isEmpty:true 7 } 8 } 9 10 mergeInto(LibraryManager.library, HogePlugin);
字符串数组、对象数组、对象类型不能传递(JSON等序列化)
不难想象,除了对象或数值类型之外,您还不能传递数组。 因此,如果要传递这样的值,则应在JS端传递序列化为JSON和反序列化之类的方法。
重载不可用
C#可以重载函数,但是JS没有函数重载.
所以,正如下面示例一样,我用重载写了C#的代码,JS代码写了一个函数,试了试.
构建工作正常,但没有产生预期的结果。
具体来说,当我运行以下代码时,JS console.log() 的结果是
1 >1,undefined 2 >2,undefined
似乎只是调用了第一个DllImport.
1 using System.Runtime.InteropServices; 2 3 class Hoge : MonoBehaviour { 4 [DllImport("__Internal")] 5 public static extern void Fuga(int arg1); 6 [DllImport("__Internal")] 7 public static extern void Fuga(int arg1, int arg2); 8 9 void Start() { 10 Fuga(1); 11 Fuga(2, 3); 12 } 13 }
1 ----------sample3.jslib------- 2 var HogePlugin = { 3 Fuga: function(arg1, arg2) { 4 console.log(arg1, arg2); // 1, undefined 5 // 2, undefined 6 } 7 } 8 mergeInto(LibraryManager.library, HogePlugin);
不能将Object或Nullable类型指定为返回类型
构建是可以通过的,但在运行时会出现错误。
由于 Emscripten 的规范,我认为这是不可避免的。
当Object被指定为返回值时
1 using System.Runtime.InteropServices; 2 3 class Hoge : MonoBehaviour { 4 [DllImport("__Internal")] 5 public static extern object Fuga(int n); 6 7 void Start() { 8 var ret = Fuga(n); 9 Debug.Log(ret); 10 } 11 }
1 ----------.jslib------- 2 var HogePlugin = { 3 Fuga: function(t) { 4 return t + 10; 5 } 6 } 7 mergeInto(LibraryManager.library, HogePlugin);
1 ----------JS控制台打印信息------- 2 MarshalDirectiveException: Cannot marshal type 'System.Object'
当int?被指定为返回值
1 using System.Runtime.InteropServices; 2 3 class Hoge : MonoBehaviour { 4 [DllImport("__Internal")] 5 public static extern int? Fuga(int n); 6 7 void Start() { 8 var ret = Fuga(n); 9 Debug.Log(ret); 10 } 11 }
1 ----------.jslib------- 2 var HogePlugin = { 3 Fuga: function(t) { 4 return t + 10; 5 } 6 } 7 8 mergeInto(LibraryManager.library, HogePlugin);
1 ----------JS控制台打印信息------- 2 MarshalDirectiveException: Cannot marshal type 'System.Nullable`1<System.Int32>'
未完....待续....
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?