如何在浏览器端与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
似乎只是调用了第一个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
未完....待续....