如何在浏览器端与JavaScript链接

如果你了解过WebGL:Communicating with a browser script,好像主要有两种方法。

  1. Application.External Call()/Send Message()---------不过第一个在unity2018某个版本后就被弃用了,
  2. 使用插件(.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

似乎只是调用了第一个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

 

当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

 

未完....待续....