Version: 2021.2
WebGL 中的内存
WebGL 性能注意事项

WebGL:与浏览器脚本交互

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

从 Unity 脚本调用 JavaScript 函数

在项目中使用浏览器 JavaScript 的建议方法是将 JavaScript 源代码添加到项目中,然后直接从脚本代码中调用这些函数。为此,请使用 .jslib 扩展名将包含 JavaScript 代码的文件放置在 Assets 文件夹中的“Plugins”子文件夹下。插件文件需要有如下所示的语法:

mergeInto(LibraryManager.library, {

  Hello: function () {
    window.alert("Hello, world!");
  },

  HelloString: function (str) {
    window.alert(UTF8ToString(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());
    }
}

Simple numeric types can be passed to JavaScript in function parameters without requiring any conversion. Other data types is passed as a pointer in the emscripten heap (which is just a big array in JavaScript). For strings, you can use the UTF8ToString helper function to convert to a JavaScript string. To return a string value you need to call _malloc to allocate some memory and the stringToUTF8 helper function to write a JavaScript string to it. If the string is a return value, then the il2cpp runtime will take care of freeing the memory for you. For arrays of primitive types, emscripten provides different ArrayBufferViews into it’s heap for different sizes of integer, unsigned integer or floating point representations of memory: HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64. To access a texture in WebGL, emscripten provides the GL.textures array which maps native texture IDs from Unity to WebGL texture objects. WebGL functions can be called on emscripten’s WebGL context, GLctx.

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

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

代码可见性

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, and makes it possible to embed more than one build on the same page.

If you have all your JavaScript code in the form of .jslib plugins inside your project, then this JavaScript code will run inside the same scope as the compiled build and your code should work the same way as in previous versions of Unity. For example, the following objects and functions should be directly visible from the JavaScript plugin code: Module, SendMessage, HEAP8, ccall etc..

但是,如果计划从嵌入页面的全局作用域调用内部 JavaScript 函数,则必须在 WebGL 模板 index.html 中使用 unityInstance 变量。在 Unity 引擎实例化成功后执行此操作,例如:

  var myGameInstance = null;
    script.onload = () => {
      createUnityInstance(canvas, config, (progress) => {...}).then((unityInstance) => {
        myGameInstance = unityInstance;
        …

然后可以使用 myGameInstance.SendMessage() 向构建发送消息,或使用 myGameInstance.Module 访问构建 Module 对象。

从 JavaScript 调用 Unity 脚本函数

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

MyGameInstance.SendMessage(objectName, methodName, value);

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

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

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

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

从 Unity 脚本调用 C 函数

由于 Unity 使用 emscripten 将源代码从 C/C++ 代码编译为 JavaScript,因此您也可以使用 C/C++ 代码编写插件,并从 C# 调用这些函数。因此,您可以不使用上面示例中的 jslib 文件,而是在项目中使用 C/C++ 文件;它将自动使用您的脚本实现编译,并且您可以从中调用函数,就像上面的 JavaScript 示例一样。

如果使用 C++ (.cpp) 来实现该插件,则必须确保使用 C 链接来声明函数以免发生名称错用问题:

# include <stdio.h>

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

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

  • replaced Pointer__stringify() with UTF8ToString in 2021.2 onwards

  • 在 2020.1 中 unity.Instance 已替换为 createUnityInstance

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

  • 在 2019.1中,WebGL 实例 gameInstance 更名为 unityInstance

WebGL 中的内存
WebGL 性能注意事项