Memory in WebGL
使用 WebGL 模板

WebGL:与浏览器脚本交互

构建适用于 Web 的内容时,可能需要与网页上的其他元素进行通信。或者,您可能希望使用 Unity 当前在默认情况下未公开的 Web API 来实现功能。在这两种情况下,都需要直接与浏览器的 JavaScript 引擎连接。Unity WebGL 提供了不同的方法来执行此操作。

从 Unity 脚本调用 JavaScript 函数

The recommended way of using browser JavaScript in your project is to add your JavaScript sources to your project, and then call those functions directly from your script code. To do so, place files with JavaScript code using the .jslib extension under a “Plugins” subfolder in your Assets folder. The plugin file needs to have a syntax like this:

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]);
  },

});

然后,可从 C# 脚本调用这些函数,如下所示:

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());
    }
}

简单的数字类型可在函数参数中传递给 JavaScript,无需进行任何转换。其他数据类型将作为 emscripten 堆(实际上就是 JavaScript 中的一个大型数组)中的指针进行传递。对于字符串,可使用 Pointer_stringify helper 函数转换为 JavaScript 字符串。要返回字符串值,必须调用 _malloc 来分配一些内存,并调用 stringToUTF8 helper 函数向其中写入 JavaScript 字符串。如果字符串是返回值,则 il2cpp 运行时将负责为您释放内存。对于原始类型的数组,emscripten 针对内存的不同大小的整数、无符号整数或浮点数表示形式,提供其堆的不同 ArrayBufferViews:__HEAP8、HEAPU8、HEAP16、HEAPU16、HEAP32、HEAPU32、HEAPF32、HEAPF64__。为了在 WebGL 中访问纹理,emscripten 提供了 GL.textures 数组,该数组将本机纹理 ID 从 Unity 映射到 WebGL 纹理对象。可在 emscripten 的 WebGL 上下文 GLctx 中调用 WebGL 函数。

有关如何与 JavaScript 交互的更多信息,请参阅 emscripten 文档

另外,请注意,在 Unity 安装文件夹中有几个插件可供参考(位于 PlaybackEngines/WebGLSupport/BuildTools/libPlaybackEngines/WebGLSupport/BuildTools/Emscripten/src/library* 中)。

从 Unity 调用 JavaScript 代码的传统方法

注意:从 Unity 5.6 开始,从 Unity 调用 JavaScript 代码的建议方法是通过 .jslib 插件。下述方法受到支持仅出于兼容性原因,并可能在 Unity 的未来版本中被弃用。

可使用 Application.ExternalCall()Application.ExternalEval() 函数在嵌入网页上调用 JavaScript 代码。请注意,表达式在构建的局部作用域内进行计算。如果希望在全局作用域内执行 JavaScript 代码,请参阅下面的代码可见性部分。

从 JavaScript 调用 Unity 脚本函数

有时需要从浏览器的 JavaScript 向 Unity 脚本发送一些数据或通知。建议的做法是调用内容中的游戏对象上的方法。如果要从嵌入在项目中的 JavaScript 插件执行调用,可使用以下代码:

SendMessage(objectName, methodName, value);

其中,__objectName__ 是场景中的对象名称;__methodName__ 是当前附加到该对象的脚本中的方法名称;__value__ 可以是字符串、数字,也可为空。例如:

SendMessage('MyGameObject', 'MyFunction');
SendMessage('MyGameObject', 'MyFunction', 5);

SendMessage('MyGameObject', 'MyFunction', 'MyString');

如果希望从嵌入页面的全局作用域内执行调用,请参阅下面的代码可见性部分。

Calling C functions from Unity scripts

Unity compiles your sources into JavaScript from C/C++ code using emscripten, so you can also write plugins in C/C++ code, and call these functions from C#. So, instead of the jslib file in the example above, you could have a C/C++ file in your project - it will automatically get compiled with your scripts, and you can call functions from it, just like in the JavaScript example above.

If you are using C++ (.cpp) to implement the plugin then you must ensure the functions are declared with C linkage to avoid name mangling issues:

#include <stdio.h>

extern "C" void Hello ()
{
    printf("Hello, world!\n");
}

extern "C" int AddNumbers (int x, int y)
{
    return x + y;
}

代码可见性

Starting from Unity 5.6 all the build code is executed in its own scope. This approach makes it possible to embed your content on an arbitrary page without causing conflicts with the embedding page code, as well as makes it possible to embed more than one build on the same page.

如果项目中包含 .jslib 插件形式的所有 JavaScript 代码,则此 JavaScript 代码的运行作用域将与编译的构建相同,并且代码应该与 Unity 先前版本中的工作方式非常相似(例如,以下对象和函数应该在 JavaScript 插件代码中直接可见:Module、SendMessage、HEAP8、ccall 等)。

However, if you are planning to call the internal JavaScript functions from the global scope of the embedding page, you should always assume that there are multiple builds embedded on the page, so you should explicitly specify which build you are referencing to. For example, if the content has been instantiated as:

var unityInstance = UnityLoader.instantiate("unityContainer", "Build/build.json", {onProgress: UnityProgress});

Then you can send a message to the build using unityInstance.SendMessage(), or access the build Module object using unityInstance.Module.


  • 2018–10–18 Page amended with no editorial review

  • 修复了代码示例中的错误。

  • WebGL instance renamed from gameInstance to unityInstance in 2019.1

Memory in WebGL
使用 WebGL 模板