Unity3D导出WebGL与ASP.NET交互

发表于2018-09-29
评论0 7.6k浏览
当我们通过Unity发布WebGL时,通常会希望我们的Unity3D程序可以和网页中的元素进行交互,通过Unity内部的函数与网页端的方法交互实现数据的实时传递。

当我们把Unity发布出来时,通常会包含如下的文件:

在Build文件夹中包含了WebGL所有打包的内容,其中最重要的部分是UnityLoader.js和*.json这两个文件。

详细的介绍可以从官方的Interacting with browser scripting中找到相关的内容。

在这里我们主要通过SendMessage()和Application.ExternalCall() 这两个方法实现WebGL和ASP.NET的交互。

SendMessage()可以实现从网页端调用Unity中的方法,该方法中主要包含三个参数,分别表示Unity中的物体名、方法名和参数。参数一般为基础类型,如字符串、数字等。
SendMessage(objectName, methodName, value);

在WebGL中,浏览器通过如下代码初始化Unity程序:
<script src="TemplateData/UnityProgress.js"></script>
<script src="Build/UnityLoader.js"></script>
<script>
    var gameInstance = UnityLoader.instantiate("gameContainer", "Build/WebAndUnity.json", { onProgress: UnityProgress });
</script>

其中UnityProgress.js实现了WebGL加载过程中的进度条,我们可以通过重写该JS中的脚本来自定义不同的进度条样式和显示方式。UnityLoader.js则主要实现了WebGL在实例化过程中需要完成的各项操作,同时也为用户提供了额外的交互方式,如SendMessage方法便是在该脚本中定义的,gameContainer为放置实例化出来的WebGL的放置空间。

用户通过gameInstance来实现对WebGL中的物体发送消息,执行相应的操作。
function sendMesseageToUnity() 
{
    gameInstance.SendMessage("ObjectName", "FunctionName", "ParamValue");//通过该函数便可实现从Web端调用WebGL内部挂在某个物体上的某个函数。
    //SendMessage('MyGameObject', 'MyFunction');
    //SendMessage('MyGameObject', 'MyFunction', 5);
    //SendMessage('MyGameObject', 'MyFunction', 'MyString');
    //网页中通过gameInstance.SendMessage()方法对Unity中的物体发送消息,自动调用相应的代码,实现预期的功能。
}

在Unity中,如果我们想要调用网页中的方法,如将游戏中的信息试试传递到网页中,我们可以在Unity中通过Application.ExternalCall()方法实现。
private void TellWebDoSomethingFromUnity()
{
    object[] obj = new object[]{"SomeValue"};
    Application.ExternalCall("FunctionName",obj);
}

我们只需要在gameInstance所在的页面定义
function FunctionName(value)
{
    //do something
}
便可调用Web端的方法,同时将Unity中的数据传递到Web页面中,在传递数据过程中如果数据量较大,我们可以分批传递,传递完成后将信息拼接处理便可使用。

以上实现方法虽然在较新的Unity版本中能够实现相关功能,但是随着Unity的更新,上述方法很可能会渐渐被新的方法替代。

在Unity官网上提供了新的实现方法,主要是从Unity调用JavaScript中的函数,新的方法中,我们需要将JavaScript函数放入到Unity项目的Plugins文件夹下,以。jslib的文件类型进行存储,通过内部类库直接调用这些JS代码。

官方提供的相关示例代码如下:
mergeInto(LibraryManager.library, {
  Hello: function () {
    window.alert("Hello, world!");
  },
  HelloString: function (str) {
    window.alert(Pointer_stringify(str));
  },
  PrintFloatArray: function (array, size) {
    for(var i = 0; i < size; i++)
    console.log(HEAPF32[(array >> 2) + i]);
  },
  AddNumbers: function (x, y) {
    return x + y;
  },
  StringReturnValueFunction: function () {
    var returnStr = "bla";
    var bufferSize = lengthBytesUTF8(returnStr) + 1;
    var buffer = _malloc(bufferSize);
    stringToUTF8(returnStr, buffer, bufferSize);
    return buffer;
  },
  BindWebGLTexture: function (texture) {
    GLctx.bindTexture(GLctx.TEXTURE_2D, GL.textures[texture]);
  },
});
其中,mergeInto只是将第二个参数的属性复制到第一个参数上,即将后边定义的所有函数和属性复制到第一个参数LibraryManager.library类库中,所有的在JavaScript中的全局对象都应该以这种方式进行定义。

当我们在C#中调用JS相关函数时,官方示例代码如下:
using UnityEngine;
using System.Runtime.InteropServices;
public class NewBehaviourScript : MonoBehaviour {
    [DllImport("__Internal")]
    private static extern void Hello();
    [DllImport("__Internal")]
    private static extern void HelloString(string str);
    [DllImport("__Internal")]
    private static extern void PrintFloatArray(float[] array, int size);
    [DllImport("__Internal")]
    private static extern int AddNumbers(int x, int y);
    [DllImport("__Internal")]
    private static extern string StringReturnValueFunction();
    [DllImport("__Internal")]
    private static extern void BindWebGLTexture(int texture);
    void Start() {
        Hello();
        HelloString("This is a string.");
        float[] myArray = new float[10];
        PrintFloatArray(myArray, myArray.Length);
        int result = AddNumbers(5, 7);
        Debug.Log(result);
        Debug.Log(StringReturnValueFunction());
        var texture = new Texture2D(0, 0, TextureFormat.ARGB32, false);
        BindWebGLTexture(texture.GetNativeTextureID());
    }
}
在Unity中需要首先对内部声明的静态库进行调用,其中“__Internal”表示静态库的名称。在某些平台中,用户可以自定义静态库名称并进行引用。

从上述代码中我们可以看到,通过JS脚本,我们可以传递的参数种类较多。其中数字类型不需要通过转换便可进行传递,其余类型的参数类型均作为emscripten堆中的指针进行传递(JavaScript中的一个数组)。

针对字符串,可以使用Pointer_stringify帮助函数转换为JavaScript字符串。为了返回一个字符串值,需要调用_malloc来分配一些内存和stringToUTF8辅助函数来为它写一个JavaScript字符串。如果字符串是一个返回值,那么il2cpp运行时将负责释放内存。对于基本类型的数组,emscripten为不同大小的整数,无符号整数或浮点内存表达式提供了不同大小的ArrayBufferViews:HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64。要访问WebGL中的纹理,emscripten提供了GL.textures数组,它将Unity中的本机纹理ID映射到WebGL纹理对象。可以在emscripten的WebGL上下文GLctx上调用WebGL函数。

在该部分主要存在的问题是我们无法在直接在Unity中对所写的JS方法进行测试,需要发布WebGL才能运行。

Unity会通过UnityScript对JS代码进行获取和处理,从Unity5.6开始,官方建议将所有的JS脚本均以jslib的形式存放到plugins文件夹中,这样可以使所有WebGL的代码在自身范围内执行,从而不会与页面上的其他逻辑产生混淆,有效降低页面代码冲突的概率。同时在一个页面中我们也可以将多个WebGL应用嵌入其中,从而实现更好的管理方式。

同时,由于H5提供了针对WebSocket的支持,所以在WebGL中,同样可以实现长链接。
来自:https://blog.csdn.net/beihuanlihe130/article/details/76214551

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引