目录
- Unity WebGL 编译和打包说明
- WebGL 简介
- WebGL 开发
- WebGL Player 设置
- 与浏览器脚本的交互
- WebGL 原生插件与 Emscripten
- Unity WebGL 中的内存
- WebGL 中的缓存行为
- WebGL 图形
- WebGL 中的音频
- WebGL 中的纹理压缩
- WebGL 中的嵌入式资源 (Embedded resources in WebGL)
- WebGL 中的输入
- 配置 WebGL 画布大小
- WebGL 浏览器访问设备功能
- WebGL Networking
- WebGL 性能考虑
- 调试和排除 WebGL 构建问题
- 构建(Build)和分发(Distribute) WebGL 应用程序
- 参考
Unity WebGL 编译和打包说明
Unity 提供了对 WebGL 平台开发游戏的支持。WebGL 允许开发者将实时交互的3D图形发布到浏览器中。
WebGL 简介
Unity 编辑器中的 WebGL 选项允许您将内容发布为 JavaScript 程序,这些程序使用 HTML5/JavaScript、WebAssembly、WebGL rendering API 以及其他 Web 标准在 Web 浏览器中运行 Unity 内容。
WebGL 浏览器兼容性 (WebGL Browser Compatibility)
Unity 对桌面浏览器的 WebGL 支持因浏览器而异,支持满足以下条件的浏览器:
- 支持 WebGL 2:注意,Unity 不再为使用 Auto Graphics API 选项创建的构建提供 WebGL 1 的支持。更多详情,请参见 WebGL 1 的弃用。
- 符合 HTML 5 标准:浏览器应符合 HTML 5 标准。
- 64 位并支持 WebAssembly:浏览器应为 64 位并支持 WebAssembly。
Unity WebGL 不支持移动设备。尽管可能在高端设备上运行,但当前的移动设备通常性能不足且内存不够,无法支持 Unity WebGL 内容。
Unity WebGL 支持一些压缩纹理格式。有关 Unity WebGL 支持的压缩纹理格式的信息,请参见 Recommended, default, and supported texture compression formats, by platform 。
平台支持 (Platform Support)
大多数流行的桌面浏览器版本都支持 Unity WebGL 内容,但请注意,不同的浏览器提供的支持水平不同。例如,Unity WebGL 不支持移动设备。
由于平台本身的限制,WebGL 构建中的以下功能不可用或有限:
- Visual Studio 中缺乏 WebGL 构建调试支持Visual Studio 不支持调试 Unity WebGL 构建。更多信息,请参见 Debug and troubleshoot WebGL builds 。
- 缺乏 Unity 缓存和缓存脚本支持由于浏览器对文件系统的访问受限,WebGL 构建不支持 Unity Cache 和 Caching Scripting API。对资产数据(asset data)和 AssetBundles 的网络请求会被缓存到浏览器缓存中。请参阅 Cache behavior in WebGL 。
- 缺乏线程支持由于 JavaScript 不支持线程,因此不支持线程。这适用于 Unity 内部使用线程来加速性能的情况,以及在脚本代码和 managed DLL 中使用线程的情况。本质上,
System.Threading
命名空间中的任何内容都不支持。 - 网络限制WebGL 平台不支持以下几项网络功能:
- 出于安全考虑,浏览器不允许直接访问 IP sockets 。更多信息,请参见 Web networking 。
System.Net
命名空间中的 .NET networking class 不受支持。- 由于浏览器的安全限制,WebGL 平台不支持 native socket 访问。因此,WebGL 也不支持 ICMP ping 或
UnityEngine.Ping
等功能。
- 图形限制WebGL 平台的图形 API 基于 OpenGL ES 图形库的功能,有一些限制。更多信息,请参见 WebGL graphics 。
- 音频限制WebGL 构建使用基于 WebGL Audio API 的自定义音频后端,但它只支持基本的音频功能。更多信息,请参见 Audio in WebGL 。
- 代码的动态生成WebGL 是一个 AOT(Ahead Of Time)平台,因此不允许使用
System.Reflection.Emit
动态生成代码。所有其他 IL2CPP 平台、iOS 和大多数游戏主机也是如此。 - 复制和粘贴复制和粘贴功能仅在 Unity UI 内部有效。无法从系统剪贴板(system clipboard)复制和粘贴,也就是说,无法与外部应用进行复制或粘贴操作。
多线程支持
尽管 Unity 为 native C/C++ 代码提供了多线程支持,但由于 WebAssembly 的限制,Web 平台尚不支持 C# 多线程。这意味着使用 Web 平台构建的应用必须在单个 C# 线程上运行。
- Web 平台仅在启用 Web Player settings中的 Native C/C++ support 支持时支持 C/C++ 多线程。
- 当
document
处于安全上下文中时,Web 平台支持多线程。 - 服务器必须设置以下 HTTP 响应头:
- 在 Web 平台上执行复杂的异步任务的推荐方法是使用协程(不是多线程操作)。更多信息,请参见 coroutines 文档。
限制多线程支持的因素
- 本地堆栈扫描(native stack scanning)的限制Web 平台使用 WebAssembly,这是一种用于在 Web 浏览器中安全高效地执行 Unity 代码的字节码格式。Web 浏览器设计为在安全和隔离的环境中运行代码,阻止直接访问本地 WebAssembly 堆栈。这影响了多线程垃圾回收,因为 Web 的垃圾回收器仅在每帧结束时运行一次,而不像其他平台那样在多帧之间增量运行。
- 没有抢占式线程信号(pre-emptive thread signaling)支持Web 上的后台工作线程独立并行执行代码。在 native 平台上,主线程可以同步发送信号给其他线程暂停以进行垃圾回收。Web 上不支持这种同步信号,这阻止了 WebAssembly 编译的 C# 代码在多个线程上运行。
- 其他资源 (Additional Resources)
安装 Unity Hub 并添加所需模块
要构建 WebGL 应用程序,首先必须安装 Unity Hub,然后添加 WebGL Build Support
模块。
有关如何完成这些步骤的信息,请参阅安装 Unity Hub和向 Unity 编辑器添加模块。
WebGL 开发
WebGL Player 设置
使用 Player 设置了解 Unity 如何构建(build)并显示最终的 WebGL 应用程序。有关一般 Player 设置的说明,请参阅 Player settings。
要访问 WebGL Player 设置:
- 从 Unity 主菜单中,转到
Edit > Project Settings > Player
。Player 设置窗口将出现。 - 选择 WebGL 选项卡以查看 WebGL Player 设置。
注意:虽然 WebGL Player 设置中显示了图标面板,但没有图标设置可用,因为 WebGL 游戏不使用图标。
有关 WebGL Publishing Settings 的更多信息,请参阅 WebGL Building and Running。
Resolution and Presentation
使用 Resolution and Presentation 部分自定义屏幕外观的各个方面。
Resolution
WebGL Template
选择要用于 WebGL 项目的模板:
- Default 页面是一个简单的白色页面,灰色画布上有加载条。
- Minimal 页面只有运行 WebGL 内容所需的基本代码。
- PWA 页面包括一个 Web manifest 文件和 service worker 代码。您可以指定自己的模板,以便在类似于成品游戏的环境中运行您的游戏。有关详细说明,请参阅 Using WebGL Templates 。
Splash Image
使用虚拟现实启动画面设置为 XR 显示器选择自定义启动图像。有关常见启动屏幕设置的信息,请参阅 Splash Screen 。
Other Settings
Rendering
使用这些设置可以自定义 Unity 如何为 WebGL 平台渲染您的游戏。
Configuration
Shader Variant Loading
使用这些设置控制运行时着色器使用的内存量。
API Compatibility Level
您可以为所有 target 选择 Mono API 兼容性级别。有时,第三方 .NET 库使用的功能超出了您的 .NET 兼容性级别。要了解这种情况下的情况以及如何最好地解决它,请尝试以下建议:
- 为 Windows 安装 ILSpy 。
- 将导致问题的 API 兼容性级别的 .NET 程序集拖入 ILSpy。您可以在
Frameworks/Mono/lib/mono/YOURSUBSET/
下找到这些问题文件。 - 拖入您的第三方程序集。
- 右键单击第三方程序集并选择 Analyze 。
- 在分析报告中,检查 Depends on 部分。报告会突出显示第三方程序集依赖但不在您选择的 .NET 兼容性级别中可用的任何内容。
Script Compilation
Optimization
Stack Trace
为 WebGL 平台选择日志记录设置。
选择与每种日志类型(Error、Assert、Warning、Log 和 Exception)对应的选项,以基于所需的日志记录类型启用首选的堆栈跟踪方法。有关更多信息,请参阅 stack trace logging。
Legacy
启用 Clamp BlendShapes(已弃用)选项以在 Skinned Mesh Renderers 中夹住混合形状权重的范围。
Publishing Settings
使用 Publishing Settings 配置 Unity 构建 WebGL 应用程序的方式。例如,您可以选择启用浏览器缓存以在构建中存储其文件。
Show Diagnostic Overlay 设置
为了帮助优化 WebGL 构建并诊断潜在问题,您可以通过启用此设置查看诊断信息(目前限于内存使用情况)。启用后,构建上会出现一个图标,显示有关构建的有用数据。此选项适用于开发和发布构建。
要查看诊断信息,请在 Player settings 窗口(File > Build Settings > Player Settings > Publishing Settings
)中启用 Show Diagnostics Overlay
选项。
在桌面上,诊断图标出现在 WebGL 画布的页脚:
在移动设备上,诊断图标出现在屏幕右下角:
点击 Diagnostics 图标。会出现一个覆盖层,显示 JavaScript 内存的详细信息,其中进一步细分显示 WASM 堆内存使用情况:
覆盖屏幕上显示以下诊断信息:
属性 (Property) | 功能 (Function) |
---|---|
Total JS Memory | JavaScript(JS)堆的当前大小,包括未分配给任何 JS 对象的未使用内存,以兆字节(MB)为单位。 |
Used JS Memory | JS 对象使用的内存,以兆字节(MB)为单位。 |
Total WASM heap memory | 表示 C/C++ Unity 引擎使用 Emscripten 编译的整个堆内存的线性内存,包括未分配的内存,以兆字节(MB)为单位。 |
Used WASM heap | 已分配的 WASM 堆空间,以兆字节(MB)为单位。 |
Important note about JS Memory
JS 内存信息是使用 performance.memory
API 获得的,目前仅支持 Chrome 或 Edge。没有其他 API 可返回此信息以供 Safari 或 Firefox 使用。
注意:iOS 设备不支持 performance.memory
API。
在不支持此 API 的浏览器上,会显示消息 “N/A”。
与浏览器脚本的交互
当你为 Web 构建内容时,可能需要与网页上的其他元素进行通信,或者使用 Web API 来实现 Unity 默认未提供的功能。
在这两种情况下,你都需要直接与浏览器的 JavaScript 引擎进行交互。Unity Web 提供了不同的方法来处理这些交互。
代码示例:在 Unity 中调用 JavaScript 和 C/C++/C# 函数
你可以使用代码在插件和 Unity 代码之间执行交互。以下示例展示了如何在 Unity 项目中从 JavaScript 和 C/C++/C# 代码调用各种类型的函数。
在 Unity C# 示例中调用 JavaScript 代码
以下代码是一个 JavaScript 示例,你的 Unity C# 脚本可以从中调用函数。
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]);
},
});
以下代码是一个 Unity C# 示例,调用了上述 JavaScript 示例中定义的函数。
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(1, 1, TextureFormat.ARGB32, false);
BindWebGLTexture(texture.GetNativeTexturePtr());
}
}
在 Unity C# 脚本中调用 C/C++/C# 代码示例
以下代码是一个 C++ 插件的示例,包含一些可以在 Unity 项目中调用的简单函数。
#include <stdio.h>
extern "C" void Hello ()
{
printf("Hello, world!\n");
}
extern "C" int AddNumbers (int x, int y)
{
return x + y;
}
然后,在你的 Unity 项目中使用以下 Unity C# 代码来调用这些 C++ 函数。
using UnityEngine;
using System.Runtime.InteropServices;
public class NewBehaviourScript : MonoBehaviour {
[DllImport("__Internal")]
private static extern void Hello();
[DllImport("__Internal")]
private static extern int AddNumbers(int x, int y);
void Start() {
Hello();
int result = AddNumbers(5, 7);
Debug.Log(result);
}
}
设置 JavaScript Plug-in
你可以在 Unity 项目中调用 JavaScript 插件中的函数。Unity 支持两种类型的 JavaScript plug-in file ,这些文件可以让你在 Unity 项目中添加 JavaScript 代码:
.jslib
.jspre
将你的 JavaScript 文件导入到 Unity 项目中
推荐的方式是将你的 JavaScript 源文件(.jspre 和 .jslib 文件)添加到你的项目中,然后在 Unity C# 脚本代码中直接使用这些函数或库。
将你的 JavaScript 插件文件放在 Assets/Plugin
文件夹中。
从 .jslib 文件类型中调用函数
你可以在 Unity C# 或 native 脚本中调用 .jslib 插件文件中的函数。
.jslib
文件类型使用 --js-library
Emscripten 模块。更多信息请参考 Emscripten 关于 [–js-library](https://emscripten.org/docs/api_reference/module.html#Module.jsLibraryFunc)
选项的文档.
以下代码示例展示了一个 .jslib 插件文件的理想语法,它定义了一些函数(Hello 和 HelloString)。
mergeInto(LibraryManager.library, {
Hello: function () {
window.alert("Hello, world!");
},
HelloString: function (str) {
window.alert(UTF8ToString(str));
},
});
然后,你可以在 Unity 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);
void Start() {
Hello();
HelloString("This is a string.");
}
}
有关 Unity C# 和 JavaScript 函数之间代码交互的完整示例,请参考 Code examples: Call JavaScript and C/C++/C# functions in Unity 。
有关 Unity C# 和 JavaScript 交互的更多信息,请参考 Call JavaScript functions from Unity C# scripts 。
使用 .jspre 文件类型包含 JavaScript 库
使用 .jspre 插件文件类型在你的 JavaScript 代码中包含现有的 JavaScript 库。你不能使用 Unity 代码与 .jspre 文件交互,但可以在 .jslib 代码中使用它们。
.jspre 文件类型使用 --pre-js
Emscripten 选项。更多信息请参考 Emscripten 关于 [–pre-js](https://emscripten.org/docs/api_reference/module.html#Module.preRun)
选项的文档.
在构建过程中,Emscripten 创建 *.framework.js
文件,并将 .jspre 文件的内容复制到 *.framework.js
文件的开头。这个过程很有用,因为所有代码都被合并到一个文件中,这样更容易管理文件,并且代码可以从 Emscripten 的优化中受益。
从 Unity C# 脚本调用 JavaScript 函数
你可以在 Unity C# 代码中使用 JavaScript 插件中的函数。在 Unity 中使用 JavaScript 代码可能很有用,因为你可能需要与网页上的其他元素或 Web API 进行通信。
要了解文件类型以及如何设置与 Unity 脚本交互的 JavaScript 插件,请参考 Set up your JavaScript plug-in 。要了解如何与 C/C++/C# 插件交互,请参考 Call C/C++/C# functions from Unity C# scripts 。
传递不同的变量从 JavaScript 到 Unity
为了集成 JavaScript 与 Unity,你需要在两者之间进行有效的通信。以下是一些关于如何在 JavaScript 和 Unity 之间传递各种类型数据的提示。
数值类型
你可以在函数参数中将简单的数值类型传递给 JavaScript,而无需转换。
例如,JavaScript 中的这个函数:
AddNumbers: function (x, y) { return x + y; },
接受从 C# 传递的整数值而无需转换:
int result = AddNumbers(5, 7);
你可以将其他数据类型作为指针传递到 Emscripten 堆中。Emscripten 堆只是 JavaScript 内存中的一个大数组。
字符串
要将字符串转换为 JavaScript 字符串,使用 UTF8ToString
辅助函数。
var stringMessage = UTF8ToString("Hello World");
要返回字符串值,请调用 _malloc
分配一些内存,并使用 stringToUTF8
辅助函数将 JavaScript 字符串写入内存。如果字符串是返回值,那么 IL2CPP 运行时会自动释放内存。
var returnStr = "Hello World";
var bufferSize = lengthBytesUTF8(returnStr) + 1;
var buffer = _malloc(bufferSize);
stringToUTF8(returnStr, buffer, bufferSize);
数组
对于基本类型的数组,Emscripten 提供不同的 ArrayBufferView
到其堆中,用于不同大小的整数、无符号整数或浮点表示的内存:HEAP8
,HEAPU8
,HEAP16
,HEAPU16
,HEAP32
,HEAPU32
,HEAPF32
,HEAPF64
。
以下函数遍历 HEAPF32
数组并输出每个索引处的值。
PrintFloatArray: function (array, size) {
for(var i = 0; i < size; i++)
console.log(HEAPF32[(array >> 2) + i]);
},
纹理
要在 WebGL 中访问纹理,Emscripten 提供 GL.textures
数组,该数组将 Unity 的本机纹理 ID 映射到 WebGL 纹理对象。你可以在 Emscripten 的 WebGL 上下文 GLctx
上调用 WebGL 函数。
例如,以下函数将 GL 纹理数组中的 WebGL 纹理绑定到 2D 纹理。
BindWebGLTexture: function (texture) {
GLctx.bindTexture(GLctx.TEXTURE_2D, GL.textures[texture]);
},
在自己的作用域中执行构建代码
建议你在自己的作用域中执行所有构建代码。如果你将代码放在自己的作用域中,你可以在页面上嵌入内容而不会与嵌入页面代码发生冲突,并且你可以在同一页面上嵌入多个构建。
.jslib 插件中的代码
如果你将所有 JavaScript 代码以 .jslib 插件的形式放入项目中,那么这些 JavaScript 代码将在与编译构建相同的作用域中运行,并且你的代码应像在以前的 Unity 版本中一样工作。JavaScript 插件代码中直接可见的一些对象和函数包括:* Module
* SendMessage
* HEAP8
* ccall
从全局作用域(Global Scope)调用 JavaScript 函数
如果你想从嵌入页面的全局作用域调用内部 JavaScript 函数,请在 Web 模板的 index.html
中使用 unityInstance
变量。
在 Unity 引擎实例化成功后使用 unityInstance
,例如:
var MyGameInstance = null;
script.onload = () => {
createUnityInstance(canvas, config, (progress) => { /*...*/ }).then((unityInstance) => {
MyGameInstance = unityInstance;
然后,你可以使用 MyGameInstance.SendMessage()
向构建发送消息,或者使用 MyGameInstance.Module
访问构建模块对象。
调用 JavaScript 函数的示例 Unity C# 代码
以下 JavaScript 代码创建了一个名为 Hello 的函数。对于此示例,将此代码用于 JavaScript 插件,以便你可以在 Unity C# 脚本中调用它。
mergeInto(LibraryManager.library, {
Hello: function () {
window.alert("Hello, world!");
},
});
然后,在 Unity 项目中使用此 C# 代码来调用 JavaScript 代码中的 Hello 函数:
using UnityEngine;
using System.Runtime.InteropServices;
public class NewBehaviourScript : MonoBehaviour {
[DllImport("__Internal")]
private static extern void Hello();
void Start() {
Hello();
}
}
使用 Unity 插件作为参考
在 Unity 安装文件夹中有几个插件,你可以用作参考:
PlaybackEngines/WebGLSupport/BuildTools/lib
PlaybackEngines/WebGLSupport/BuildTools/Emscripten/src/library*
从 JavaScript 调用 Unity C# 脚本函数
你可能希望从你的 JavaScript 插件或浏览器代码中调用一些 Unity 代码。例如,你可能希望某个 JavaScript UI 元素触发一个 Unity 行为,并需要访问该方法。
从浏览器的 JavaScript 向 Unity C# 脚本发送数据或通知的推荐方法是使用 SendMessage
函数调用 Unity 项目中 GameObject 上的方法。
使用 SendMessage 辅助函数
使用 SendMessage
从 JavaScript 代码调用 Unity 脚本 API 的 Unity 方法。
你只能调用 GameObject 的方法,不能调用附加到其他对象的通用 C# 方法。另外,仅在以下情况之一为真时使用 SendMessage
调用方法:
- 该方法不接受参数。
- 该方法有一个参数,且该参数是一个字符串。
- 该方法有一个参数,且该参数是一个数字。
具有多个参数或其他类型参数的方法不能使用 SendMessage
调用。
SendMessage 代码示例
要从嵌入项目的 JavaScript 插件中进行调用,使用以下代码:
MyGameInstance.SendMessage(objectName, methodName, value);
objectName
是场景中某个对象的名称。methodName
是当前附加到该对象的脚本中的方法名。value
可以是一个字符串、一个数字或为空。
以下代码是一个进一步的示例,展示了你可以使用不同参数调用的每种方法类型:
MyGameInstance.SendMessage('MyGameObject', 'MyFunction');
MyGameInstance.SendMessage('MyGameObject', 'MyFunction', 5);
MyGameInstance.SendMessage('MyGameObject', 'MyFunction', 'MyString');
要从嵌入页面的全局范围进行调用,请参阅 Call JavaScript functions from global scope 。
从 Unity C# 脚本调用 C/C++/C# 函数
你可以在 Unity 项目中调用你的 C、C++ 或 C# 插件中的函数。
Unity 使用 Emscripten 将 C/C++/C# 代码编译成 WebAssembly,因此你可以用 C/C++/C# 代码编写插件,并从 Unity C# 脚本中调用这些函数。
将你的 C/C++/C# 插件导入 Unity 项目
为了让 Unity 项目能够调用 C/C++/C# 插件代码中的函数,你需要将插件导入 Unity 项目。
在你的 Unity 项目中,将插件文件放入 Assets/Plugin
文件夹中。
在 Unity 中使用的 C++ 代码示例
如果使用 C++(.cpp)实现插件,你必须使用 C 链接(extern "C"
)声明函数,以避免名称修饰问题。以下代码是一个简单的 C++ 插件示例,其中包含可以在 Unity 项目中调用的函数。
#include <stdio.h>
extern "C" void Hello ()
{
printf("Hello, world!\n");
}
extern "C" int AddNumbers (int x, int y)
{
return x + y;
}
注意:Unity 使用 Emscripten 版本 2.0.19 工具链。
在你的 Unity 项目中,使用以下 Unity C# 代码来调用这些 C++ 函数。
using UnityEngine;
using System.Runtime.InteropServices;
public class NewBehaviourScript : MonoBehaviour {
[DllImport("__Internal")]
private static extern void Hello();
[DllImport("__Internal")]
private static extern int AddNumbers(int x, int y);
void Start() {
Hello();
int result = AddNumbers(5, 7);
Debug.Log(result);
}
}
将静态库编译为 Unity 插件
你可以使用 Emscripten 编译库并在 Unity 中使用这些库。相对于直接从插件目录调用 C++ 代码,调用封装 C++ 代码的库函数有以下几个好处:
- 你的代码更容易与 Web Unity 项目集成。
- 对于较大的库,调用库函数的性能可能会优于直接调用 C++ 代码。
- 你可以在不同项目中重用代码。
在 Unity 中使用静态库的步骤
要编译可用作 Unity 插件的静态库,请按照以下步骤操作:
下载 Emscripten SDK
你必须下载与 Unity 版本匹配的 Emscripten SDK。
要找到适合你 Unity 版本的 Emscripten 版本,请参阅适用于 Emscripten 的 Web native 插件。
有关下载链接和如何安装 Emscripten SDK 的更多信息,请参阅Emscripten 下载和安装。
配置现有项目脚本
Emscripten 作为 gcc 或 clang 编译器的替代品。如果你有现有的 C/C++ 代码,你需要在 C++ 项目中进行以下更改:
- 使用 “emcc” 作为 C/C++ 编译器
- 使用 “emar” 作为静态链接器
有关更多信息,请参阅Emscripten 文档中的编译和运行项目。
更新编译器选项
如果你的 Unity 项目具有以下 Player 设置(菜单:Edit > Project Settings > Player),你可能需要在 C/C++ 项目中添加一些编译器选项。
属性 | 描述 |
---|---|
Enable Exceptions | 如果此属性设置为 None,请添加编译器选项 “-fno-exceptions”。 |
Enable Native C/C++ Multithreading | 如果启用此属性,请包含编译器选项 “-pthread”。 |
Enable WebAssembly 2023 | 如果启用此属性,请包含以下编译器选项:“-fwasm-exceptions” “-sSUPPORT_LONGJMP=wasm” “-mbulk-memory” “-mnontrapping-fptoint” “-msimd128” “-msse4.2” |
编译并导入静态库文件
编译你的静态库文件,然后将这些文件导入到 Unity 项目的 Assets/Plugins
文件夹中。
注意:如果你没有 Plugins
文件夹,需要自己创建一个。
从 Unity C# 脚本调用静态库函数
你可以从 Unity C# 脚本调用静态库函数。调用函数的方式与从 C# 脚本调用 C 或 C++ 函数的方式相同。有关示例,请参阅 Call C/C++/C# functions from Unity C# scripts 。
在 Unity C#、JavaScript 和 C/C++/C# 代码之间创建回调
回调是 Web 开发的重要部分,因为它们可以让你的 Unity 项目更好地与 JavaScript 浏览器、JavaScript 插件和 C/C++/C# 插件通信。你可能需要在 Unity C# 代码中调用 C/C++/C# 或 JavaScript 代码中的函数,回调是实现这一目标的有效方法。
使用 makeDynCall
函数进行回调
使用 makeDynCall
从 JavaScript 代码调用 C、C++ 或 C# 的回调。例如,以下 JavaScript 代码调用名为 callback
的函数:
{{{ makeDynCall('vii', 'callback') }}}(1, 2)
'vii'
是参数的签名。例如,回调函数的返回类型是void
(v),且(1, 2)
都是整数 (ii)。'callback'
是回调函数的名称。(1, 2)
是传递给函数的参数。
以下代码是另一个示例,展示了如何使用 onresponse
函数进行回调,并传递一个浮点型 (f) 和多个整数类型的值:
{{{ makeDynCall('vfii', 'onresponse') }}}(2.8, arg, 0);
在脚本中使用回调
以下脚本展示了如何使用回调函数在 JavaScript、C++ 和 C# 代码之间进行通信的示例。
JavaScript 插件代码
以下代码是一个 .jslib
文件示例,它设置了一个超时并调用回调函数:
mergeInto(LibraryManager.library, {
JsSetTimeout: function (message, timeout, callback) {
// 创建消息的副本,因为在回调运行前消息可能会被删除
var stringMessage = UTF8ToString(message);
var buffer = stringToNewUTF8(stringMessage);
setTimeout(function () {
{{{ makeDynCall('vi', 'callback') }}} (buffer);
_free(buffer);
}, timeout);
}
});
C++ 插件代码
以下代码是一个 C++ 文件的示例,它调用外部 JavaScript 函数 (JsSetTimeout
) 并包含 JavaScript 在超时发生时调用的回调函数。
// CallbackExample.cpp
#include <stdio.h>
typedef void (*OnTimeOutCallback)(char* message);
extern "C" {
void JsSetTimeout(char* message, int timeout, OnTimeOutCallback callback);
}
void CCallback(char* message) {
printf("C 回调接收到 \"%s\"\n", message);
}
extern "C" void SetCTimeout() {
JsSetTimeout("Hello World", 1000, CCallback);
}
Unity C# 代码
以下代码是一个 Unity C# 代码示例,它接收来自 C++ 和 JavaScript 回调的消息。
using AOT;
using System;
using System.Text;
using System.Runtime.InteropServices;
using UnityEngine;
public class CallbackExample : MonoBehaviour {
void Awake() {
SetCSharpTimeout();
SetCTimeout();
}
void SetCSharpTimeout() {
JsSetTimeout("Hello World", 500, CSSharpCallback);
}
[DllImport("__Internal")]
public static extern void SetCTimeout();
[DllImport("__Internal")]
public static extern void JsSetTimeout(string message, int timeout, Action<string> action);
[MonoPInvokeCallback(typeof(Action<string>))]
public static void CSSharpCallback(string message) {
Debug.Log($"C# 回调接收到 \"{message}\"");
}
}
这些示例展示了如何在 Unity 项目中使用回调函数,实现 JavaScript、C++ 和 C# 代码之间的通信。这些回调方法使得跨语言的代码集成更加高效和简洁。
WebGL 原生插件与 Emscripten
Unity 使用 Emscripten 编译工具链将用 C 和 C++ 编写的 Unity 运行时代码交叉编译为 WebAssembly(也称为 Wasm)。Emscripten 生成小型代码,具有加载时间和内存效率,并旨在提供接近原生速度的执行。
Unity 使用 IL2CPP 将 C# 脚本中的 .NET 游戏代码转换为 Wasm。IL2CPP 将 .NET 字节码转换为相应的 C++ 源文件,然后使用 Emscripten 编译器将脚本转换为 Wasm。
目标版本
Unity 将 Emscripten 编译器与 WebGL 平台包捆绑在一起,因此无需手动下载。编辑器使用的 Emscripten 版本因编辑器版本而异:
Unity 版本 | Emscripten 版本 | 支持的原生插件文件格式 |
---|---|---|
Unity 2022.2 及以后 | Emscripten 3.1.8-unity | .a, .bc |
Unity 2021.2 及以后 | Emscripten 2.0.19.6-unity | .a, .bc |
Unity 2019.2 至 2021.2 | 1.38.11-unity | .bc |
Unity 2018.4 至 2019.2 | 1.37.40-unity | .bc |
Unity 2018.2 至 2018.4 | 1.37.33-unity | .bc |
说明:
以下列表提供了上述表中列出的文件格式的扩展名:
- GNU archive file format (.a)
- WebAssembly Object Files (.o)
- LLVM Bitcode file format (.bc)
在 Emscripten 2.0 之前的版本中,Unity 推荐构建 Bitcode 插件(文件类型 .bc)。从 Emscripten 2.0 开始,Unity 推荐构建 Wasm 对象文件插件,这些插件是类型为 .o 的 Wasm 对象文件,捆绑成类型为 .a 的 GNU 存档文件。
可以通过以下路径找到 emscripten-version.txt 文件以确定 Emscripten 工具链的确切版本:
C:\Program Files\Unity\Hub\Editor\<Editor version>\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emscripten-version.txt
使用 Emscripten 编译原生插件
如果您正在为 Unity 应用程序创建原生 C/C++ 插件,可以将 C/C++ 代码与项目捆绑在一起(参见 Native plug-ins),也可以使用 Emscripten 编译工具链预先将原生代码构建为插件存档。
如果选择预先构建插件代码,应使用 Emscripten 编译工具链。为了确保 LLVM 二进制格式的兼容性,用于编译插件的 Emscripten 版本必须与 Unity 使用的 Emscripten 版本匹配。
通常,插件会编译为静态库存档格式。在 Unity 2021.2 之前的版本中(Emscripten 2.0),LLVM Bitcode 文件格式 (.bc) 是首选的插件格式。从 Unity 2021.2 开始,首选的插件格式是包含 WebAssembly 对象文件 (.o) 的 GNU 存档文件格式 (.a)。尽管早期的 LLVM Bitcode .bc 插件文件仍受支持,但使用它们可能会对编译时间产生负面影响。
注意:
当从一个 Unity 版本迁移原生插件到使用不同版本 Emscripten 的版本时,Unity 建议从源代码重新编译 Unity 插件,因为 LLVM 编译器项目不保证跨编译器版本的构建工件文件的二进制兼容性。
Emscripten 构建标志
如果要编译用于 C/C++ 多线程的 WebAssembly 对象文件,请包含 Emscripten 构建标志 -pthread
。没有此标志,代码可能无法正确链接到使用 Unity 构建的代码。
Unity WebGL 中的内存
Unity WebGL 中的内存约束
Unity WebGL 中的内存约束可能会限制您运行内容的复杂性。
WebGL 内容在浏览器中运行。浏览器会在其内存空间中分配您的应用程序运行内容所需的内存。可用内存的数量取决于以下因素:
- 使用的设备
- 使用的操作系统
- 使用的浏览器,以及是否在 32 位或 64 位处理器上运行
- 浏览器的 JavaScript 引擎解析代码所需的内存量
- 浏览器是否为每个标签页使用单独的进程,或内容是否需要与所有其他打开的标签页共享内存空间
注意:有关与 WebGL 内存相关的安全风险的信息,请参阅安全和内存资源。
Unity Heap
Unity 使用内存堆来存储所有 Unity 引擎运行时对象,包括 managed 和 native 对象、加载的资源、场景和着色器。这类似于 Unity Player 在任何其他平台上使用的内存。
Unity 堆是一个连续分配的内存块。Unity 支持自动调整堆的大小以适应应用程序的需要。堆大小会随着应用程序运行而扩展,最多可以扩展到 2GB。Unity 将此内存堆创建为一个 Memory 对象。Memory 对象的 buffer 属性是一个可调整大小的 ArrayBuffer,用于保存由 WebAssembly 代码访问的 raw bytes 内存。
自动调整堆的大小可能会导致应用程序崩溃,如果浏览器无法在地址空间中分配一个连续的内存块。因此,重要的是尽可能保持 Unity 堆的大小尽可能小。因此,在规划应用程序的内存使用时要注意。如果您想测试 Unity 堆的大小,可以使用 Profiler 分析内存块的内容。
默认选项配置为适合所有桌面使用情况。然而,对于移动浏览器,您需要使用高级调优选项。对于移动浏览器,建议将初始内存大小(Initial Memory Size)配置为应用程序的典型堆使用量。
资源数据(Asset data)
当您创建 Unity WebGL build 时,Unity 会生成一个 .data
文件。该文件包含应用程序启动所需的所有场景和资源。由于 Unity WebGL 无法访问真实的文件系统,因此它会创建一个虚拟内存文件系统,并在浏览器中解压缩 .data 文件。Emscripten framework(JavaScript)在浏览器内存空间中分配此内存文件系统。在您的内容运行时,浏览器内存会保留未压缩的数据。为了保持下载时间和内存使用量尽可能低,请尽量保持此未压缩数据的体积尽可能小。
为了减少内存使用,可以将资源数据打包到 AssetBundles 中。AssetBundles 提供了对资源下载的完全控制。您可以控制应用程序何时下载资源,以及运行时何时卸载它。可以卸载未使用的资源以释放内存。
AssetBundles
直接下载到 Unity 堆中,因此这些不会导致浏览器的额外分配。
启用**数据缓存(Data Caching)**可以在用户的机器上自动缓存内容中的资源数据,这意味着在后续运行中无需重新下载这些数据。Unity WebGL 加载器使用 IndexedDB API 实现数据缓存。此选项允许您缓存过大而无法通过浏览器本地缓存缓存的文件。
数据缓存使浏览器能够在用户的机器上存储应用程序数据。浏览器通常限制您可以在其缓存中存储的量以及可以缓存的最大文件大小。通常,这不足以让应用程序顺利运行。Unity WebGL 加载器通过 IndexedDB API 进行缓存,这使 Unity 能够将数据存储在 IndexedDB 中而不是 browser cache 中。
要启用数据缓存选项,请转到 File > Build Settings > Player Settings > Publishing Settings。
垃圾回收
垃圾回收是定位并释放未使用内存的过程。有关 Unity 垃圾回收工作原理的概述,请参阅自动内存管理。要调试垃圾回收过程,请使用 Unity Profiler。
由于 WebAssembly 的安全限制,用户程序不允许检查 native 执行堆栈以防止可能的漏洞。
这意味着在 Web 平台上,GC 只能在没有 managed code 执行时运行(这些代码可能会引用 live C# 对象)。这发生在每个渲染的游戏帧结束时。
换句话说,在 Web 平台上,垃圾收集器不能在执行 C# 代码中间运行,只能在每个程序帧结束时运行。这种差异导致 Web 平台上的垃圾收集行为与其他平台相比有所不同。
由于这些差异,请密切注意每帧执行大量临时分配的代码,尤其是这些分配可能表现出线性大小增长序列的情况。这些分配可能会对垃圾收集器造成临时的二次内存增长压力。
例如,如果您有一个长时间运行的循环,以下代码在 Web 上运行时可能会失败,因为垃圾收集器不会在 for 循环的迭代之间运行。在这种情况下,垃圾收集器无法释放中间字符串对象使用的内存,并且会在 Unity 堆中耗尽内存。
string hugeString = "";
for (int i = 0; i < 100000; i++)
{
hugeString += "foo";
}
在上面的示例中,循环结束时 hugeString 的长度为 3 * 100000 = 300000 个字符。然而,代码在生成最终字符串之前生成了十万个临时字符串。整个循环过程中分配的总内存为 3 * (1 + 2 + 3 + … + 100000) = 3 * (100000 * 100001 / 2) = 15 GB。
在 native 平台上,垃圾收集器会在循环执行期间不断清理之前的临时字符串副本。因此,上述代码不需要 15 GB 的 RAM 才能运行。
在 Web 平台上,垃圾收集器不会在每帧结束时回收临时字符串副本。因此,上述代码在尝试分配 15 GB RAM 时会耗尽内存。
以下代码显示了另一个示例,在这种情况下,可能会出现这种类型的临时二次内存压力:
byte[] data;
for (int i = 0; i < 100000; i++)
{
data = new byte[i];
// do something temporary with data[]
}
在这里,代码临时分配了 1 + 2 + 3 + … + 100000 字节 = 5 GB 的字节,尽管只有最后的 100 KB 数组被保留。这导致程序在 Web 平台上似乎耗尽了内存,即使最终输出只需要 100 KB。
为了限制这些问题,避免执行二次增加临时内存分配的代码结构。相反,要么预先分配所需的数据大小,要么使用执行几何容量预留的 List<T>
或类似数据结构,以减轻临时内存压力。
例如,对于 List 容器,考虑使用 List<T>.ReserveCapacity()
函数,如果您知道数据结构的最终大小,它可以预先分配所需的容量。同样,考虑在缩小之前占用数兆字节内存的容器大小时使用 List<T>.TrimExcess()
函数。
注意:当您使用 C# 委托或事件(如 Delegate、Action、Func)时,这些类会在内部使用类似上述的线性增长分配。避免过多的每帧委托注册和注销,以最小化 Web 平台上垃圾收集器的临时内存压力。
WebGL 中的缓存行为
在 Unity WebGL 中,Cache API 允许您将缓存的 .data 文件和 AssetBundles 资源数据存储在浏览器缓存中。浏览器缓存的存储限制,例如最大文件大小、总体缓存大小以及淘汰标准,取决于您使用的浏览器和平台。有关更多信息,请参阅浏览器存储限制和淘汰标准。
数据缓存
要访问数据缓存,请从 File(文件) > Build Settings(构建设置) > Player Settings(播放器设置)中打开 WebGL 的发布设置。这使浏览器能够将主数据文件缓存到 IndexedDB 数据库中。
使用默认的浏览器 HTTP 缓存不能保证浏览器缓存特定的响应。这是因为浏览器 HTTP 缓存的空间有限,浏览器可能无法缓存过大的文件。
为了提高加载速度,IndexedDB 允许您缓存超过浏览器限制的文件。当您缓存更多文件时,在下次运行构建时,用户计算机上可用的已下载内容的机会就会增加。
数据缓存仅在 IndexedDB 缓存中缓存 HTTP 响应的 .data 文件。要缓存 AssetBundles,您需要启用数据缓存并覆盖 unityInstance.Module.cacheControl()
。为此,请确保 Module.cacheControl(url)
返回所请求的 AssetBundle URL 的 must-revalidate
。例如,您可以在 createUnityInstance()
返回的 Promise 的完成回调中覆盖 unityInstance.Module.cacheControl()
函数。有关 createUnityInstance()
的详细信息,请参阅 Compressed builds and server configuration 。
自定义 WebGL 缓存行为
默认情况下,WebGL 缓存存储 .data 和 .bundle 文件,并在从缓存加载前重新验证它们。您可以通过添加新的 WebGL Template 来更改 UnityLoader 配置,从而更改此行为。
以下示例显示了在 index.html
文件中向 UnityLoader 配置添加自定义 cacheControl
函数:
var config = {
// ...
#if USE_DATA_CACHING
cacheControl: function (url) {
// 为 .data 和 .bundle 文件启用缓存。
// 在从缓存加载前重新验证文件是否为最新版本
if (url.match(/\.data/) || url.match(/\.bundle/)) {
return "must-revalidate";
}
// 为 .mp4 和 .custom 文件启用缓存
// 从缓存加载文件而不进行重新验证。
if (url.match(/\.mp4/) || url.match(/\.custom/)) {
return "immutable";
}
// 对所有其他文件禁用显式缓存。
// 注意:默认的浏览器缓存可能仍会缓存它们。
return "no-store";
},
#endif
// ...
}
cacheControl
函数将请求的 URL 作为参数,并返回以下之一:
must-revalidate
:如果函数返回must-revalidate
,则缓存返回启用状态,并在从缓存加载前重新验证文件。immutable
:如果函数返回immutable
,则缓存启用,并从缓存中加载文件而不重新验证。no-store
:如果函数返回no-store
,则缓存禁用。
浏览器会自动存储(缓存)某些文件类型,例如 .html、.js、.css、.json、.jpg、.png,因此无需在 WebGL 缓存中显式存储它们。WebGL 缓存的典型候选者包括大型文件和使用自定义文件格式的文件。
WebGL 图形
WebGL 是一个用于在网页浏览器中渲染图形的 API,基于 OpenGL ES 图形库的功能。WebGL 1.0 大致相当于 OpenGL ES 2.0 的功能,而 WebGL 2.0 大致相当于 OpenGL ES 3.0 的功能。
相机清除(Camera clear)
在 Unity WebGL 中,无论 Camera.clearFlags 设置如何,帧缓冲区(frame buffer)的内容都会被清除。然而,您可以在实例化时更改此行为。为此,请在 WebGL 模板的 index.html 文件中设置 webglContextAttributes.preserveDrawingBuffer 为 true
。
注意:如果设置任何 WebGL context attributes ,还必须添加一行以保留 Power Preference Player setting 。
script.onload = () => {
config['webglContextAttributes'] = {
preserveDrawingBuffer: true, // 添加此行以保留 Camera.clearFlags 设置
powerPreference: {{{ WEBGL_POWER_PREFERENCE }}} // 添加此行以保留 Power Preference Player 设置
};
createUnityInstance(canvas, config, (progress) => {
延迟渲染
Unity WebGL 仅在 WebGL2.0 可用时支持延迟渲染路径(Deferred Rendering Path)。在 WebGL 1.0 上,Unity WebGL 运行时会回退到前向渲染(Forward Rendering)。
全局照明
Unity WebGL 仅支持烘焙全局照明(baked GI)。目前,WebGL 不支持实时全局照明(Realtime Global Illumination)。此外,Unity WebGL 仅支持非定向光照贴图(Non-Directional lightmaps)。
线性渲染(Linear rendering)
Unity WebGL 仅在 WebGL 2.0 上支持线性色彩空间渲染( linear color space rendering)。线性色彩空间渲染不支持 WebGL 1.0 回退。要使用线性色彩空间渲染构建 WebGL player,需要在 Player 设置中移除 WebGL 1.0 API,打开 Other Settings 面板,禁用 Automatic Graphics API 设置。
某些网页浏览器不支持 sRGB DXT texture compression 。这可能会降低使用线性渲染时的渲染性能,因为需要在运行时解压所有的 DXT 纹理。
视频剪辑导入器(Video clip importer)
您不能使用 VideoClipImporter 将视频剪辑导入到 Unity 项目中,因为这可能会增加初始资产数据的下载大小并防止网络流媒体播放。对于视频播放,请使用 VideoPlayer Component 中的 URL 选项,并将资产放在 StreamingAssets/ 目录中以使用浏览器的内置网络流媒体播放功能。
WebGL 着色器代码限制
WebGL 1.0 规范对 GLSL 着色器代码施加了一些限制,这些限制比大多数 OpenGL ES 2.0 实现更严格。这在您编写自己的着色器时尤为重要。
WebGL 对用于索引数组或矩阵的值有特定的限制。例如,WebGL 仅允许使用常量表达式、循环索引或两者结合进行动态索引,除了在顶点着色器中的 uniform access 可以使用任何表达式进行索引外。
WebGL 1.0 说明:WebGL 1.0 对控制结构施加了额外的限制,不允许使用 while 循环和大多数类型的 for 循环。然而,允许使用计数 for 循环,其中字段初始化器将变量设置为常量,更新添加常量或从变量中减去常量,继续测试将变量与常量进行比较。
注意:由于 WebGL 中可用内存有限,应避免包含不需要的着色器变体(shader variants),这会导致不必要的内存使用。因此,Unity 建议熟悉着色器变体和着色器剥离(shader stripping),并特别注意确保不要将具有过多变体的着色器(例如 Unity 的 Standard Shader)添加到 Graphics Settings 中的 [Always-included Shaders] 部分。
字体渲染
Unity WebGL 支持类似于其他 Unity 平台的动态字体渲染。然而,由于不能访问用户机器上安装的字体,如果要使用任何字体,请确保将它们包含在项目文件夹中(包括任何国际字符的备用字体,或字体的粗体/斜体版本),并设置为备用字体名称(set as fallback font names)。
抗锯齿(Anti-aliasing)
WebGL 在大多数(但不是全部)浏览器和 GPU 组合上支持抗锯齿。要使用它,必须在 WebGL 平台的默认质量设置(default Quality setting)中启用抗锯齿。
反射探针(Relection Probe)
Unity WebGL 支持所有反射探针。注意:WebGL 1.0 不支持平滑的实时反射探针。
WebGL 2.0 支持
Unity 支持 WebGL 2.0 API,这为网页带来了 OpenGL ES 3.0 级别的渲染功能。
默认情况下,Unity WebGL 构建支持 WebGL 2.0 API。您可以在 WebGL Player 设置 > Other Settings 面板中通过禁用 Automatic Graphics API
属性并添加 WebGL 1.0 API 来配置此设置。
支持 WebGL 2.0 的浏览器具有以下优势:
- 标准着色器(Standard Shader )中的内容质量更高。
- 支持 GPU Instancing 和方向光贴图(directional lightmap)。
- 着色器代码中的索引和循环没有限制。
- 更好的性能。
您可以在运行时使用 SystemInfo.graphicsDeviceType 确定 Unity 实例是否使用 OpenGLES3(WebGL2.0)或 OpenGLES2(WebGL1.0)进行渲染。
WebGL 中的音频
此页面仅提供有关 WebGL 中音频功能的信息。要了解如何在 Unity 项目中使用音频,请参阅 音频概述。
由于 Unity 使用 FMOD 来管理平台的音频,Unity WebGL 仅支持有限的音频功能,仅包括基本功能。FMOD 依赖于线程,而 WebGL 不支持线程。因此,Unity 使用基于内部 Web Audio API 的实现,这使浏览器能够处理音频播放和混音。
注意:Google Chrome 的新自动播放策略在某些情况下会阻止音频和视频的自动播放。例如,虽然您的游戏可能设置为在加载后自动播放一些背景音乐,但除非您点击或轻击网站,否则它不会自动播放。有关如何启用或禁用此策略的更多信息,请参阅 Google Chrome 的自动播放策略文档。
支持的类
Unity WebGL 支持以下 API 类:
类 | WebGL 支持状态 |
---|---|
AudioSource | WebGL 支持一些 API。请参阅 AudioSource 的具体支持详情。 |
AudioListener | 所有 API 均受支持。 |
AudioClip | WebGL 支持一些 API。请参阅 AudioClip 的具体支持详情。 |
SystemInfo.supportsAudio | 浏览器为 WebGL 提供音频支持。因此,SystemInfo.supportsAudio 始终为 true。 |
Microphone | 不支持。 |
AudioSource
AudioSource API 支持基本的定位音频播放,包括:
- 暂停和恢复
- 平移
- 衰减
- 音高设置
- 多普勒效应支持
Unity WebGL 支持以下 AudioSource API:
设置 | 描述 |
---|---|
Clip | 确定下一个播放的音频片段。 |
dopplerLevel | 设置 AudioSource 的多普勒缩放。 |
ignoreListenerPause | 允许 AudioSource 忽略 AudioListener.pause 并继续播放音频。 |
ignoreListenerVolume | 忽略最终用户的 AudioSource 音量。 |
isPlaying | 如果 AudioSource.clip 正在播放,则返回 true。 |
loop | 允许应用程序循环播放 AudioSource.clip。 |
maxDistance | 设置 AudioSource.clip 停止衰减或变得不可听的最大距离。 |
minDistance | 设置 AudioSource.clip 不再增加音量的最小距离。声音在最小距离之外开始衰减。 |
mute | 静音 AudioSource。 |
pitch | 设置 AudioSource.clip 的音高。WebGL 仅支持正音高值。 |
playOnAwake | 在 Awake 时播放 AudioSource。 |
rolloffMode | 设置 AudioSource 的距离衰减模式。 |
time | 设置播放位置(秒)。 |
timeSamples | 设置播放位置(脉冲编码调制(PCM)样本)。 |
velocityUpdateMode | 设置 AudioSource 是在固定还是动态更新循环中更新。 |
volume | 设置 AudioSource 的音量(0.0 到 1.0)。 |
Pause | 暂停 AudioSource.clip。 |
Play | 播放 AudioSource.clip。 |
PlayDelayed | 播放 AudioSource.clip 并指定延迟时间(秒)。 |
PlayOneShot | 播放 AudioClip 并按 volumeScale 缩放 AudioSource 音量。 |
PlayScheduled | 在指定时间播放 AudioSource。 |
SetScheduledEndTime | 设置计划的 AudioSource.clip 结束时间。 |
SetScheduledStartTime | 设置计划的 AudioSource.clip 开始时间。 |
Stop | 停止播放 AudioSource.clip。 |
UnPause | 取消暂停的 AudioSource.clip。 |
PlayClipAtPoint | 在世界空间中的给定位置播放 AudioSource.clip。 |
AudioClip
Unity WebGL 以 AAC 格式导入 AudioClip 文件,该格式大多数浏览器都支持。Unity WebGL 支持以下 AudioClip API:
属性 | 描述 |
---|---|
length | AudioClip 的长度(秒)。 |
loadState | 返回与 AudioClip 关联的音频数据的当前加载状态。 |
samples | AudioClip 的长度(样本)。 |
loadType | 音频片段的加载类型。您可以在 Inspector 中设置 AudioClip 加载类型。 |
方法 | 描述 |
— | — |
AudioClip.Create | 使用指定名称和长度创建 AudioClip。 |
AudioClip.SetData | 在 AudioSource.clip 中设置样本数据。 |
注意:对于 Linux 上的音频片段支持,请确保您已安装 ffmpeg 包。 |
压缩音频
要在 Unity 中使用 WebGL 压缩音频,请将 AudioClip loadType 设置为以下选项之一:
压缩方法 | 描述 | 注意事项 |
---|---|---|
CompressedInMemory | 将音频压缩到磁盘并在加载到应用程序内存后保持压缩状态。 | 压缩音频可能会导致延迟,并且在音频播放时不太精确。然而,压缩音频使用的内存比解压音频少。对于不受精度影响的音频(例如背景音乐),建议使用 CompressedInMemory。 |
DecompressOnLoad | 将音频压缩到磁盘,与 CompressedInMemory 类似,并在加载到应用程序内存时解压。 | 解压音频使用的内存比压缩音频多,但具有较低的延迟和更高的音频灵活性。对于受精度影响的音频(例如角色对话或声音效果),建议使用 DecompressOnLoad。 |
音频播放和浏览器安全
出于安全原因,浏览器在用户通过鼠标点击、触摸事件或按键与您的应用程序网页交互之前,不允许音频播放。使用加载屏幕允许用户与您的应用程序交互并在主要内容开始之前启动音频播放。
WebGL 中的纹理压缩
在 WebGL 中使用纹理压缩可以创建针对不同平台的构建,这些平台基于它们支持的纹理压缩格式。
纹理压缩格式
桌面和移动设备支持不同的纹理压缩格式。如果你希望你的 WebGL 应用程序在这两种类型的浏览器上都使用压缩纹理,首先需要选择一种支持的纹理压缩格式。
要在桌面和移动浏览器上使用压缩纹理运行游戏,你可能需要创建两个构建,分别针对:
- 使用 DXT 作为纹理压缩格式的桌面浏览器。
- 使用可自适应可伸缩纹理压缩(ASTC)作为纹理压缩格式的移动浏览器。
压缩格式设置的优先级
你可以从 WebGL Build Settings 窗口或 WebGL Player Settings 窗口设置 WebGL 应用程序的默认纹理压缩格式。在设置纹理压缩格式之前,重要的是要决定这些设置中的哪一个具有优先级。Build Settings 中设置的纹理压缩格式值优先于 Player Settings 中设置的值。默认情况下,Unity 编辑器将 Build Settings 的值设置为使用 Player Settings。
请注意,编辑器会在 Library 文件夹中序列化 Build Settings 中的纹理压缩格式,这意味着它不受版本控制管理。
你还可以为单个纹理自定义纹理压缩格式。为单个纹理设置的值将覆盖默认的纹理压缩格式值。有关如何更改单个纹理格式的信息,请参阅纹理导入设置。
设置默认压缩格式
你可以使用 Build Settings 或 Player Settings 设置 WebGL 应用程序的默认纹理压缩格式。Build Settings 中设置的纹理压缩格式值优先于 Player Settings 中设置的值。默认情况下,Unity 编辑器将 Build Settings 的值设置为使用 Player Settings。
使用 Build Settings 选择默认纹理压缩格式的步骤:
- 选择 File > Build Settings。
- 从 Platform 窗格中的平台列表中选择 Web。
- 从 Texture Compression 下拉菜单中选择一种压缩格式。
使用 Player Settings 选择默认纹理压缩格式的步骤:
- 选择 File > Build Settings。
- 从 Platform 窗格中的平台列表中选择 Web。
- 选择 Player Settings > Other Settings。
- 从 Texture compression format 下拉菜单中选择一种压缩格式。
有关如何同时为桌面浏览器和移动浏览器创建带有相应纹理压缩格式的构建的示例,请参阅通过脚本为桌面和移动浏览器创建构建。
通过脚本为桌面和移动浏览器创建构建
你可以使用脚本同时为桌面浏览器和移动浏览器运行具有相应纹理压缩格式的构建。例如:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Diagnostics;
using System.IO;
using UnityEditor.Build.Reporting;
public class comboBuild
{
// 这会创建一个菜单项来触发双重构建
[MenuItem("Game Build Menu/Dual Build")]
public static void BuildGame()
{
// 这会构建两次:一次具有桌面特定的纹理设置 (WebGL_Build),以及一次具有移动特定的纹理设置 (WebGL_Mobile),并将必要的文件合并到一个目录 (WebGL_Build) 中
string dualBuildPath = "WebGLBuilds";
string desktopBuildName = "WebGL_Build";
string mobileBuildName = "WebGL_Mobile";
string desktopPath = Path.Combine(dualBuildPath, desktopBuildName);
string mobilePath = Path.Combine(dualBuildPath, mobileBuildName);
string[] scenes = new string[] {"Assets/scene.unity"};
EditorUserBuildSettings.webGLBuildSubtarget = WebGLTextureSubtarget.DXT;
BuildPipeline.BuildPlayer(scenes, desktopPath, BuildTarget.WebGL, BuildOptions.Development);
EditorUserBuildSettings.webGLBuildSubtarget = WebGLTextureSubtarget.ASTC;
BuildPipeline.BuildPlayer(scenes, mobilePath, BuildTarget.WebGL, BuildOptions.Development);
// 将 mobile.data 文件复制到桌面构建目录以将它们合并
FileUtil.CopyFileOrDirectory(Path.Combine(mobilePath, "Build", mobileBuildName + ".data"), Path.Combine(desktopPath, "Build", mobileBuildName + ".data"));
}
你可以修改 WebGL 模板的 index.html 文件来选择适当的数据文件(如果支持纹理压缩格式扩展):
// 根据是否支持 ASTC 纹理压缩格式选择数据文件
var dataFile = "/{{{ DATA_FILENAME }}}";
var c = document.createElement("canvas");
var gl = c.getContext("webgl");
var gl2 = c.getContext("webgl2");
if ((gl && gl.getExtension('WEBGL_compressed_texture_astc')) || (gl2 &&
gl2.getExtension('WEBGL_compressed_texture_astc'))) {
dataFile = "/WebGL_Mobile.data";
}
var buildUrl = "Build";
var loaderUrl = buildUrl + "/{{{ LOADER_FILENAME }}}";
var config = {
dataUrl: buildUrl + dataFile,
frameworkUrl: buildUrl + "/{{{ FRAMEWORK_FILENAME }}}",
#if USE_WASM
codeUrl: buildUrl + "/{{{ CODE_FILENAME }}}",
#endif
#if MEMORY_FILENAME
memoryUrl: buildUrl + "/{{{ MEMORY_FILENAME }}}",
#endif
#if SYMBOLS_FILENAME
symbolsUrl: buildUrl + "/{{{ SYMBOLS_FILENAME }}}",
#endif
streamingAssetsUrl: "StreamingAssets",
companyName: {{{ JSON.stringify(COMPANY_NAME) }}},
productName: {{{ JSON.stringify(PRODUCT_NAME) }}},
productVersion: {{{ JSON.stringify(PRODUCT_VERSION) }}},
showBanner: unityShowBanner,
};
WebGL 中的嵌入式资源 (Embedded resources in WebGL)
.NET 程序集可以包含嵌入式资源。嵌入式资源是一种二进制数据集合,它是 .NET 程序集的一部分。你可以通过类似文件的 API 在代码中访问这些二进制数据。
默认情况下,WebGL 构建不包含嵌入式资源。这有助于降低最终二进制文件的大小,因为嵌入式资源可能相当大。然而,一些用户代码和 .NET 类库 API 依赖于嵌入式资源的存在才能正常工作。例如,使用 StringComparison.InvariantCultureIgnoreCase
值进行的字符串比较在比较非 ASCII 字符时使用嵌入式资源。
你可以在 Unity 编辑器中使用以下脚本为 WebGL 构建启用嵌入式资源:
using UnityEditor;
public class WebGLEditorScript
{
[MenuItem("WebGL/Enable Embedded Resources")]
public static void EnableEmbeddedResources()
{
PlayerSettings.WebGL.useEmbeddedResources = true;
}
}
启用此玩家设置后,WebGL 构建将包含项目中使用的任何 .NET 程序集中的嵌入式资源。
WebGL 中的嵌入式资源 (Embedded resources in WebGL)
.NET 程序集可以包含嵌入式资源。嵌入式资源是一种二进制数据集合,它是 .NET 程序集的一部分。你可以通过类似文件的 API 在代码中访问这些二进制数据。
默认情况下,WebGL 构建不包含嵌入式资源。这有助于降低最终二进制文件的大小,因为嵌入式资源可能相当大。然而,一些用户代码和 .NET 类库 API 依赖于嵌入式资源的存在才能正常工作。例如,使用 StringComparison.InvariantCultureIgnoreCase
值进行的字符串比较在比较非 ASCII 字符时使用嵌入式资源。
你可以在 Unity 编辑器中使用以下脚本为 WebGL 构建启用嵌入式资源:
using UnityEditor;
public class WebGLEditorScript
{
[MenuItem("WebGL/Enable Embedded Resources")]
public static void EnableEmbeddedResources()
{
PlayerSettings.WebGL.useEmbeddedResources = true;
}
}
启用此玩家设置后,WebGL 构建将包含项目中使用的任何 .NET 程序集中的嵌入式资源。
WebGL 中的输入
Unity WebGL 支持来自不同设备的各种输入,包括游戏手柄、操纵杆、触摸屏、键盘和运动传感器。
游戏手柄和操纵杆支持
WebGL 支持以下游戏手柄输入:
- Input
- InputSystem
WebGL 还支持支持 HTML5 Gamepad API 的浏览器中的操纵杆。
某些浏览器仅在用户与设备交互且应用程序在焦点状态下时才允许访问输入设备。这种类型的安全措施防止用户使用连接的设备进行浏览器指纹识别。因此,请确保你的应用程序指示用户在调用 Input.GetJoystickNames() 以检查连接的设备之前按下游戏手柄/操纵杆上的按钮。
控制器映射
旧输入系统的 WebGL 游戏控制器映射与 W3 spec 一致,其中按钮映射布局如下:
buttons[0]
:右侧集群(right cluster)的底部按钮 = CROSS (X)buttons[1]
:右侧集群的右侧按钮 = CIRCLEbuttons[2]
:右侧集群的左侧按钮 = SQUAREbuttons[3]
:右侧集群的顶部按钮 = TRIANGLE
注意:由于 WebGL 遵循 W3 规范,因此它可能与其他平台不一致,如果你同时针对多个平台(例如 Windows 和 Web),则需要进行独特的处理。
触摸支持
Unity WebGL 尚未正式支持移动设备(参见 WebGL WebGL browser compatibility ),但它在支持触摸的浏览器和设备中实现了 Input.touches 和其他相关 API。默认情况下,移动设备会在触摸屏上显示一个软键盘,用于在 UI 输入字段中输入文本。要禁用此行为,请使用 [WebGLInput.mobileKeyboardSupport](https://docs.unity3d.com/2022.3/Documentation/ScriptReference/WebGLInput-mobileKeyboardSupport.html)
属性。
键盘输入和焦点处理
默认情况下,Unity WebGL 处理网页接收到的所有键盘输入,无论 WebGL 画布是否具有焦点。这允许最终用户在无需点击 WebGL 画布的情况下使用基于键盘的应用程序。
以下注意事项适用于特定的键盘输入行为:
- HTML 元素(如文本字段):如果在网页中引入 HTML 元素(如文本字段)以接收键盘输入,可能会导致错误。Unity 会在页面的其他部分接收输入事件之前消费这些输入事件。要使 HTML 元素接收键盘输入,请将
WebGLInput.captureAllKeyboardInput
设置为false
。这样,应用程序仅在 WebGL 画布具有焦点时才会接收输入。 - Esc 键:一些浏览器(如 Safari)阻止使用 Esc 键切换到全屏模式,因为 Esc 键在 Web 环境中受到保护。其他浏览器(如 Google Chrome)可能表现得不可预测。当浏览器处于全屏模式时,用户只能使用 Esc 键退出全屏模式。根据浏览器类型,按 Esc 键不一定总能触发事件从浏览器转发到应用程序。
- HideMobileInput:TextMeshPro(也称为 TMP)输入字段中的
HideMobileInput
选项在 WebGL 平台上无效。此选项无效,因为你需要一个文本输入字段来触发浏览器中的虚拟键盘。当 Unity 玩家期望文本输入时,WebGL 平台会实例化一个文本输入字段来触发虚拟键盘。该文本输入字段显示在虚拟键盘上方,类似于其他平台。由于虚拟键盘依赖于移动文本输入字段,因此在 WebGL 平台上无法隐藏它。
移动传感器支持
Unity WebGL 尚未正式支持移动设备(参见 WebGL 浏览器兼容性),但对于支持触摸的浏览器和移动设备,Unity WebGL 支持以下传感器:
- 使用 Input.acceleration 的加速度计
- 使用
Gyroscope.userAcceleration
的线性加速度计 - 使用
Gyroscope.rotationRate
的陀螺仪 - 使用
Gyroscope.gravity
的重力 - 使用
Gyroscope.attitude
的姿态 - Input System 包也支持这些传感器。
重要提示:浏览器仅允许在安全上下文中使用传感器输入。这意味着你必须通过 HTTPS 提供页面。唯一的例外是 http://localhost
,你可以在开发期间使用。
光标锁定支持
Unity WebGL 平台支持光标锁定,使用 HTML5 API Element.requestPointerLock
。使用光标锁定可以将鼠标光标锁定在游戏窗口的中心。当光标被锁定时,它在 Unity 中看起来是隐藏的,并且在鼠标移动时不会移动。这对于第一人称游戏特别有用,在这种游戏中,通常使用鼠标光标来控制玩家的视角。
注意:由于浏览器对光标锁定的支持有所不同,请参阅 Mozilla 文档中的 Element: requestPointerLock() method 方法。
启用光标锁定
要锁定光标,请使用 [Cursor.lockState](https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Cursor-lockState.html)
属性。例如,以下代码在用户点击鼠标左键时将光标切换到锁定状态:
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Cursor.lockState = CursorLockMode.Locked;
}
}
光标锁定需要通过用户交互来激活。有关更多信息,请参阅 Additional considerations for full-screen mode and cursor locking 。
取消光标锁定
按下 Esc 键即可解锁光标。
粘性光标锁定
stickyCursorLock
属性通常用于第一人称游戏,因为它有助于在浏览器行为中保持光标锁定模式。
使用 [stickyCursorLock](https://docs.unity3d.com/2022.3/Documentation/ScriptReference/WebGLInput-stickyCursorLock.html)
确保 Cursor.lockState
的状态是持久的,即使浏览器从 Unity 画布中释放光标锁定(通常使用 Esc 键),在下次画布获得焦点时光标仍然会被锁定。
因此,如果你将 WebGLInput._stickyCursorLock
设置为 true
,即使 Unity 画布 HTML 元素解锁光标,Cursor.lockState
仍会保持在 CursorLockMode.Locked
状态。
如果你将 WebGLInput._stickyCursorLock
设置为 false
,则会发生以下情况:
Cursor.lockState
与浏览器的光标锁定状态保持同步。- 如果用户按下 Esc 键以取消画布光标锁定,
Cursor.lockState
将更改为CursorLockMode.None
。
注意:在 WebGL 中,stickyCursorLock
默认为 true
。
全屏模式
在游戏中使用全屏模式可以:
- 使用整个屏幕进行游戏。
- 隐藏浏览器用户界面元素,例如地址栏和标签。
- 隐藏 Unity 玩家用户界面元素,例如标题栏和工具栏。
Unity WebGL 平台支持使用 HTML5 API Element.requestFullscreen
的全屏模式。
注意:由于浏览器对全屏模式的支持有所不同,请参阅 Mozilla 文档中的 Element: requestFullscreen() method 。
在 Web 中启用全屏模式
要启用全屏模式,请使用 Screen.fullScreen
属性。例如,以下代码在用户按下 F
键时将游戏切换到全屏模式:
void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
Screen.fullScreen = true;
}
}
注意:Screen.fullScreen
属性默认为 false
。
全屏模式需要通过用户交互来激活。有关更多信息,请参阅全屏模式和光标锁定的附加注意事项。
退出全屏模式
要退出全屏模式,再次按下 Esc 键,或将鼠标指针悬停到屏幕顶部以访问地址栏和标签。
全屏模式和光标锁定的附加注意事项
出于安全考虑,浏览器仅允许在用户发起的事件(如鼠标点击或按键)之后锁定光标和启用全屏模式。
由于 Unity 不支持单独的事件和渲染循环,因此它会延迟事件处理,直到浏览器不再承认从 Unity 脚本发出的全屏或光标锁定请求作为事件的直接响应。
因此,Unity 会在下一个用户发起的事件上触发请求,而不是触发光标锁定或全屏请求的事件。
要更好地启用光标锁定或全屏模式,请使用鼠标/按键按下事件来触发响应,而不是鼠标/按键松开事件。这样可以确保延迟的请求可以由相应的鼠标/按键松开事件触发,如果不是由用户发起的事件提前触发。
你还可以使用 Unity 的 UI.Button
组件来实现所需的行为,通过创建 Button
的子类来覆盖 OnPointerDown
方法。
注意:某些浏览器可能会显示通知消息或提示用户在进入全屏模式或锁定光标之前授予权限。
配置 WebGL 画布大小
在 WebGL 应用程序中,canvas 元素是浏览器在渲染游戏时绘制图形的地方。本节描述了如何配置 WebGL 画布元素的可见大小以及游戏渲染的分辨率。
要配置 WebGL 画布大小,你需要考虑以下类型的画布大小:
画布大小元素
元素 | 描述 |
---|---|
画布元素 CSS 大小 | 这个 document 对象模型(DOM)指定了网页上画布所占用的可见区域。你可以使用 HTML 或 JavaScript 配置画布大小。 |
WebGL 渲染目标大小 | 此大小指定 GPU 渲染游戏的像素分辨率。你可以将此大小与 CSS 大小同步以提供像素完美渲染,或者将其与 CSS 大小解耦。 |
如果以上两个画布大小元素不匹配,浏览器将拉伸渲染的 WebGL 输出以填充网页上的可见画布区域。 |
解耦渲染分辨率
在高 DPI(每英寸点数)显示器上实现缩小比例的低 DPI 渲染时,解耦渲染分辨率非常有用。这不仅有助于节省 GPU 填充率功率,还能帮助你的应用程序在对渲染分辨率敏感时正常运行。例如,如果你的应用程序逻辑使用屏幕空间像素单位,但应用程序本身需要特定的分辨率才能正常工作。
默认情况下,Unity 会保持画布元素的 CSS 大小和 WebGL 渲染目标大小同步,并提供 1:1 像素完美渲染。如果网页中的 JavaScript 修改了画布 CSS 大小,Unity 将自动调整 WebGL 渲染目标大小以匹配它。默认情况下,此匹配是为了实现高 DPI 渲染。
覆盖 DPI 缩放
如果你想覆盖 DPI 缩放比例,请打开 index.html
文件并添加 Unity 实例配置变量 devicePixelRatio=<double>
。例如,在 WebGL 站点模板页面上设置 devicePixelRatio=1
将强制 Unity 始终应用低 DPI 渲染。请参阅使用 WebGL 模板示例。
手动更改 WebGL 画布渲染大小
要手动配置画布大小,你必须首先禁用自动大小同步。为此,请在 WebGL 模板的 index.html
文件中将 Unity 实例配置变量设置为 false
:matchWebGLToCanvasSize=false
。设置后,Unity 将保持画布的渲染目标大小不变,但如果需要,你始终可以配置其大小。
例如,要更改画布的 CSS 大小,请编辑 index.html
文件中的以下文本:
<div id="unity-container" style="position: absolute; width: 100%; height: 100%">
<canvas id="unity-canvas" width={{{ WIDTH }}} height={{{ HEIGHT }}} style="width: 100%; height: 100%"></canvas>
</div>
这样可以手动设置画布的 CSS 大小和渲染目标大小,以满足你的特定需求。
WebGL 浏览器访问设备功能
Unity WebGL 支持 WebCam access 。为了允许 WebGL 应用程序访问设备上的摄像头,浏览器必须请求用户提供访问摄像头的权限。如果没有访问摄像头的权限,浏览器将返回不完整或不准确的信息。
注意:当前,Unity WebGL 仅支持 WebCam 设备。
要请求浏览器访问摄像头的权限,请使用 Application.RequestUserAuthorization
API:
using UnityEngine;
using System.Collections;
// 从浏览器获取 WebCam 信息
public class ExampleClass : MonoBehaviour
{
private WebCamDevice[] devices;
// 初始化
IEnumerator Start()
{
yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
if (Application.HasUserAuthorization(UserAuthorization.WebCam))
{
Debug.Log("webcam found");
devices = WebCamTexture.devices;
for (int cameraIndex = 0; cameraIndex < devices.Length; ++cameraIndex)
{
Debug.Log("devices[cameraIndex].name: ");
Debug.Log(devices[cameraIndex].name);
Debug.Log("devices[cameraIndex].isFrontFacing");
Debug.Log(devices[cameraIndex].isFrontFacing);
}
}
else
{
Debug.Log("no webcams found");
}
}
}
注意:Unity 建议使用 MediaDevices.getUserMedia()
API 来请求用户权限以访问设备。此功能仅在安全上下文(HTTPS)中可用。
WebGL Networking
在 WebGL 中,你可以通过以下几种方式进行网络操作:
- 使用 UnityWebRequest 类。
- 使用 WebSockets or WebRTC from JavaScript 实现自定义网络。你不能在 Web 平台中使用任何 .NET 网络类,因为 JavaScript 代码无法直接访问实现网络连接的 IP 协议 socket 。具体来说,Web 不支持 System.Net 命名空间中的任何 .NET 类。
在 WebGL 中使用 UnityWebRequest 类
Unity 支持在 WebGL 中使用 UnityWebRequest 类。为了实现 UnityWebRequest 类,Unity 使用 JavaScript 的 Fetch API,这会使用浏览器来处理网络请求。这对访问跨域资源(cross-domain resources)施加了安全限制。
如果你向托管 Unity 内容的服务器之外的服务器发送网络请求,那么你发送请求的服务器必须授权 Unity content 。
要在 WebGL 中访问跨域的 Web 资源,你尝试访问的服务器需要使用跨域资源共享 (CORS) 来授权跨域的 Web 资源。
如果你尝试使用 UnityWebRequest 访问内容,但远程服务器没有正确配置(cross-origin resource sharing (CORS)),你会在浏览器控制台中看到类似以下的错误:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://myserver.com/. This can be fixed by moving the resource to the same domain or enabling CORS.
服务器需要向其发送的 http 响应添加 Access-Control 标头,以指示哪些网页有权从浏览器读取该信息。
以下示例展示了如何添加允许 Unity WebGL 从任何来源访问 Web 服务器上的资源的 Access-Control 标头。此示例包含常见的请求标头,并允许 GET、POST 或 OPTIONS 方法:
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Headers": "Accept, X-Access-Token, X-Application-Name, X-Request-Sent-Time",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Origin": "*",
UnityWebRequest 下载
不要使用阻塞 UnityWebRequest 下载的代码,例如:
while(!www.isDone) {}
你不能阻塞线程以等待 UnityWebRequest 下载完成,否则你的应用程序会冻结。因为 WebGL 是单线程的,并且 JavaScript 中的 Fetch API 是异步的,除非你将控制权返回给浏览器,否则你的下载可能不会完成。相反,使用 Coroutine 和 yield 语句等待下载完成。有关更多信息,请参见 使用 UnityWebRequest 的协程示例。
使用 Unity Multiplayer
Unity Multiplayer 通过 WebSockets 协议进行通信。参见 NetworkServer.useWebSockets
。
从 JavaScript 使用 WebSockets 或 WebRTC
WebGL 不允许直接访问 IP 套接字,但你可以使用 WebSockets 或 WebRTC(浏览器支持的两种最常见的网络协议)来解决这个问题。虽然 WebSockets 被广泛支持,但 WebRTC 允许浏览器之间的点对点连接和不可靠的连接。Unity 没有内置的 API 允许你使用 WebSockets 或 WebRTC,但你可以使用 JavaScript plugin 来实现这一点。你可以在 Unity Asset Store 中找到实现 WebSocket 网络的插件。
WebGL 网络的限制
WebGL 平台不支持一些网络功能。
不支持 native socket access
- 由于浏览器内的安全限制,WebGL 平台不支持本地套接字访问。因此,WebGL 也不支持像 UnityEngine.Ping(ICMP)这样的功能。
WebGL 性能考虑
一般来说,WebGL 的 GPU 性能接近于原生应用,因为 WebGL 图形 API 使用你的 GPU 进行硬件加速渲染。唯一的例外是将 WebGL API 调用和 shaders 转换为操作系统图形 API(通常是 Windows 上的 DirectX,Mac 和 Linux 上的 OpenGL)时的轻微开销。
在 CPU 方面,Emscripten 将你的代码翻译成 WebAssembly,其性能取决于你使用的 Web 浏览器。有关更多信息,请参阅 Unity 博客文章 WebAssembly Load Times and Performance。
以下是需要注意的其他考虑事项:
- JavaScript 语言不支持多线程或 SIMD。
- 任何受益于这些功能的代码都可能比其他代码更慢。
- 一些引擎部分是多线程或 SIMD 优化的,在 WebGL 上性能较低。例如,mesh skinning 既是多线程的,又是 SIMD 优化的。
提示:要查看 Unity 如何在非 WebGL 平台上将工作分配到不同线程,请参阅 Unity 的 new timeline Profiler 。
影响性能的 WebGL 特定设置
为了提高性能,在 WebGL 的 Player 设置中,将异常支持 (Exception support) 设置为 None,路径为 Other Settings > Stack Trace。
WebGL 性能分析
WebGL 支持 Unity Profiler。请参阅 Profiler 文档了解如何设置。
后台标签中的 WebGL 内容
如果在 WebGL 平台的 Player 设置中启用了 Run in background
,或者启用了 Application.runInBackground ,当 canvas 或浏览器窗口失去焦点时,你的内容将继续运行。
然而,一些浏览器会限制在后台标签页中运行的内容。如果包含你内容的标签页不可见,大多数浏览器中你的内容每秒只更新一次。请注意,这会导致 Time.time 以比平时更慢的速度进行,因为 Time.maximumDeltaTime 的默认值小于一秒。
节流 WebGL 性能
在某些情况下,你可能希望以较低的帧率运行 WebGL 内容以减少 CPU 使用。例如,在其他平台上,你可以使用 Application.targetFrameRate
API 来实现。
当你不想限制性能时,将此 API 设置为默认值 -1,而不是设置为较高的值。这允许浏览器在浏览器的渲染循环中调整帧率以获得最平滑的动画,这可能比 Unity 尝试自己进行主循环计时以匹配目标帧率产生更好的效果。
调试和排除 WebGL 构建问题
Visual Studio 不支持调试 Unity WebGL 内容。请使用以下提示获取构建信息。
浏览器中的 JavaScript 控制台
Unity WebGL 无法访问你的文件系统,因此它不会像其他平台那样写入日志文件。然而,它会将所有日志信息(如 Debug.Log
、Console.WriteLine
或 Unity 的内部日志)写入浏览器的 JavaScript 控制台。
要打开 JavaScript 控制台:
操作系统 | 浏览器 | 操作说明 |
---|---|---|
Windows | Firefox | 按 Ctrl-Shift-K |
Chrome | 按 Ctrl-Shift-J | |
Microsoft Edge | 按 F12 | |
Internet Explorer | 按 F12 | |
Mac | Firefox | 按 Command-Option-K |
Chrome | 按 Command-Option-J | |
Safari | 进入 Preferences > Advanced > Develop ,然后按 Command-Option-C |
开发构建(Development builds)
为了调试,你可能需要在 Unity 中进行开发构建。要进行开发构建:
- 打开 Build Settings 窗口。
- 启用
Development Build
。
开发构建允许你连接 Profiler。Unity 不会压缩(minify)代码,因此生成的 JavaScript 代码仍然包含可读的 C++ 混淆( C+±mangled)的函数名。
当你遇到浏览器错误、使用 Debug.LogError 或发生异常且异常支持被禁用时,浏览器使用这些函数名来显示堆栈跟踪。这些堆栈跟踪与托管代码和 Unity 引擎内部代码一起出现,包含混淆的名称。
异常支持
WebGL 有不同级别的异常支持,但默认情况下,Unity WebGL 仅支持显式抛出的异常。有关更多信息,请参阅 Build your WebGL application 。你可以启用完全异常支持(Full exception support),这会在 IL2CPP 生成的代码中发出额外检查,以捕获托管代码中的空引用和数组越界访问。这些额外检查会显著影响性能,并增加代码大小和加载时间,因此你应仅在调试时使用。
完全异常支持还会发出函数名以生成托管代码的堆栈跟踪。因此,未捕获的异常和 Debug.Log 语句会在控制台中显示堆栈跟踪。使用 System.Environment.Stacktrace
获取堆栈跟踪字符串。
故障排除
问题:构建运行时内存不足
这是一个常见问题,特别是在 32 位浏览器上。有关 WebGL 内存问题及其解决方法的更多信息,请参阅 Unity Memory in Unity WebGL 。
错误信息:Incorrect header check
通常是由于服务器配置错误,浏览器控制台日志会打印此错误。有关如何部署发布版本的更多信息,请参阅 Deploying compressed builds 。
错误信息:Decompressing this format (1) isn’t supported on this platform
当内容尝试加载使用 LZMA 压缩的 AssetBundle 时,浏览器控制台日志会打印此错误,而 Unity WebGL 不支持 LZMA 压缩。重新使用 LZ4 压缩 AssetBundle 以解决此问题。有关 WebGL 压缩的更多信息,请参阅 WebGL building 文档,特别是 AssetBundles 部分。
构建(Build)和分发(Distribute) WebGL 应用程序
在构建和分发 WebGL 应用程序之前,了解如何执行以下任务:
WebGL 构建设置
使用 WebGL 构建设置配置 Unity 如何为 WebGL 平台构建应用程序。
访问 WebGL 的构建设置
要访问 WebGL 的构建设置:
- 转到 File > Build Settings。
- 选择 WebGL。
WebGL 构建设置参考
以下表格概述了 WebGL 构建设置:
设置 | 功能 |
---|---|
Texture Compression | 构建使用的纹理压缩格式。有关更多信息,请参阅 WebGL texture compression 。可用选项有:* Use Player Settings: 默认选择。使用在 Player settings 窗口中设置的纹理压缩格式。 |
- ETC2: 使用 ETC2 格式,广泛支持于移动设备。
- ASTC: 使用 ASTC 格式,广泛支持于移动设备。
- DXT: 使用 DXT 格式,广泛支持于桌面设备。
|
|Development Build |启用此设置以在构建中包含脚本调试符号和 Profiler。当启用此选项时,Unity 会设置 DEVELOPMENT_BUILD 脚本定义。仅在测试应用程序时使用此设置,因为开发构建不会压缩内容,分发时体积较大。 |
|Code Optimization |选择用于编译 WebGL 代码的优化模式。* Shorter Build Time: 默认设置。选择此选项生成构建时间最短的 WebGL 代码。 - Runtime Speed: 选择此选项生成运行时性能优化的 WebGL 代码,但构建时间较长。
- Runtime Speed with LTO: 与 Runtime Speed 相同,但有额外的链接时间优化(Link Time Optimizations,LTO)阶段(有时称为整体程序优化)。在为最终用户部署主版本构建时启用 LTO。
- Disk Size: 选择此选项以最小化构建大小的优化,构建时间较长。推荐在针对可能对 WebAssembly 文件大小有限制的移动浏览器时选择此选项。较小的磁盘大小通常会缩短页面启动时间。
- Disk Size with LTO: 与 Disk Size 相同,但启用了额外的链接时间优化(LTO)阶段。在为最终用户部署主版本构建时选择此选项。
|
|Autoconnect Profiler |通常启用时,此设置允许你自动将 Unity Profiler 连接到构建。然而对于 WebGL,由于无法将 Profiler 连接到正在运行的构建,使用此选项将内容连接到 Unity 编辑器。这是因为 Profiler 连接在 WebGL 上使用 WebSockets 处理,但网页浏览器只允许内容发出的连接。要使此设置可用,必须启用 Development Build 选项。 |
|Deep Profiling |启用此设置以在 Profiler 中激活深度分析(Deep Profiling)。这会使 Profiler 为应用程序中的每个函数调用进行检测,并返回更详细的分析数据。启用深度分析支持时,可能会减慢脚本执行速度。仅在启用 Development Build 选项时,此选项才可用。 |
|Build |用于构建你的应用程序。 |
|Build And Run |用于在本地查看 WebGL Player。Unity 使用本地 Web 服务器来托管构建,并从 localhost URL 打开它。或者,你可以使用适当配置响应头的自定义本地 Web 服务器。有关更多信息,请参阅压缩构建和服务器配置(Compressed builds and server configuration)。 |
要更改Asset Import Override
s 的设置,请参阅 Build Settings 。
构建 WebGL 应用程序
要为 WebGL 创建构建,请从 Unity 的主菜单中选择 File > Build Settings
。在平台列表中选择 WebGL
,然后点击 Switch Platform
。
有关构建设置的详细信息,请参阅 WebGL Build Settings。
配置好构建设置后,从以下选项中选择一个:
- Build:将你的应用程序构建为一个 Player。
- Build and Run:构建你的应用程序并在目标平台上打开该 Player。
构建文件夹结构
构建文件夹包含以下文件,其中 [ExampleBuild]
代表目标构建文件夹的名称。
文件名 | 内容 |
---|---|
[ExampleBuild].loader.js | 加载 Unity 内容的网页所需的 JavaScript 代码。 |
[ExampleBuild].framework.js | JavaScript 运行时和插件。 |
[ExampleBuild].wasm | WebAssembly 二进制文件。 |
[ExampleBuild].mem | 用于初始化 Player 堆内存的二进制映像文件。仅在多线程 WebAssembly 构建中生成。 |
[ExampleBuild].data | 资产数据和场景。 |
[ExampleBuild].symbols.json | 用于解除错误堆栈跟踪混淆的调试符号名称。仅在启用调试符号选项时(File > Build Settings > Player Settings. )生成此文件。 |
[ExampleBuild].jpg | 加载构建时显示的背景图像。仅在 Player Settings 中有背景图像时生成此文件(File > Build Settings > Player Settings > Splash Image )。有关更多信息,请参见 Splash Screen。 |
如果为构建启用了压缩方法(Compression Method),Unity 会根据压缩方法添加相应的扩展名到构建子文件夹中的文件名。如果启用了解压回退(Decompression Fallback),Unity 会将扩展名 .unityweb 添加到构建文件名中。否则,Unity 会根据压缩方法添加 .gz (Gzip)或 .br (Brotli)。有关更多信息,请参阅 压缩构建和服务器配置。 |
如果在 Player Settings 中启用了 Name Files As Hashes
,Unity 会使用文件内容的哈希值作为文件名。这适用于构建文件夹中的每个文件。此选项允许你将更新的游戏版本上传到服务器上的同一文件夹中,并且只上传构建迭代之间更改的文件。
注意:直接从文件系统打开 Player 可能在某些浏览器中不起作用,这是由于本地文件 URL 的安全限制。
启用异常
使用 Enable Exceptions
来指定在运行时如何处理意外的代码行为(也称为错误)。要访问 Enable Exceptions
,请转到 WebGL Player Settings 中的 Publishing Settings
部分。
选项如下:
- None:如果不需要异常支持,请选择此项。这提供了最佳性能和最小的构建。选择此项时,任何抛出的异常都会导致你的内容在该设置中停止并出现错误。
- Explicitly Thrown Exceptions Only(默认):选择此项以捕获脚本中通过
throw
语句显式指定的异常,并确保执行finally
块。请注意,选择此项会使生成的 JavaScript 代码变得更长更慢;只有在脚本是项目中的主要瓶颈时,这才可能是一个问题。 - Full Without Stacktrace:选择此项以捕获以下异常:
- 显式指定的
throw
语句中的异常(与Explicitly Thrown Exceptions Only
选项相同) - 空引用
- 数组越界访问
- 显式指定的
- Full With Stacktrace:此选项类似于上一个选项,但它还会捕获堆栈跟踪。Unity 通过在代码中嵌入检查来生成这些异常,因此此选项会降低性能并增加浏览器的内存使用量。仅用于调试,并始终在 64 位浏览器中进行测试。
优化你的 WebGL 构建
优化你的 WebGL 构建非常重要,因为基于 WebGL 的应用程序在构建较小的情况下表现最佳。较小的构建意味着需要下载的数据更少,初始化时需要加载的数据更少,从而缩短加载时间。加载时间过长会导致用户体验不佳和高跳出率。
使用以下推荐设置来进行特定于 Unity WebGL 平台的优化。
优化 WebGL 构建的推荐图形设置
使用以下推荐的图形设置来优化 Unity WebGL 平台的构建。
可以在 Edit > Project Settings > Graphics
下找到这些设置。有关每个设置的详细信息,请参阅 Graphics 。
设置推荐值及描述
设置 | 推荐设置 | 描述 |
---|---|---|
Lightmap Modes | Automatic(默认) | 自动剔除未使用的变体。 |
Fog Modes | Automatic(默认) | 自动剔除未使用的变体。 |
Instancing Variants | Strip Unused(默认) | 仅包含至少有一个材质使用的 shader 变体。 |
BatchRendererGroup Variants | Strip all | 移除所有 BatchRendererGroup shader 变体。 |
Always Included Shaders | 移除项目中未使用的 shaders。 |
Lightmap Modes
使用 Lightmap Modes 设置更改与 lightmap 相关的 shader 变体剔除行为。推荐设置为 Automatic(默认)
,会移除构建中未使用的 shader 变体。这一设置有助于减少构建时间、文件大小和内存使用。
Fog Modes
使用 Fog Modes 设置更改与内置 Unity 雾效相关的 shader 变体剔除行为。推荐设置为 Automatic(默认)
,会从构建中移除未使用的雾效 shader。移除未使用的 shader 可以减少构建时间、文件大小和内存使用。
Instancing Variants
使用 Instancing Variants 设置更改 Unity 剔除 GPU 实例化 shader 变体的程度。推荐设置为 Strip Unused
,会移除项目中未使用的实例化变体 shader。移除未使用的 shader 可以减少构建时间、文件大小和内存使用。如果需要保留一些未使用的 shader 供将来使用或供其他 shader 参考,可以选择 Keep All
。
Batch Renderer Group Variants
使用 BatchRendererGroup Variants 设置更改与批处理渲染器组(batch renderer groups, BRGs)相关的 shader 变体剔除行为。如果项目中不使用 BRGs,请将该设置设为 Strip all
,以移除所有 BRG shader 变体。未使用的 shader 会增加构建时间、文件大小和内存使用。如果项目中使用 BRGs,则忽略此建议。
Always Included Shaders
Always Included Shaders 是一个包含 Unity 在每个构建中包含所有可能变体的 shader 列表。如果项目中不使用该列表中的任何 shader,建议将它们移除,因为未使用的 shader 会增加构建时间、文件大小和内存使用。
通过脚本编辑 Always Included Shaders 列表
如果要通过脚本更改 Always Included Shaders 列表,可以创建一个包含所需 shaders 的列表并按如下方式分配:
GraphicsSettings.alwaysIncludedShaders = newShadersList.ToArray();
推荐的 Player 设置以优化 WebGL 构建
使用以下推荐的 Player 设置来优化 Unity WebGL 平台的构建。
Player 设置快速参考
可以在 Edit > Project Settings > Player
下找到这些设置。有关每个设置的详细信息,请参阅 Player 设置的文档。
设置 | 推荐设置 | 描述 |
---|---|---|
API Compatibility Level | .NET Standard 2.1 | 生成较小的构建。 |
IL2CPP Code Generation | Faster (smaller) builds | 生成优化构建大小和迭代速度的代码。 |
Managed Stripping Level | High | Unity 会进行高级别的托管剥离,以创建更小的构建。 |
在 Publishing Settings 部分中配置以下推荐设置: |
设置 | 推荐设置 | 描述 |
---|---|---|
Enable Exceptions | None | 异常会导致开销。 |
Compression Format | Brotli | Brotli 压缩的文件更小。 |
Data Caching | Enabled | 运行更快,因为缓存的数据在后续运行时无需重新下载(除非内容已更改)。 |
Debug Symbols | Off | 调试符号会使应用程序变慢。 |
API Compatibility Level
使用 API Compatibility Level 设置选择项目中可以使用的 .NET API。推荐设置为 .NET Standard 2.1,因为该设置生成的构建较小,并且具有跨平台支持。但是,需要检查目标平台是否完全支持 .NET Standard 2.1。有关其他选项,请参阅 WebGL Player 设置。
通过脚本更改 API Compatibility Level:
PlayerSettings.SetApiCompatibilityLevel(namedBuildTarget, ApiCompatibilityLevel.NET_2_0);
IL2CPP Code Generation
使用 IL2CPP Code Generation 设置配置 Unity 如何管理 IL2CPP 代码生成(如果项目使用 IL2CPP 脚本后端)。
推荐设置为 Faster (smaller) builds,因为它创建的构建较小,生成的代码较少,从而缩短了构建时间。对于 WebGL 应用程序,较快的构建时间至关重要。然而,该设置可能会降低运行时性能。
通过脚本更改 IL2CPP Code Generation:
PlayerSettings.SetIl2CppCodeGeneration(namedBuildTarget, Il2CppCodeGeneration.OptimizeSize);
Managed Stripping Level
使用 Managed Stripping Level 设置配置 Unity 链接器过程从项目使用的托管 DLL 中剥离未使用代码的程度。推荐设置为 High,因为剥离代码可以显著减小可执行文件的大小,这对于 WebGL 应用程序非常重要。然而,可能的副作用包括:
- 某些方法的托管代码调试可能不再工作。
- 可能需要维护自定义的 link.xml 文件。
- 某些反射代码路径可能不会表现相同。
通过脚本更改 Managed Stripping Level:
PlayerSettings.SetManagedStrippingLevel(namedBuildTarget, ManagedStrippingLevel.High);
Compression Format
选择用于发布构建文件的压缩格式。推荐设置为 Brotli,因为它具有最佳的压缩比,Brotli 压缩的文件比 gzip 更小。较小的文件对于 WebGL 应用程序来说是最好的。然而,Chrome 和 Firefox 仅在用户通过 HTTPS 访问网站时才支持 Brotli。此外,尽管大多数现代服务器支持 Brotli,但仍有一些服务器不支持。确保服务器支持 Brotli。
通过脚本更改 Compression Format:
PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Brotli;
Data Caching
启用 Data Caching 以便 Unity 将内容的资产数据缓存到用户的机器上。数据缓存可以使应用程序运行更快。
通过脚本启用 Data Caching:
PlayerSettings.WebGL.dataCaching = true;
Debug Symbols
Debug Symbols 设置保留调试符号,并在发生错误时显示堆栈跟踪的原始函数名称,从而更容易识别错误的来源。建议在最终发布构建时关闭 Debug Symbols 选项,因为它会使构建不必要地变大并使应用程序变慢。在开发和测试期间启用此设置以便更容易识别错误的来源。
通过脚本禁用 Debug Symbols:
PlayerSettings.WebGL.debugSymbolMode = WebGLDebugSymbolMode.Off;
Enable Exceptions
Enable Exceptions 设置允许你选择在运行时如何处理错误。建议在最终发布构建中选择 None 选项,因为此设置提供了最佳性能和最小的构建。
通过脚本禁用 Exceptions:
PlayerSettings.WebGL.exceptionSupport = WebGLExceptionSupport.None;
推荐的质量设置以优化 WebGL 构建
使用以下推荐的质量设置来优化 Unity WebGL 平台的构建。
在 Edit > Project Settings > Quality
中配置以下推荐设置。有关这些设置的详细信息,请参阅 Quality 。
质量级别
质量设置包含一组可能的质量级别。如果将质量级别设置为较低的设置(如 Fastest 或 Fast),可以带来更快的加载时间和更好的性能,这对于 WebGL 应用程序是推荐的。然而,较低的质量可能意味着视觉效果不如高质量设置。
有关此设置的更多信息,请参阅质量设置文档。
通过 C# 代码更改质量级别
要通过脚本更改质量级别,可以将以下代码添加到你的脚本中:
QualitySettings.SetQualityLevel(0, true); // 使用质量设置矩阵的索引(0 = Fastest,1 = Fast)。
使用 C# 代码启用优化设置
您可以使用代码来启用《优化 WebGL 构建》中推荐的一些优化。如果使用代码配置这些设置,可以节省手动逐个设置的时间。
注意:此脚本仅在编辑器中有效,不能在构建中使用。
创建一个 C# 脚本来优化您的 WebGL 构建
要在 Unity 项目设置中一次性启用大部分这些优化:
- 转到
Assets > Create > C# Script
。 - 将以下代码粘贴到脚本中:
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Callbacks;
using UnityEngine;
public class WebGLOptimization : MonoBehaviour
{
[MenuItem("WebGL/Apply Optimization Settings")]
public static void ApplyOptimizationSettings()
{
var namedBuildTarget = NamedBuildTarget.WebGL;
var buildOptions = BuildOptions.CompressWithLz4HC;
// Set IL2CPP code generation to Optimize Size
PlayerSettings.SetIl2CppCodeGeneration(namedBuildTarget, Il2CppCodeGeneration.OptimizeSize);
// Set the Managed Stripping Level to High
PlayerSettings.SetManagedStrippingLevel(namedBuildTarget, ManagedStrippingLevel.High);
// Strip unused mesh components
PlayerSettings.stripUnusedMeshComponents = true;
// Enable data caching
PlayerSettings.WebGL.dataCaching = true;
// Set the compression format to Brotli
PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Brotli;
// Deactivate exceptions
PlayerSettings.WebGL.exceptionSupport = WebGLExceptionSupport.None;
// Deactivate debug symbols
PlayerSettings.WebGL.debugSymbolMode = WebGLDebugSymbolMode.Off;
// Set Platform Settings to optimize for disk size (LTO)
EditorUserBuildSettings.SetPlatformSettings(namedBuildTarget.TargetName, "CodeOptimization", "disksizelto");
Debug.Log("WebGL optimization settings applied.");
}
}
- 根据项目需求修改脚本。
- 将脚本附加到一个 GameObject 上。
- 进入 Play 模式,您的各项设置将被更新。
优化 WebGL 平台以适应移动设备
对于基于 Web 的应用程序来说,快速加载时间至关重要,尤其是在移动设备上,它们可能需要通过较慢的移动网络下载应用程序资源。加载时间过长会导致用户体验差和高跳出率。因此,优化您的 WebGL 构建以适应移动设备非常重要。
Web 基于移动设备的应用程序在构建较小且代码高效时效果最好,因为这样下载的数据更少,用户设备上存储的数据更少,初始化时加载的数据也更少,从而加快了加载时间。
对于不特定于移动设备的 WebGL 优化,请参阅上一节 Optimize your WebGL build 。
优化快速参考
使用以下推荐设置来为移动设备上的 Unity WebGL 平台进行特定优化:
推荐项 | 描述 |
---|---|
优化尺寸 | 优化您的构建以适应磁盘大小(使用 LTO 的磁盘大小)。 |
使用 Brotli 压缩 | 使用 Brotli 压缩方法压缩您的 Player 构建。 |
使用 Unity Addressables 系统 | 使用 Unity Addressables 系统管理资源。 |
优化音频文件 | 减少音频文件占用的磁盘空间。 |
优化项目中的图形 | 减少图形文件占用的磁盘空间。 |
更改图形质量等级 | 降低质量等级以加快构建速度。 |
优化尺寸
较小的构建尺寸更适合移动设备,因为下载的数据更少,通常会缩短加载时间并减少用户设备上的存储空间。为了确保构建尽可能小,请确保优化您的构建以适应磁盘大小:
- 转到
File > Build Settings
。 - 选择
WebGL
。 - 将
Code Optimization
设置为Disk Size with LTO
。
注意:使用 LTO 优化的构建可能需要很长时间。仅在最终发布或需要测试性能时使用此优化。在开发期间使用更快的构建选项。
对于影响构建大小的其他设置,请参阅 Recommended Player settings to optimize your Web build 。
使用 C# 启用优化尺寸设置
如果您有编辑设置的脚本并且想要使用 C# 启用 Disk Size with LTO 设置,请将以下代码添加到您的脚本中:
// 设置平台设置以优化磁盘大小(LTO)
EditorUserBuildSettings.SetPlatformSettings(namedBuildTarget.TargetName, "CodeOptimization", "disksizelto");
使用 Brotli 压缩
Brotli 压缩是一种 Unity 在构建时预压缩文件的方法。Brotli 压缩的文件比 gzip 压缩的文件更小,这可以减少您的构建大小。
然而,Brotli 压缩时间更长。此外,Chrome 和 Firefox 仅在 HTTPS 上本地支持 Brotli 压缩。因此,如果这不适合您的应用程序,请考虑使用 Gzip 压缩。
要使用 Brotli 压缩设置:
- 确保您的 Web 服务器配置为使用正确的编码服务 Brotli 文件。更多详细信息,请参阅 Deploy WebGL application 。
- 访问
Player Settings
(菜单:Edit > Project Settings > Player
)。 - 单击
WebGL
设置选项卡。 - 展开
Publishing Settings
部分。 - 将
Compression Format
设置为Brotli
。
使用 C# 更改压缩格式
如果您有项目设置脚本并想要使用 C# 更改压缩格式,请将以下代码添加到您的脚本中:
// 将压缩格式设置为 Brotli
PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Brotli;
使用 Unity Addressables 系统
移动浏览器应用程序需要较短的加载时间来减少用户跳出率。为了保持初始化时间较短,不要在启动时加载所有资源,而是使用 Addressables 系统在需要时加载资源。在游戏加载之后再延迟加载某些资源。
对于这些更改,请确保使用任何新的 addressable files 更新 StreamingAssets/aa/catalog.json
。
有关 Addressables 以及如何设置它们的更多信息,请参阅 Addressables。
要进一步优化 Addressables,请尝试以下操作:
- 将 Addressables 分类到组(Group)中:将 Addressables 分类到组中,以减少 asset bundle 的数量。asset bundle 数量越少,构建大小越小。有关可寻址组以及如何创建它们的更多信息,请参阅 管理和创建组。
- 压缩可寻址文件:使用 Brotli 或 Gzip 压缩
.bundle
可寻址文件。压缩可寻址文件可以使文件更小,从而减少构建大小。
优化音频文件
如果您的项目中有很多音频文件,最好压缩文件以降低音频文件的大小。但是,压缩音频可能会导致音频质量降低。有关压缩格式的信息,请参阅 Audio Clip。
优化项目中的图形
复杂的图形会占用资源并使您的构建变大且性能下降。但是,可以通过以下方法优化图形来减少构建大小并提高性能:
- 在可能的情况下使用 ASTC 压缩纹理:ASTC 压缩纹理类型提供较小的纹理大小并节省下载时间。如果 ASTC 不支持,请考虑使用 ETC2。有关更多信息,请参阅 纹理格式。
- 使用较大的 ASTC 压缩纹理块大小:如果使用 ASTC 压缩纹理,请尝试使用较大的块大小进行压缩。ASTC 支持 4x4 到 12x12 texels 的块大小。较大的块大小会导致纹理质量较低,但构建更小。对于在移动网络上优化下载时间的质量和大小之间的良好平衡,考虑使用 8x8。如果下载时间仍然太慢,请尝试增加块大小。
要设置 ASTC 压缩纹理的块大小:
- 在项目中选择纹理资源以打开纹理导入设置窗口。
- 选择
WebGL
设置。 - 启用
Override For WebGL
。 - 将
Format
设置为较大的 ASTC 块选项之一,例如RGB(A) Compressed ASTC 8x8 block
。
启用 Shader 剥离
Unity 使用 Shader 剥离来移除未使用的 Shader 变体。Shader 剥离可以减少构建文件大小,从而减少应用程序加载时间并提高性能。
但是,请确保测试您的应用程序以确保未使用的 Shader 未被其他 Shader 引用。
要启用 Shader 剥离,请参阅 Graphics。
降低图形质量等级
建议将图形质量等级设置为最快选项。较快的选项会导致构建更小。
要更改图形质量等级:
- 访问
Quality Settings
(菜单:Edit > Project Settings > Quality
)。 - 选择
Fastest
或Fast
质量等级。
但是,最快的设置会影响应用程序的视觉效果,因此请确保您的应用程序看起来符合您的需求。
使用 C# 更改质量等级
如果您有项目设置脚本并想要在脚本中更改项目的质量等级,请添加以下代码:
// 将质量等级设置为 Fastest(索引 0)
QualitySettings.SetQualityLevel(0, true);
SetQualityLevel()
函数将质量等级矩阵的索引作为值。所以在这种情况下,SetQualityLevel(0, true)
是 Fastest 设置,即质量等级矩阵中的第一个选项。要将其更改为 Fast 或第二个选项,请使用 SetQualityLevel(1, true)
。
使用 AssetBundles 减少加载时间
由于必须在内容启动前下载所有资源数据,考虑将资源从主数据文件移至 AssetBundles。将资源移至 AssetBundles 可以为内容创建一个小型加载器场景,并在用户浏览内容时动态加载资源。AssetBundles 还可以帮助管理 Asset data memory 。可以通过调用 AssetBundle.Unload
来卸载不再需要的资源数据。
在 WebGL 平台上使用 AssetBundles 时,需考虑以下事项:
- 当在 AssetBundle 中使用的类类型未在主构建中使用时,Unity 可能会从构建中剥离这些类的代码。这可能会导致尝试从 AssetBundle 加载资源时失败。使用 BuildPlayerOptions.assetBundleManifestPath 可以解决此问题,或者参考 Distribution size and code stripping 的其他选项。
- WebGL 不支持多线程。由于 HTTP 下载仅在下载完成后可用,Unity WebGL 构建需要在下载完成后在主线程上解压 AssetBundle 数据,从而阻塞主线程。为了避免这种中断,WebGL 上的 AssetBundles 不使用 LZMA AssetBundle compression ,而是使用 LZ4 压缩,LZ4 可以高效地按需解压。如果需要比 LZ4 提供的更小的压缩大小,可以配置 Web 服务器对 AssetBundles 使用 gzip 或 Brotli 压缩(在 LZ4 压缩的基础上)。有关如何执行此操作的更多信息,请参阅部署压缩构建。
- WebGL 支持使用 UnityWebRequestAssetBundle.GetAssetBundle 进行 AssetBundle 缓存。此方法使用浏览器的 IndexedDB API 在用户设备上存储缓存。一些浏览器可能对 IndexedDB 支持有限,并且任何浏览器可能会请求用户授权在磁盘上存储数据。有关更多信息,请参阅WebGL 浏览器兼容性。
WebGL 的发布大小和代码剥离
在为 WebGL 发布时,保持构建文件的大小较小非常重要,这样用户在内容开始之前可以获得合理的下载时间。有关减少资源大小的一般提示,请参阅 Reducing the file size of the build 。
针对 WebGL 的提示和技巧
- 指定 Crunch 纹理压缩格式:在 Texture Importer 中为所有压缩纹理指定 Crunch 压缩格式。
- 禁用异常支持:在 Player settings 窗口(点击 Edit > Project Settings > WebGL),展开 Publishing Settings,并将 Enable Exceptions 设置为 None,如果不需要异常支持的话。
- 启用剥离引擎代码:在 Player settings > Other Settings 面板中启用 Strip Engine Code,以确保构建效率。
- 第三方托管 DLL:使用第三方托管 DLL 时,要注意它可能带有依赖项,增加生成的代码大小。
- 发布压缩构建:如果你进行发布构建,Unity 会根据 WebGL Player settings 的 Publishing Settings 面板中选择的压缩格式来压缩构建输出文件。
有关如何发布压缩构建的更多信息,请参阅 Deploying compressed builds 。
代码剥离
Unity 默认会从构建中移除所有未使用的代码。你可以通过 Player settings(菜单:Edit > Project Settings,然后选择 Player 类别)更改此设置:选择 Other Settings 面板以访问 Strip Engine Code 选项。启用剥离会更好。
通过代码剥离,Unity 会扫描项目中使用的所有 UnityObject 派生类(无论是通过脚本代码引用,还是在场景中的序列化数据引用)。然后,Unity 会从构建中移除没有类使用的所有 Unity 子系统。这使得构建中包含的代码更少,从而减少下载量,并加快代码的解析速度,使用更少的内存。
代码剥离的问题
代码剥离可能会导致项目出现问题,如果剥离了实际需要的代码,尤其是在运行时加载包含主构建中未包含类的 AssetBundle 时,项目可能会出错。在浏览器的 JavaScript 控制台中会出现错误消息(可能会有更多错误),例如:
Could not produce class with ID XXX
要解决这些错误,可以在 Class ID Reference 中查找 ID(如上例中的 XXX)以查看它试图创建实例的类。在这种情况下,可以通过在脚本或场景中引用该类,或者在项目中添加 link.xml
文件来强制 Unity 包含该类的代码。
下面是一个确保 Collider 类和 Physics 模块在项目中保留的 XML 示例。将此 XML 代码添加到一个名为 link.xml 的文件中,并将该文件放入 Assets 文件夹中:
<linker>
<assembly fullname="UnityEngine">
<type fullname="UnityEngine.Collider" preserve="all"/>
</assembly>
</linker>
如果你怀疑代码剥离导致构建出现问题,可以在测试期间禁用 Strip Engine Code 选项(也就是说,如果禁用 Strip Engine Code 之后问题还是出现,就说明不是代码剥离导致的)。
Unity 没有提供方便的方式查看构建中包含哪些模块和类,但可以在构建后查看生成的文件 Temp/StagingArea/Data/il2cppOutput/UnityClassRegistration.cpp
来获取包含类和模块的概览。
注意,Strip Engine Code 选项仅影响 Unity 引擎代码。IL2CPP 总是会剥离托管 DLL 和脚本中的字节码。当你需要通过反射而不是静态引用访问托管类型时,这可能会导致问题。如果需要通过反射访问类型,可能还需要设置 link.xml 文件以保留这些类型。有关 link.xml 文件的更多信息,请参阅 iOS Build size optimization 。
移动构建输出文件
要更改 Build 文件夹的位置,可以修改 WebGL Template index.html 文件中的 buildUrl 变量。
要更改 Build 文件夹中文件的位置,可以在 index.html 文件中的 config 变量参数中更改它们的 URL(即 dataUrl、wasmCodeUrl、wasmMemoryUrl 和 wasmFrameworkUrl)。
如果想将这些文件托管在内容分发网络 (content distribution network,CDN) 上,可以指定外部服务器上的 URL,但需要确保托管服务器启用了跨域资源共享 (CORS)。有关 CORS 的更多信息,请参阅 WebGL networking 。
增量构建
IL2CPP 为项目生成的 C++ 代码是增量编译的;即仅重新编译自上次构建以来发生变化的生成 C++ 代码。未更改的源代码会重用上次构建生成的相同目标文件。用于增量 C++ 构建的目标文件存储在 Unity 项目的 Library/il2cpp_cache
目录中。
要进行不使用增量编译的全新构建,请删除 Unity 项目中的 Library/il2cpp_cache
目录。请注意,如果 Unity Editor 版本与上次 WebGL 构建时使用的版本不同,Unity 会自动进行全新构建。
WebGL Template
重要提示:要使用 WebGL 的模板功能,需要事先了解 JavaScript 概念和术语。
当你构建一个 WebGL 项目时,Unity 会将 Player 嵌入到一个 HTML 页面中,以便浏览器可以打开它。WebGL 模板是一种配置设置,允许你控制此 HTML 页面的外观,这样你就可以在浏览器中测试、演示和预览你的 WebGL 应用程序。
要访问 WebGL 模板:
- 转到 Player 设置(菜单:Edit > Project Settings > Player),并将平台特定设置设置为 WebGL。
- 打开 Resolution and Presentation。
默认情况下,WebGL 模板设置有以下选项:
- Default:一个带有灰色画布上的加载条的白色页面。
- Minimal:一个包含运行 WebGL 内容所需的基本代码的最小 WebGL 模板。
- PWA:一个包含 web manifest file 和服务工作代码(service worker code)的渐进式 Web 应用程序(**Progressive Web App****,**PWA)。
这些内置的 HTML 页面对于测试和演示一个最小的 Player 非常有用。
你还可以使用 JavaScript 构建和提供自己的 WebGL 模板来托管 Player。这在生产环境中非常有用,可以预览最终部署页面中托管的 Player。例如,如果 Unity Player 内容通过外部调用接口与页面中的其他元素交互,你应该使用包含这些交互元素的页面进行测试。
添加 WebGL 模板
要向项目中添加自定义模板,导航到项目的 Assets 文件夹并创建一个名为 WebGLTemplates
的文件夹。每个模板都是 WebGLTemplates 文件夹中的一个子文件夹。每个模板子文件夹包含一个 index.html 文件以及页面所需的其他资源,例如图像或样式表(stylesheets)。
创建新自定义 WebGL 模板的最简单方法是复制内置的 Default 或 Minimal 模板,它们存储在 <Unity Installation>/PlaybackEngines/WebGLSupport/BuildTools/WebGLTemplates/
下的相应子文件夹中。每个 Unity 项目默认包含这些模板。将模板复制并放置在你自己的 Project/Assets/WebGLTemplates
文件夹中,并将其重命名为有意义的名称,以便以后能够识别你的模板。
注意:在 Mac 上,你可以在 Applications 文件夹中找到 Unity Installation 文件夹。
项目的 WebGLTemplates 文件夹中的模板会出现在 Unity Player 设置的 WebGL Template 设置中。模板的名称与其文件夹相同。要为此选项添加缩略图图像以便于参考,请将一个 128x128 像素的图像添加到模板文件夹中,并命名为 thumbnail.png
。
模板变量、宏和条件指令(Template variables, macros, and conditional directives)
在构建过程中,Unity 预处理模板文件并评估这些文件中包含的所有宏和条件指令。作为此过程的一部分,Unity 查找并替换所有宏声明,替换为编辑器提供的值。Unity 自动预处理模板文件夹中的所有 .html
、.php
、.css
、.js
和 .json
文件。
内部预处理变量(Internal preprocessor variables)
以下内部预处理变量引用项目中的数据,Unity 在构建时根据编辑器提供的值为它们赋值。JavaScript 宏和条件指令可以使用这些内部预处理变量。
JavaScript Macros
JavaScript 宏是模板文件中的 JavaScript 代码块,包含在三组大括号中。此 JavaScript 代码可以使用上面列出的内部预处理变量。这些变量在构建时根据编辑器提供的值进行赋值。在构建过程中,预处理器评估所有宏,并用变量的输出替换它们。
你可以使用 JavaScript 宏预处理由编辑器提供的值。这些宏可以是任意复杂的,包括多个操作符、循环、函数和任何其他 JavaScript 构造。
以下示例行来自 Default 模板中使用的 index.html 文件:
<div id="unity-build-title">{{{ PRODUCT_NAME }}}</div>
如果 Player 设置中的产品名称设置为 My WebGL Game,则内部预处理变量 PRODUCT_NAME
的值为 My WebGL Game。在输出的 index.html 文件中,该行显示为:
<div id="unity-build-title">My WebGL Game</div>
下面是同一个 index.html 模板文件中的一个更复杂的示例:
canvas.style.background = "url('" + buildUrl + "/{{{ BACKGROUND_FILENAME.replace(/'/g, '%27') }}}') center / cover";
如果目标构建文件夹名为 Let’s try WebGL,并且在 Player 设置中选择了背景图像,则内部预处理变量 BACKGROUND_FILENAME
的值为 Let’s try WebGL.jpg
。在输出的 index.html 文件中,该行变为:
canvas.style.background = "url('" + buildUrl + "/Let%27s try WebGL.jpg') center / cover";
条件指令(Conditional directives)
条件指令 #if
、#else
和 #endif
控制 Unity 是在输出文件中包含预处理文件的特定部分,还是为当前构建丢弃它。
以 #if
指令开头并以 #endif
指令结尾的代码称为条件组。条件组还可以包括 #else
指令。Unity 以 JavaScript 表达式的形式评估写在 #if
后面的表达式。如果此表达式具有在布尔上下文中评估为 true 的真值,则 Unity 将 #if
指令后面的行组包含在输出文件中。如果 #if
表达式为 false,并且条件组中包含 #else
指令,则 Unity 将 #else
指令后面的行组包含在输出中。以下是条件组的示例:
#if EXPRESSION// 如果 EXPRESSION 具有真值,则将此块包含在输出中
#else// 否则将此块包含在输出中
#endif
评估的 JavaScript 表达式可以包括括号、逻辑运算符和其他 JavaScript 构造。条件指令可以嵌套。
自定义用户变量(Custom user variables)
当你选择 WebGL 模板时,Unity 解析模板并查找 JavaScript 宏和条件指令。如果这些 JavaScript 变量:
- 在 JavaScript 宏和条件指令内使用。
- 没有在模板代码中声明。
- 不是内部预处理变量。
Unity 会自动将这些自定义用户变量添加到 Player 设置窗口中的 Resolution and Presentation 部分。
例如,如果你想直接从 Player 设置窗口控制生成的 index.html 页面的标题,首先需要修改自定义模板中 index.html 的 <title>
行,如下所示:
<title>{{{ PAGE_TITLE }}}</title>
完成此操作后,重新选择你的自定义模板。这将再次解析模板,并且你应该在 Player 设置窗口的 Resolution and Presentation > WebGL Template
部分中找到一个 Page Title
字段。
当你在此字段中输入文本并构建项目时,模板宏中使用的自定义变量 PAGE_TITLE 会自动成为 Page Title 字段中的文本。
如果你希望在宏中使用自定义整数或浮点变量,请在宏中使用 parseInt()
或 parseFloat()
JavaScript 函数来预处理编辑器提供的字符串值。这是因为自定义用户变量总是分配字符串值。
注意:变量名中的下划线字符在字段内显示为空格,以提高可读性。
index.html 文件的结构
index.html 包含加载构建所需的代码,应包括以下内容:
- 一个
<canvas>
元素。Unity 运行时使用<canvas>
元素来渲染应用程序。 - 下载构建加载程序的 JavaScript 代码。例如:
var buildUrl = "Build";
var loaderUrl = buildUrl + "/{{{ LOADER_FILENAME }}}";
var script = document.createElement("script");
script.src = loaderUrl;
script.onload = () => {// 实例化构建的代码
};
document.body.appendChild(script);
在这个示例中,{{{ LOADER_FILENAME }}}
在生成构建时由模板预处理器自动解析。
- 或者,可以使用 script 标签下载构建加载程序,例如:
<script src="Build/{{{ LOADER_FILENAME }}}"></script>
- 用于实例化构建的 JavaScript 代码。Unity 构建是使用
createUnityInstance()
函数实例化的,该函数在构建加载程序脚本中定义。
实例化函数:createUnityInstance()
createUnityInstance()
函数创建你的内容的新实例。你可以这样使用它:createUnityInstance(canvas, config, onProgress).then(onSuccess).catch(onError);
这个函数返回一个 Promise 对象,其中:
createUnityInstance()
函数在构建加载程序脚本中定义,并且特定于实例化的构建。因此,如果你在同一个 HTML 文档中嵌入两个或更多构建,请确保 createUnityInstance()
函数从相应的构建加载程序脚本的 onload 回调中调用。有关 Unity WebGL 加载程序的更多信息,请参阅 Building and Running a WebGL Project 。
构建配置
配置对象包含构建配置,包含代码和数据 URL、产品名称、公司名称和版本。你可以使用以下代码定义它:
var buildUrl = "Build";
var config = {
dataUrl: buildUrl + "/{{{ DATA_FILENAME }}}",
frameworkUrl: buildUrl + "/{{{ FRAMEWORK_FILENAME }}}",
codeUrl: buildUrl + "/{{{ CODE_FILENAME }}}",
#if MEMORY_FILENAMEmemoryUrl: buildUrl + "/{{{ MEMORY_FILENAME }}}",
#endif
#if SYMBOLS_FILENAMEsymbolsUrl: buildUrl + "/{{{ SYMBOLS_FILENAME }}}",
#endif
streamingAssetsUrl: "StreamingAssets",
companyName: "{{{ COMPANY_NAME }}}",
productName: "{{{ PRODUCT_NAME }}}",
productVersion: "{{{ PRODUCT_VERSION }}}",
};
在上面的示例中,构建文件夹 URL 存储为一个名为 buildUrl 的单独变量。当你不知道嵌入页面与托管服务器(host ing server)上的构建文件夹之间的关系时,这很有用。它使你能够在其他 HTML 文档中重用嵌入代码。例如,如果你将 Build 文件夹移动到服务器上的另一个位置,可以调整嵌入页面上 buildUrl 变量的值,并可以使用相同的嵌入代码。这也适用于 StreamingAssets 文件夹(streamingAssetsUrl
)。
除了在构建中设置上述 URL 和字段名称之外,您还可以在配置对象上设置以下属性。
属性 | 用途 |
---|---|
matchWebGLToCanvasSize: false | 默认情况下(如果设置为 true 或未设置),Unity 会将 WebGL 画布的渲染目标大小与画布元素的文档对象模型 (DOM) 大小同步(按 window.devicePixelRatio 缩放)。如果您希望手动设置画布 DOM 大小和 WebGL 渲染目标大小,请将此属性设置为 false 。 |
devicePixelRatio: <number> | 该字段用于强制设置页面的 DPI 缩放比例。将其设置为 1 可以强制渲染为“标准 DPI”(或非 Retina DPI),这有助于在低端移动设备上提高性能。默认情况下,该字段未设置,这意味着渲染页面会使用浏览器的 DPR 缩放比例,从而实现高 DPI 渲染。 |
autoSyncPersistentDataPath: true | 如果设置为 true ,Unity 应用程序的 persistentDataPath 目录中的所有文件写入操作都会自动持久化,以便用户下次访问网站时记住内容。如果未设置(或设置为 false ),则必须通过调用 JS_FileSystem_Sync() JavaScript 函数手动同步 Application.persistentDataPath 目录中的文件修改。 |
构建交互(Build interaction)
构建成功实例化后,Promise object 的履约处理程序回调(fulfilment handler callback)会接收新创建的 Unity 实例对象作为参数。要与构建进行交互,请调用 Unity 实例的以下方法:
通过这些设置和方法,你可以自定义和优化 WebGL 构建,并使其与外部网页元素进行交互,以实现更好的用户体验。
部署 WebGL 应用程序
要部署 WebGL 构建,必须配置服务器并确保使用正确的响应头,以便浏览器接收并正确处理响应。
Unity 中有两个主要设置会影响你如何设置服务器:
- Compression Format:确定 Unity 在构建步骤中如何压缩文件。
- Decompression Fallback:确定 Unity 在浏览器中运行构建时如何处理下载的文件。
压缩格式
从 Web Player 设置窗口选择压缩类型(菜单:Edit > Project Settings > Player,然后选择 Web 并展开 Publishing Settings 部分):
压缩方法 | 描述 |
---|---|
gzip | 默认选项。gzip 文件比 Brotli 文件大,但构建速度更快,并且在所有浏览器中都可以通过 HTTP 和 HTTPS 原生支持。 |
Brotli | Brotli 压缩提供最佳压缩比。Brotli 压缩的文件比 gzip 文件小,但压缩时间更长,这会增加发布构建的迭代时间。Chrome 和 Firefox 仅在 HTTPS 上原生支持 Brotli 压缩。 |
Disabled | 禁用压缩。如果你想在后处理脚本中实现自己的压缩,或计划在托管服务器上使用静态压缩,请使用此选项。 |
有关所选压缩方法的浏览器支持的更多信息,请参阅 WebGL browser compatibility 。 |
注意:Compression Format 设置仅适用于发布构建。开发构建不进行压缩。
Web 服务器配置
你可能需要调整服务器配置以匹配你的特定构建设置。特别是,如果你已经有另一个服务器端配置来压缩托管文件,这可能会干扰此设置。为了让浏览器在下载应用程序时原生执行解压,请在服务器响应中附加 Content-Encoding
头。此头必须与 Unity 在构建时使用的压缩类型相对应。有关代码示例,请参阅服务器配置代码示例。
解压回退(Decompression fallback)
解压回退选项使 Unity 能够自动将 JavaScript 解压程序嵌入到构建中。此解压程序对应你选择的压缩方法,并在浏览器无法解压时解压内容。
启用解压回退
从 Player 设置窗口启用解压回退(菜单:Edit > Project Settings > Player,然后选择 WebGL 并展开 Publishing Settings 部分)。
当你启用解压回退时,Unity 会在构建文件中添加 .unityweb
扩展名。如果你对服务器配置不太熟悉,或者无法配置服务器,建议使用解压回退。
注意:启用解压回退会导致加载器尺寸增大,并且加载文件的方案效率较低。
禁用解压回退
默认情况下,解压回退选项是禁用的。因此,默认情况下,构建文件包含与你选择的压缩方法相对应的扩展名。
有两种压缩方法可供选择:gzip 或 Brotli。有关更多信息,请参阅压缩格式部分。
要在浏览器下载文件时原生解压 Unity 构建文件,你需要配置 Web 服务器以使用适当的 HTTP 头提供压缩文件。这称为原生浏览器解压(native browser decompression),比 JavaScript 解压回退更快,可以减少应用程序的启动时间。
具体的原生浏览器解压设置取决于你的 Web 服务器。有关代码示例,请参阅服务器配置代码示例。
Content-Encoding 头
Content-Encoding 头告诉浏览器 Unity 为压缩文件使用的压缩类型。这允许浏览器原生解压文件。
将 Content-Encoding 响应头设置为在 Player 设置中选择的压缩方法。
压缩方法 | 文件扩展名 | 响应头 |
---|---|---|
gzip | .gz | Content-Encoding: gzip |
Brotli | .br | Content-Encoding: br |
WebAssembly streaming (higher level header)
WebAssembly 流式传输允许浏览器在下载代码的同时编译 WebAssembly 代码,这显著提高了加载时间。
要使 WebAssembly 流式编译工作,服务器需要以 application/wasm
MIME 类型返回 WebAssembly 文件。要使用 WebAssembly 流式传输,需要使用 Content-Type: application/wasm
响应头提供 WebAssembly 文件。Content-Type 头告诉服务器内容的媒体类型是什么。对于 WebAssembly 文件,此值应设置为 application/wasm
。
文件扩展名 | 响应头 |
---|---|
.wasm, .wasm.gz, .wasm.br | Content-Type: application/wasm |
注意:当启用解压回退选项时,WebAssembly 流式传输无法与 JavaScript 解压一起工作。下载的 WebAssembly 文件必须首先通过 JavaScript 解压程序,因为浏览器无法在下载期间流式传输它。 |
其他头
如果你的文件包含 JavaScript,应添加 application/javascript
Content-Type 头。某些服务器可能会自动包含此头,而其他服务器则不会。
文件扩展名 | 响应头 |
---|---|
.js, .js.gz, .js.br | Content-Type: application/javascript |
服务器配置代码示例
使用以下代码示例来配置 WebGL 构建时的服务器。这些示例适用于 Nginx、Apache 和 IIS 服务器。
Nginx 服务器配置(WebGL 构建)
http {
# ...
server {
# 在 HTTP 服务器配置中添加以下配置
# ...
# 磁盘上 Brotli 预压缩的数据文件应启用压缩:
location ~ .+\.(data|symbols\.json)\.br$ {
# 因为此文件已经在磁盘上预压缩,禁用按需压缩。
# 否则 nginx 会尝试进行二次压缩。
gzip off;
add_header Content-Encoding br;
default_type application/octet-stream;
}
# 磁盘上 Brotli 预压缩的 JavaScript 代码文件:
location ~ .+\.js\.br$ {
gzip off; # 不要对已经压缩的文件进行动态 gzip 压缩
add_header Content-Encoding br;
default_type application/javascript;
}
# 磁盘上 Brotli 预压缩的 WebAssembly 文件:
location ~ .+\.wasm\.br$ {
gzip off; # 不要对已经压缩的文件进行动态 gzip 压缩
add_header Content-Encoding br;
# 通过指定正确的 MIME 类型启用流式 WebAssembly 编译
default_type application/wasm;
}
# 磁盘上 gzip 预压缩的数据文件应启用压缩:
location ~ .+\.(data|symbols\.json)\.gz$ {
gzip off; # 不要对已经压缩的文件进行动态 gzip 压缩
add_header Content-Encoding gzip;
default_type application/gzip;
}
# 磁盘上 gzip 预压缩的 JavaScript 代码文件:
location ~ .+\.js\.gz$ {
gzip off; # 不要对已经压缩的文件进行动态 gzip 压缩
add_header Content-Encoding gzip; # 由于 Safari 的一个 bug https://bugs.webkit.org/show_bug.cgi?id=247421, 使用 MIME 类型 application/gzip 比 application/octet-stream 更好。
default_type application/javascript;
}
# 磁盘上 gzip 预压缩的 WebAssembly 文件:
location ~ .+\.wasm\.gz$ {
gzip off; # 不要对已经压缩的文件进行动态 gzip 压缩
add_header Content-Encoding gzip;
# 通过指定正确的 MIME 类型启用流式 WebAssembly 编译
default_type application/wasm;
}
}
}
Apache 服务器配置(WebGL 构建)
可以通过两种方法在 Apache 中配置 WebGL 构建:在 httpd.conf
中配置虚拟主机,或使用 .htaccess
。如果你有访问 httpd.conf
的权限,Unity 推荐在 httpd.conf
中配置虚拟主机。
使用 httpd.conf 配置 WebGL 构建:
<Directory /var/www/html/root/path/to/your/unity/content/>
<IfModule mod_mime.c>
RemoveType .gz
AddEncoding gzip .gz
# 由于 Safari 的一个 bug https://bugs.webkit.org/show_bug.cgi?id=247421, 使用 MIME 类型 application/gzip 比 application/octet-stream 更好。
# AddType application/octet-stream .data.gz
AddType application/gzip .data.gz
AddType application/wasm .wasm.gz
AddType application/javascript .js.gz
AddType application/octet-stream .symbols.json.gz
RemoveType .br
AddEncoding br .br
AddType application/octet-stream .data.br
AddType application/wasm .wasm.br
AddType application/javascript .js.br
AddType application/octet-stream .symbols.json.br
</IfModule>
</Directory>
使用 .htaccess 配置 WebGL 构建:
如果你无法修改 httpd.conf,例如,由于无法获得 Web 托管的管理权限,但你的 Apache 支持 .htaccess,那么可以按照以下方式配置 .htaccess:
# 此配置文件应上传到服务器 "<Application Folder>/Build/.htaccess"
# 注意:必须启用 "mod_mime" Apache 模块才能使此配置生效。
<IfModule mod_mime.c>
# 以下几行是针对不带解压回退、使用 gzip 压缩的构建所需的
RemoveType .gz
AddEncoding gzip .gz
AddType application/gzip .data.gz # 由于 Safari 的一个 bug https://bugs.webkit.org/show_bug.cgi?id=247421, 使用 MIME 类型 application/gzip 比 application/octet-stream 更好。
AddType application/wasm .wasm.gz
AddType application/javascript .js.gz
AddType application/octet-stream .symbols.json.gz
# 以下几行是针对不带解压回退、使用 Brotli 压缩的构建所需的
RemoveType .br
RemoveLanguage .br
AddEncoding br .br
AddType application/octet-stream .data.br
AddType application/wasm .wasm.br
AddType application/javascript .js.br
AddType application/octet-stream .symbols.json.br
# 以下行提高未压缩构建的加载性能
AddType application/wasm .wasm
# 取消注释以下行以提高带解压回退的 gzip 压缩构建的加载性能
# AddEncoding gzip .unityweb
# 取消注释以下行以提高带解压回退的 Brotli 压缩构建的加载性能
# AddEncoding br .unityweb
</IfModule>
IIS 服务器配置(压缩的 WebGL 构建,不带解压回退)
<?xml version="1.0" encoding="UTF-8"?>
<!--
此服务器配置可用于不带解压回退的压缩 WebGL 构建。
此配置文件应上传到服务器 "<Application Folder>/Build/web.config"。
注意:要托管不带解压回退的压缩 WebGL 构建,必须在服务器上安装 "URL Rewrite" IIS 模块。
否则,使用此配置文件时 IIS 会引发异常。
该模块可在 https://www.iis.net/downloads/microsoft/url-rewrite 下载。
-->
<configuration>
<system.webServer>
<!--
无法正确托管不带解压回退的压缩 Unity 构建的服务器,这可能会导致构建文件被压缩两次。
以下行禁用静态服务器压缩。
-->
<urlCompression doStaticCompression="false" />
<!-- 为了托管压缩 Unity 构建,必须为压缩的构建文件设置正确的 mimeType。 -->
<staticContent>
<!--
注意:如果为同一扩展名多次指定 mimeType,IIS 将引发异常。
为了避免与服务器上已经存在的配置发生冲突,应使用 <remove> 元素移除相应扩展名的 mimeType,
然后使用 <mimeMap> 元素添加 mimeType。
-->
<!-- 以下几行是针对不带解压回退、使用 gzip 压缩的构建所需的。 -->
<remove fileExtension=".data.gz" />
<mimeMap fileExtension=".data.gz" mimeType="application/gzip" /><!-- 由于 Safari 的一个 bug https://bugs.webkit.org/show_bug.cgi?id=247421, 使用 MIME 类型 application/gzip 比 application/octet-stream 更好。 -->
<remove fileExtension=".wasm.gz" />
<mimeMap fileExtension=".wasm.gz" mimeType="application/wasm" />
<remove fileExtension=".js.gz" />
<mimeMap fileExtension=".js.gz" mimeType="application/javascript" />
<remove fileExtension=".symbols.json.gz" />
<mimeMap fileExtension=".symbols.json.gz" mimeType="application/octet-stream" />
<!-- 以下几行是针对不带解压回退、使用 Brotli 压缩的构建所需的。 -->
<remove fileExtension=".data.br" />
<mimeMap fileExtension=".data.br" mimeType="application/octet-stream" />
<remove fileExtension=".wasm.br" />
<mimeMap fileExtension=".wasm.br" mimeType="application/wasm" />
<remove fileExtension=".js.br" />
<mimeMap fileExtension=".js.br" mimeType="application/javascript" />
<remove fileExtension=".symbols.json.br" />
<mimeMap fileExtension=".symbols.json.br" mimeType="application/octet-stream" />
</staticContent>
<!--
托管不带解压回退的压缩 Unity 构建依赖于浏览器原生解压,
因此应为压缩的构建文件添加适当的 "Content-Encoding" 响应头。
注意:如果未安装 "URL Rewrite" 模块,则使用以下部分时 IIS 将引发异常。
从 https://www.iis.net/downloads/microsoft/url-rewrite 下载 "URL Rewrite" 模块
-->
<rewrite>
<outboundRules>
<!--
注意:如果多次使用相同的规则名称,IIS 将引发异常。
为了避免与服务器上已经存在的配置发生冲突,应使用 <remove> 元素移除相应扩展名的 mimeType,
然后使用 <mimeMap> 元素添加 mimeType。
-->
<!-- 以下部分是针对不带解压回退、使用 gzip 压缩的构建所需的。 -->
<remove name="Append gzip Content-Encoding header" />
<rule name="Append gzip Content-Encoding header">
<match serverVariable="RESPONSE_Content-Encoding" pattern=".*" />
<conditions>
<add input="{REQUEST_FILENAME}" pattern="\.gz$" />
</conditions>
<action type="Rewrite" value="gzip" />
</rule>
<!-- 以下部分是针对不带解压回退、使用 Brotli 压缩的构建所需的。 -->
<remove name="Append brotli Content-Encoding header" />
<rule name="Append brotli Content-Encoding header">
<match serverVariable="RESPONSE_Content-Encoding" pattern=".*" />
<conditions>
<add input="{REQUEST_FILENAME}" pattern="\.br$" />
</conditions>
<action type="Rewrite" value="br" />
</rule>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>
IIS 服务器配置(未压缩的 WebGL 构建)
<?xml version="1.0" encoding="UTF-8"?>
<!--
此服务器配置可用于未压缩的 WebGL 构建。
此配置文件应上传到服务器 "<Application Folder>/Build/web.config"
-->
<configuration>
<system.webServer>
<!--
IIS 默认不提供 .data 和 .wasm 文件(以及某些情况下 .json 文件)的处理程序,
因此除非明确指定它们的 mimeType,否则这些文件不会被提供。
-->
<staticContent>
<!--
注意:如果为同一扩展名多次指定 mimeType,IIS 将引发异常。
为了避免与服务器上已经存在的配置发生冲突,应使用 <remove> 元素移除相应扩展名的 mimeType,
然后使用 <mimeMap> 元素添加 mimeType。
-->
<remove fileExtension=".data" />
<mimeMap fileExtension=".data" mimeType="application/octet-stream" />
<remove fileExtension=".wasm" />
<mimeMap fileExtension=".wasm" mimeType="application/wasm" />
<remove fileExtension=".symbols.json" />
<mimeMap fileExtension=".symbols.json" mimeType="application/octet-stream" />
</staticContent>
</system.webServer>
</configuration>
参考
- https://www.khronos.org/webgl/
- https://developer.mozilla.org/en-US/
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Polic
- https://docs.unity3d.com/2022.3/Documentation/Manual/DrawCallBatching.html
- Texture compression format overview
- Lightmaps: Technical information
- WebAssembly 官方网站
- MDN Web 文档上的 WebAssembly 指南
- –js-library Emscripten option
- https://www.w3.org/TR/webgpu/#security-memory-resources
- https://blog.csdn.net/shaobing32/article/details/129089513
转载自CSDN-专业IT技术社区
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/sinat_34012434/article/details/141531817