# 最佳实践:Unity 小游戏开启 Wasm Exception 指南

# 背景

# 问题:JS 模拟异常的性能与包体开销

Unity WebGL 构建默认使用 JS 模拟异常(-fexceptions。在这种模式下,所有 try 块内的函数调用在进入 Wasm 执行前,都必须先跳转到 JavaScript 层记录堆栈信息,执行完再跳回 Wasm。

这带来两方面开销:

  • 性能开销:JS ↔ Wasm 的跨层跳转发生在每一次函数调用上,无论是否真的抛出了异常。在异常密集或调用链较深的代码路径中,这一开销尤为显著。
  • 包体开销:JS 模拟异常需要在 Wasm 二进制中保留大量辅助数据和跳转表,导致 .wasm 文件体积偏大。更隐蔽的问题是:即使将 PlayerSettings 中的 Enable Exceptions 设为 None,旧版 Unity 也并不会彻底移除这些异常处理指令,JS ↔ Wasm 跳转依然存在。

原生 Wasm Exception(-fwasm-exceptions 是 WebAssembly 规范层面的异常支持,由 V8 / JavaScriptCore 引擎直接处理,异常完全在 Wasm 内部流转,无需 JS 中转。非异常路径的正常执行几乎零额外开销,包体中的异常相关指令也更精简。

# 平台兼容性

运行环境 支持情况 备注
iOS 真机(微信) ✅ 支持 Safari/WebKit JSC 15.2+ 原生支持 Wasm EH
Android 真机(微信) ✅ 支持 完整支持
PC ✅ 支持 完整支持

# Wasm-Exception 开启方法详解

# 团结引擎开启方法

如果你使用的是团结引擎(TuanJie Engine),上述问题已得到官方修复,无需手动处理。团结引擎在 PlayerSettings 中直接提供了以下选项:

  • Enable Exceptions → Wasm Exception:一键开启原生 Wasm 异常处理
  • Enable Exceptions Only For User Codes:仅对用户 C# 代码和 Library 开启异常,IL2CPP Runtime / CoreLib 关闭异常,进一步减少开销

设置入口:Project Settings → Player → Publish Settings

详见官方文档:团结引擎 — 异常处理优化


# Unity 2021.3.x 方案

# 原理

Unity 2021 使用 Emscripten 1.x,其构建系统在 WebGLSupport/BuildTools/Emscripten/emscripten/ 目录下提供了一个 emcc.py 包装脚本(非 Emscripten 原生文件)。

需要修改的核心逻辑有三处:

  1. 编译阶段:将 -fexceptions(JS 模拟)替换为 -fwasm-exceptions(原生)
  2. 链接阶段:处理 response file(.rsp)时,无条件追加 -fwasm-exceptions + 显式置零 DISABLE_EXCEPTION_THROWING=0DISABLE_EXCEPTION_CATCHING=0,消解 Unity Bee 构建系统注入的参数冲突
  3. 导出函数:在 EXPORTED_FUNCTIONS 中追加 _main,防止链接时卡死

# 快速操作(推荐)

下载 wasm-exception-patch-scripts.zip,解压后找到 apply_wasm_eh_2021.py

# 1. 用文本编辑器打开脚本,修改第一行 UNITY_EDITOR_ROOT 为你的 Unity 2021 Editor 安装路径
# Mac 示例:    /Applications/Unity/Hub/Editor/2021.3.30f1c1
# Windows 示例: C:\Program Files\Unity\Hub\Editor\2021.3.30f1c1\Editor

# 2. 执行脚本
python apply_wasm_eh_2021.py

# 3. 重启 Unity Editor,重新构建 WebGL

脚本会自动:

  • 备份原始 emcc.pyemcc.py.original(只备份一次,已有则跳过)
  • 用 patch 版本替换 emcc.py

# 手动操作

找到并编辑以下文件:

  • Mac/Applications/Unity/Hub/Editor/<版本>/PlaybackEngines/WebGLSupport/BuildTools/Emscripten/emscripten/emcc.py
  • Windows<Unity安装根目录>\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\emcc.py

修改 1:在文件顶部 sys.argv 赋值处添加替换逻辑(原文件约第 40 行附近):

# 将 JS 模拟异常替换为原生 Wasm Exception(作用于编译阶段)
sys.argv = ['-fwasm-exceptions' if item == '-fexceptions' else item for item in sys.argv]

修改 2:在 substitute_response_files 函数的 if arg.startswith('@'): 分支末尾追加(处理链接阶段):

# 确保链接阶段原生 Wasm Exception 相关功能全部被链接进去
new_args += ['-fwasm-exceptions']
new_args += ['-sDISABLE_EXCEPTION_THROWING=0']
new_args += ['-sDISABLE_EXCEPTION_CATCHING=0']

修改 3:在同一分支中,处理 EXPORTED_FUNCTIONS 参数时追加 _main

if 'EXPORTED_FUNCTIONS=' in resp_arg:
    new_args.append(resp_arg + ',_main')
else:
    new_args.append(resp_arg)

# Unity 2022.3.x 方案

# 原理

Unity 2022 使用 Emscripten 3.1.8,构建系统(Bee)直接调用 Emscripten 原生脚本。需要修改 3 个文件、4 处

文件 修改位置 作用
emcc.py substitute_response_files() 之后 编译 + 链接阶段注入 wasm-eh 参数
emcc.py 互斥检查(DISABLE_EXCEPTION_THROWING 消解 Bee rsp 与 wasm-eh 的参数冲突
building.py JS-EH LLVM flag 注入处 防止 -enable-emscripten-cxx-exceptions 与 wasm-eh 冲突
system_libs.py force_include 处理处 强制 --whole-archive 链接 libunwind-except.a,解决 __wasm_lpad_context 未定义

# 快速操作(推荐)

下载 wasm-exception-patch-scripts.zip,解压后找到 apply_wasm_eh_2022.py

# 1. 用文本编辑器打开脚本,修改第一行 UNITY_EDITOR_ROOT 为你的 Unity 2022 Editor 安装路径
# Mac 示例:    /Applications/Unity/Hub/Editor/2022.3.62f1c1
# Windows 示例: C:\Program Files\Unity\Hub\Editor\2022.3.62f1c1\Editor

# 2. 执行脚本
python apply_wasm_eh_2022.py

# 3. 重启 Unity Editor,重新构建 WebGL

脚本会自动:

  • 备份 emcc.pybuilding.pysystem_libs.py.original 文件
  • 应用 4 处 patch,并输出每处的应用状态

# 手动操作

所有文件位于:

  • Mac/Applications/Unity/Hub/Editor/<版本>/PlaybackEngines/WebGLSupport/BuildTools/Emscripten/emscripten/
  • Windows<Unity安装根目录>\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\

修改 1 — emcc.pysubstitute_response_files() 调用之后,约 L1051):

# --- Unity 2022 wasm-eh patch ---
# Bee 在链接阶段不传 -fexceptions,必须无条件追加 -fwasm-exceptions
args = ['-fwasm-exceptions' if a == '-fexceptions' else a for a in args]
args.append('-fwasm-exceptions')
args.append('-sDISABLE_EXCEPTION_THROWING=0')
args.append('-sDISABLE_EXCEPTION_CATCHING=0')

修改 2 — emcc.pyDISABLE_EXCEPTION_THROWING 互斥检查处,约 L1465):

# 原来:
if settings.DISABLE_EXCEPTION_THROWING and not settings.DISABLE_EXCEPTION_CATCHING:
    exit_with_error("DISABLE_EXCEPTION_THROWING was set ...")

# 改为:
if settings.DISABLE_EXCEPTION_THROWING and not settings.DISABLE_EXCEPTION_CATCHING:
    if settings.EXCEPTION_HANDLING:
        settings.DISABLE_EXCEPTION_CATCHING = 1
    else:
        exit_with_error("DISABLE_EXCEPTION_THROWING was set ...")

修改 3 — tools/building.py-enable-emscripten-cxx-exceptions 注入处,约 L230):

# 原来:
if not settings.DISABLE_EXCEPTION_CATCHING:
    args += ['-enable-emscripten-cxx-exceptions']

# 改为:
if not settings.DISABLE_EXCEPTION_CATCHING and not settings.EXCEPTION_HANDLING:
    args += ['-enable-emscripten-cxx-exceptions']

修改 4 — tools/system_libs.pyforce_include 处理,约 L1756):

# 原来:
force_include += forced
if force_include:

# 改为:
force_include += forced
if settings.EXCEPTION_HANDLING:
    force_include.append('libunwind')  # ensure __wasm_lpad_context resolved
if force_include:

# 验证

构建完成后,创建一个测试脚本挂载到场景中验证:

using System;
using UnityEngine;

public class WasmExceptionTest : MonoBehaviour
{
    void Start()
    {
        // 测试 1:捕获异常
        try
        {
            throw new Exception("这是一个测试异常,会被 catch");
        }
        catch (Exception e)
        {
            Debug.Log("捕获异常: " + e.Message);
        }
        finally
        {
            Debug.Log("finally 执行");
        }

        // 测试 2:未被捕获的异常(预期会在 console 看到报错)
        throw new Exception("这是一个测试异常,不会被 catch");
    }
}

预期结果:

  • 捕获异常: 这是一个测试异常,会被 catch
  • finally 执行
  • 第二个 throw 在 console 中正常报错,不会导致游戏静默挂起 ✅

# 恢复原始文件

所有脚本在第一次执行时会备份原文件(后缀 .original),如需回退:

# 2021:恢复 emcc.py
cp emcc.py.original emcc.py

# 2022:恢复三个文件
cp emcc.py.original emcc.py
cp tools/building.py.original tools/building.py
cp tools/system_libs.py.original tools/system_libs.py

# 常见问题

Q:升级 Unity 小版本后 patch 还有效吗?
A:Unity 同大版本(如 2022.3.x)内,Emscripten 工具链脚本通常不变,patch 可继续使用。升级后建议重新运行一键脚本(它会检测 patch 是否已应用并跳过已有的部分)。

Q:Windows 微信开发者工具报 CompileError,怎么办?
A: Windows 微信开发者工具内嵌的 Chromium V8 默认未启用 --experimental-wasm-eh flag,加载含 Exception section 的 wasm 文件时会报:

CompileError: WebAssembly.instantiate(): unexpected section <Exception>
(enable with --experimental-wasm-eh)

下载 微信开发者工具 Nightly Build,其内嵌 V8 默认启用了 --experimental-wasm-eh,可正常加载。

Q:2022 一键脚本提示 Patch A not found (may already be patched)
A:说明该 patch 已经应用过,无需重复操作。

文档变更日志(1条)
2026 年 08 月 18 日
文档描述优化
点击咨询小助手