optimize-web

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Performance Notes

性能说明

  • Take your time to do this thoroughly.
  • Quality is more important than speed.
  • 请花时间仔细完成这些步骤。
  • 质量比速度更重要。

Running C# in the Editor

在编辑器中运行C#代码

Every step below that reads or writes a Player Setting runs inside a live Editor through the Unity CLI. The
unity-cli
skill owns getting you there
— installing the CLI, confirming a connected Editor, adding the project's
com.unity.pipeline
package, telling a genuinely absent Editor apart from one stuck in Safe Mode, and discovering the Editor's command catalog. Follow it first; don't re-derive any of it here.
Two things it can't know for you:
  • You need
    eval
    in particular
    , not just a reachable Editor. Confirm it appears in the catalog. Its presence depends on the Pipeline package version, not on the CLI, so a healthy install can still lack it — if it's missing, say so and stop.
  • Player Settings can be read from
    ProjectSettings/ProjectSettings.asset
    in a pinch, but do not write them that way.
    The serialized names don't match the API names, several of these settings are per-build-target, and a hand-edited value silently disagrees with what the build actually uses. An unreachable Editor is a stop for the write steps.
Run C# with
unity command eval --code '<snippet>'
.
unity command
defaults to a 30 second timeout.
以下每一步中涉及读取或写入Player Setting的操作,都需要通过Unity CLI在运行中的编辑器内执行。
unity-cli
技能负责帮你完成前置准备
——安装CLI、确认已连接编辑器、添加项目的
com.unity.pipeline
包、区分真正未启动的编辑器和卡在安全模式的编辑器,以及发现编辑器的命令目录。请先遵循该技能的指引;此处不再重复相关内容。
有两件事它无法为你判断:
  • 你特别需要
    eval
    命令
    ,而不只是能连接到编辑器。请确认它出现在命令目录中。它是否存在取决于Pipeline包的版本,而非CLI,因此即使安装正常也可能缺少该命令——如果缺失,请告知用户并停止操作。
  • 必要时可以从
    ProjectSettings/ProjectSettings.asset
    读取Player Setting,但请勿通过该方式写入
    。序列化名称与API名称不匹配,其中多项设置是按构建目标区分的,手动编辑的值可能与构建实际使用的值不一致且无提示。如果无法连接到编辑器,写入步骤无法进行。
使用
unity command eval --code '<snippet>'
运行C#代码。
unity command
默认超时时间为30秒。

Passing C# to
eval

eval
传递C#代码

eval
compiles a statement block, not a file. Two consequences, both compile errors rather than warnings:
  • No
    using
    directives.
    The compiler reads
    using UnityEditor;
    as a resource-disposal statement and rejects it (
    CS0210
    ).
  • Types must be fully qualified. A bare
    PlayerSettings
    does not resolve (
    CS0246
    ), and a bare
    Object
    is ambiguous with
    object
    (
    CS0104
    ).
eval
编译的是语句块,而非文件。这会导致两个后果,均为编译错误而非警告:
  • 不支持
    using
    指令
    。编译器会将
    using UnityEditor;
    视为资源释放语句并拒绝执行(错误码
    CS0210
    )。
  • 类型必须完全限定。直接使用
    PlayerSettings
    无法解析(错误码
    CS0246
    ),直接使用
    Object
    会与
    object
    产生歧义(错误码
    CS0104
    )。

Reading the settings this skill audits

读取本技能审计的设置

One call returns the whole Pre-Flight picture. Verified against Unity 6000.5.7f1:
csharp
var target = UnityEditor.Build.NamedBuildTarget.WebGL;
var w = new System.Collections.Generic.List<string>();
w.Add($"activeBuildTarget={UnityEditor.EditorUserBuildSettings.activeBuildTarget}");
w.Add($"compressionFormat={UnityEditor.PlayerSettings.WebGL.compressionFormat}");
w.Add($"decompressionFallback={UnityEditor.PlayerSettings.WebGL.decompressionFallback}");
w.Add($"stripEngineCode={UnityEditor.PlayerSettings.stripEngineCode}");
w.Add($"managedStrippingLevel={UnityEditor.PlayerSettings.GetManagedStrippingLevel(target)}");
w.Add($"il2cppCodeGeneration={UnityEditor.PlayerSettings.GetIl2CppCodeGeneration(target)}");
w.Add($"apiCompatibilityLevel={UnityEditor.PlayerSettings.GetApiCompatibilityLevel(target)}");
w.Add($"exceptionSupport={UnityEditor.PlayerSettings.WebGL.exceptionSupport}");
w.Add($"debugSymbolMode={UnityEditor.PlayerSettings.WebGL.debugSymbolMode}");
w.Add($"dataCaching={UnityEditor.PlayerSettings.WebGL.dataCaching}");
w.Add($"wasm2023={UnityEditor.PlayerSettings.WebGL.wasm2023}");
w.Add($"initialMemorySize={UnityEditor.PlayerSettings.WebGL.initialMemorySize}");
w.Add($"maximumMemorySize={UnityEditor.PlayerSettings.WebGL.maximumMemorySize}");
w.Add($"memoryGrowthMode={UnityEditor.PlayerSettings.WebGL.memoryGrowthMode}");
w.Add($"targetFrameRate={UnityEngine.Application.targetFrameRate}");
w.Add($"vSyncCount={UnityEngine.QualitySettings.vSyncCount}");
return string.Join("\n", w);
Three API names to get right, because the obvious spellings do not exist and fail to compile:
SettingCorrect formDoes NOT exist
Managed stripping level
PlayerSettings.GetManagedStrippingLevel(NamedBuildTarget.WebGL)
PlayerSettings.managedStrippingLevel
Wasm code optimization
UnityEditor.WebGL.UserBuildSettings.codeOptimization
PlayerSettings.WebGL.codeOptimization
,
PlayerSettings.WebGL.optimizationLevel
IL2CPP code generation
PlayerSettings.GetIl2CppCodeGeneration(NamedBuildTarget.WebGL)
a bare property
UserBuildSettings
lives in the WebGL build-support module, so it only resolves when that module is installed. Read it in a separate call from the rest, and treat a resolution failure as "the Web module isn't installed" rather than as a bad snippet.
一次调用即可返回完整的预检信息。已在Unity 6000.5.7f1版本验证:
csharp
var target = UnityEditor.Build.NamedBuildTarget.WebGL;
var w = new System.Collections.Generic.List<string>();
w.Add($"activeBuildTarget={UnityEditor.EditorUserBuildSettings.activeBuildTarget}");
w.Add($"compressionFormat={UnityEditor.PlayerSettings.WebGL.compressionFormat}");
w.Add($"decompressionFallback={UnityEditor.PlayerSettings.WebGL.decompressionFallback}");
w.Add($"stripEngineCode={UnityEditor.PlayerSettings.stripEngineCode}");
w.Add($"managedStrippingLevel={UnityEditor.PlayerSettings.GetManagedStrippingLevel(target)}");
w.Add($"il2cppCodeGeneration={UnityEditor.PlayerSettings.GetIl2CppCodeGeneration(target)}");
w.Add($"apiCompatibilityLevel={UnityEditor.PlayerSettings.GetApiCompatibilityLevel(target)}");
w.Add($"exceptionSupport={UnityEditor.PlayerSettings.WebGL.exceptionSupport}");
w.Add($"debugSymbolMode={UnityEditor.PlayerSettings.WebGL.debugSymbolMode}");
w.Add($"dataCaching={UnityEditor.PlayerSettings.WebGL.dataCaching}");
w.Add($"wasm2023={UnityEditor.PlayerSettings.WebGL.wasm2023}");
w.Add($"initialMemorySize={UnityEditor.PlayerSettings.WebGL.initialMemorySize}");
w.Add($"maximumMemorySize={UnityEditor.PlayerSettings.WebGL.maximumMemorySize}");
w.Add($"memoryGrowthMode={UnityEditor.PlayerSettings.WebGL.memoryGrowthMode}");
w.Add($"targetFrameRate={UnityEngine.Application.targetFrameRate}");
w.Add($"vSyncCount={UnityEngine.QualitySettings.vSyncCount}");
return string.Join("\n", w);
三个必须正确使用的API名称,因为直观的拼写并不存在,会导致编译失败:
设置项正确写法不存在的写法
托管代码剥离级别
PlayerSettings.GetManagedStrippingLevel(NamedBuildTarget.WebGL)
PlayerSettings.managedStrippingLevel
Wasm代码优化
UnityEditor.WebGL.UserBuildSettings.codeOptimization
PlayerSettings.WebGL.codeOptimization
,
PlayerSettings.WebGL.optimizationLevel
IL2CPP代码生成
PlayerSettings.GetIl2CppCodeGeneration(NamedBuildTarget.WebGL)
直接使用属性
UserBuildSettings
属于WebGL构建支持模块,因此仅当该模块已安装时才能解析。请单独调用读取该设置,若解析失败则视为“Web模块未安装”,而非代码片段错误。

Applying the settings

应用设置

Most of the writes in this skill are a single batch, and resources/WebOptimizer.cs already is that batch. It declares a class with a
[MenuItem]
, so it is a project file, not
eval
input
— a class declaration cannot be flattened into a statement block. Save it under
Assets/Editor/
, let Unity compile, then invoke it in one line:
csharp
UnityEditor.EditorApplication.ExecuteMenuItem("Tools/Apply Web Release Settings");
Keep its
using
directives; they are correct in a file. For one-off changes — a single quality level, a frame-rate flip — an inline
eval
statement is fine.
本技能中的大多数写入操作可批量完成,resources/WebOptimizer.cs已实现该批量操作。它声明了一个带有
[MenuItem]
的类,因此是项目文件,而非
eval
输入
——类声明无法简化为语句块。将其保存到
Assets/Editor/
目录,等待Unity编译完成后,通过一行代码调用:
csharp
UnityEditor.EditorApplication.ExecuteMenuItem("Tools/Apply Web Release Settings");
保留文件中的
using
指令;这些指令在文件中是有效的。对于单次修改——比如调整单个质量级别、切换帧率——使用内联
eval
语句即可。

0. Pre-Flight

0. 预检

  1. Confirm Web build target: Read, with the Pre-Flight snippet above,
    EditorUserBuildSettings.activeBuildTarget
    — must be
    WebGL
    ; if not, warn the user.
  2. Read compression and stripping settings: Read
    compressionFormat
    ,
    decompressionFallback
    ,
    stripEngineCode
    and the managed stripping level with the Pre-Flight snippet above. Note the stripping level is
    PlayerSettings.GetManagedStrippingLevel(NamedBuildTarget.WebGL)
    — there is no
    PlayerSettings.managedStrippingLevel
    property.
  3. Read exception and optimization settings: Read
    PlayerSettings.WebGL.exceptionSupport
    from the Pre-Flight snippet above. For the wasm code optimization level use
    UnityEditor.WebGL.UserBuildSettings.codeOptimization
    — the
    PlayerSettings.WebGL.codeOptimization
    and
    optimizationLevel
    spellings do not exist and will not compile.
  4. Read frame rate settings: Read, with the Pre-Flight snippet above,
    Application.targetFrameRate
    and
    QualitySettings.vSyncCount
    .
  5. Read additional player settings: Read, with the Pre-Flight snippet above,
    PlayerSettings.WebGL.dataCaching
    ,
    PlayerSettings.WebGL.debugSymbolMode
    ,
    PlayerSettings.WebGL.maximumMemorySize
    , and
    PlayerSettings.GetApiCompatibilityLevel
    .
  6. Proceed only after compression, stripping, frame rate, and player settings are confirmed.
  1. 确认Web构建目标:使用上述预检代码片段读取
    EditorUserBuildSettings.activeBuildTarget
    ——必须为
    WebGL
    ;若不是,请向用户发出警告。
  2. 读取压缩与剥离设置:使用上述预检代码片段读取
    compressionFormat
    decompressionFallback
    stripEngineCode
    以及托管代码剥离级别。注意剥离级别需使用
    PlayerSettings.GetManagedStrippingLevel(NamedBuildTarget.WebGL)
    ——不存在
    PlayerSettings.managedStrippingLevel
    属性。
  3. 读取异常与优化设置:使用上述预检代码片段读取
    PlayerSettings.WebGL.exceptionSupport
    。对于wasm代码优化级别,使用
    UnityEditor.WebGL.UserBuildSettings.codeOptimization
    ——
    PlayerSettings.WebGL.codeOptimization
    optimizationLevel
    这两种写法不存在,会导致编译失败。
  4. 读取帧率设置:使用上述预检代码片段读取
    Application.targetFrameRate
    QualitySettings.vSyncCount
  5. 读取额外的Player设置:使用上述预检代码片段读取
    PlayerSettings.WebGL.dataCaching
    PlayerSettings.WebGL.debugSymbolMode
    PlayerSettings.WebGL.maximumMemorySize
    以及
    PlayerSettings.GetApiCompatibilityLevel
  6. 仅在确认压缩、剥离、帧率和Player设置后,再继续后续操作。

1. Assess Current State

1. 评估当前状态

  1. Check Build Report: Instruct the user to open
    Window > General > Build Report
    after a build and identify the largest asset and code size contributors.
  2. Verify server configuration: Ask the user to confirm whether the hosting server sends
    Content-Encoding: br
    (Brotli) or
    Content-Encoding: gzip
    headers, and whether
    Content-Type: application/wasm
    is set for
    .wasm
    files.
  3. Check frame rate config: Confirm, with the Pre-Flight snippet above,
    Application.targetFrameRate
    — should be
    -1
    for Web (let the browser drive).
  4. Check memory settings: Read, with the Pre-Flight snippet above,
    PlayerSettings.WebGL.initialMemorySize
    and
    PlayerSettings.WebGL.memoryGrowthMode
    .
  5. Report findings before making recommendations.
  1. 查看构建报告:指导用户在构建完成后打开
    Window > General > Build Report
    ,识别体积最大的资源和代码贡献项。
  2. 验证服务器配置:询问用户确认托管服务器是否发送
    Content-Encoding: br
    (Brotli)或
    Content-Encoding: gzip
    头信息,以及是否为
    .wasm
    文件设置了
    Content-Type: application/wasm
  3. 检查帧率配置:使用上述预检代码片段确认
    Application.targetFrameRate
    ——Web端应设为
    -1
    (由浏览器控制)。
  4. 检查内存设置:使用上述预检代码片段读取
    PlayerSettings.WebGL.initialMemorySize
    PlayerSettings.WebGL.memoryGrowthMode
  5. 在给出建议前,先报告评估结果。

2. Understand Request

2. 理解用户需求

User SaysDefault Interpretation
"build too large" / "download too slow"Strip Engine Code on; Managed Stripping High; Disk Size + LTO; Brotli
"Decompression Fallback" / "slow startup"Decompression Fallback off; fix server to send Content-Encoding
"stutter in Chrome" / "stutter in Safari"Profile in browser DevTools; Safari caps at 60 fps
"excessive battery in browser"
OnDemandRendering
on static screens;
targetFrameRate = -1
"exceptions too large"None for release; Wasm 2023 exceptions if browser baseline allows
"set up CDN"Addressables remote groups + Brotli/Gzip on CDN
"WebAssembly 2023"Enable when browser baseline supports it — smaller and faster
"memory growth slow"Tune Initial Memory Size to peak estimate; use Geometric growth mode
"KTX" / "Basis Universal" / "texture formats unknown GPU"KTX2 with Basis Universal; ETC1S for size, UASTC for quality
"strip unused code" / "remove unused packages"Web Stripping Tool + remove unused packages + shader stripping
"quality settings for web"Quality Level to Very Low or Low; lower quality = faster load
"shader variants too many"Graphics settings: auto lightmap/fog modes; strip instancing + BRG variants; audit Always Included Shaders
"video not playing" / "audio issues"Video: URL-only or StreamingAssets; Audio: no AudioEffects on Web, use Mono, compress
"profiler symbols" / "can't read Wasm stacks"Embed profiling symbols via build processor or emscriptenArgs
"iOS crashes" / "Safari memory"iOS memory limits; set Initial Memory Size high rather than growing; Gigacage 2GB limit pre-iOS 18
用户表述默认解读
"构建体积过大" / "下载太慢"启用剥离引擎代码;设置托管代码剥离级别为高;开启磁盘大小优化+LTO;使用Brotli压缩
"需要解压回退" / "启动缓慢"关闭解压回退;修复服务器使其发送Content-Encoding头
"Chrome中卡顿" / "Safari中卡顿"使用浏览器DevTools分析性能;Safari帧率上限为60fps
"浏览器中电池消耗过高"静态画面启用
OnDemandRendering
;设置
targetFrameRate = -1
"异常处理体积过大"发布版本禁用异常处理;若浏览器基线支持则使用Wasm 2023异常模型
"配置CDN"使用Addressables远程组+CDN上的Brotli/Gzip压缩
"WebAssembly 2023"当浏览器基线支持时启用——体积更小、速度更快
"内存增长缓慢"根据峰值估算调整初始内存大小;使用几何增长模式
"KTX" / "Basis Universal" / "未知GPU纹理格式"使用带Basis Universal的KTX2;ETC1S优先考虑体积,UASTC优先考虑质量
"剥离未使用代码" / "移除未使用包"使用Web剥离工具+移除未使用包+着色器剥离
"Web端质量设置"质量级别设为极低或低;质量越低,加载速度越快
"着色器变体过多"图形设置:自动光照贴图/雾效模式;剥离实例化+BRG变体;审核始终包含的着色器
"视频无法播放" / "音频问题"视频:仅使用URL或StreamingAssets;音频:WebGL不支持AudioEffects,使用单声道并压缩
"性能分析符号" / "无法读取Wasm堆栈"通过构建处理器或emscriptenArgs嵌入性能分析符号
"iOS崩溃" / "Safari内存问题"iOS内存限制;设置较高的初始内存大小而非依赖增长;iOS 18之前Gigacage上限为2GB

3. Web Build Optimization Workflow

3. Web构建优化流程

IMPORTANT: One-click optimization script

重要:一键优化脚本

Always offer to generate this script for the user. Unity's official web optimization docs provide a single editor menu script that applies all recommended release settings at once. Place in
Assets/Editor/WebOptimizer.cs
— see resources/WebOptimizer.cs for the template.
Adapt the script to the user's project needs (e.g. keep exceptions if they use
try/catch
, switch Brotli to Gzip for HTTP hosting). This script is the single most impactful action for a new web project — it prevents settings from being missed.
始终主动为用户生成该脚本。Unity官方Web优化文档提供了一个编辑器菜单脚本,可一次性应用所有推荐的发布设置。将其放置在
Assets/Editor/WebOptimizer.cs
目录——模板可参考resources/WebOptimizer.cs
根据用户项目需求调整脚本(例如,如果项目使用
try/catch
则保留异常处理,针对HTTP托管将Brotli切换为Gzip)。对于新的Web项目,该脚本是最有效的操作——可避免遗漏设置。

Player Settings audit

Player Settings审计

Verify and set these values through
eval
:
SettingRelease recommendation
Compression FormatBrotli (HTTPS hosting); Gzip for HTTP
Decompression FallbackOff when server is correctly configured
Strip Engine CodeOn
Managed Stripping LevelHigh (release) / Medium (dev)
Code OptimizationDisk Size with LTO (release) / Build Times (dev)
WebAssembly Language Features2023 if browser baseline allows
Enable ExceptionsNone (smallest); Explicitly Thrown Only if
try/catch
required
Initial Memory SizeTune to peak estimate; too small causes expensive growth
Memory Growth ModeGeometric
API Compatibility Level.NET Standard 2.1 — smaller than .NET Framework
IL2CPP Code GenerationOptimize Size — smaller Wasm at slight runtime cost
Debug SymbolsOff for release; on for development builds only
Data CachingOn — caches asset data in browser IndexedDB for faster repeat loads
Strip Unused Mesh ComponentsOn — removes unused vertex attributes
Maximum Memory Size2048 MB default; up to 4096 for complex 3D (Firefox and Chrome < 119 have issues above 2048)
vSyncCount0 (browser handles pacing)
targetFrameRate-1 (use
requestAnimationFrame
)
通过
eval
验证并设置以下值:
设置项发布版本建议
压缩格式Brotli(HTTPS托管);HTTP托管使用Gzip
解压回退服务器配置正确时关闭
剥离引擎代码开启
托管代码剥离级别(发布版本)/ 中(开发版本)
代码优化磁盘大小优化+LTO(发布版本)/ 构建速度优先(开发版本)
WebAssembly语言特性浏览器基线支持时启用2023
启用异常处理(体积最小);若需要
try/catch
则设为仅显式抛出
初始内存大小根据峰值估算调整;过小会导致昂贵的内存增长操作
内存增长模式几何增长
API兼容性级别.NET Standard 2.1 —— 比.NET Framework体积更小
IL2CPP代码生成优化体积 —— 牺牲少量运行时性能以减小Wasm体积
调试符号发布版本关闭;仅在开发构建中开启
数据缓存开启 —— 在浏览器IndexedDB中缓存资源数据,加快重复加载速度
剥离未使用网格组件开启 —— 移除未使用的顶点属性
最大内存大小默认2048 MB;复杂3D项目可设为4096 MB(Firefox和Chrome 119以下版本超过2048 MB会有问题)
vSyncCount0(由浏览器控制 pacing)
targetFrameRate-1(使用
requestAnimationFrame

Compression and server configuration

压缩与服务器配置

CompressionUse whenNotes
BrotliHTTPS or localhostBest ratio; browsers accept only over secure contexts
GzipHTTP delivery, legacy CDNsUniversal
NoneLocal dev / file://Largest payload; do not ship
Configure the server to:
  • Serve
    .br
    files with
    Content-Encoding: br
    .
  • Serve
    .gz
    files with
    Content-Encoding: gzip
    .
  • Set
    Content-Type: application/wasm
    for
    .wasm
    ,
    application/javascript
    for
    .js
    .
  • Enable HTTP/2 or HTTP/3 to parallelize chunk fetches.
If the host cannot inject
Content-Encoding
: set Decompression Fallback = On as a fallback only — it adds ~150 KB JS and slows startup.
压缩方式使用场景说明
BrotliHTTPS或本地主机压缩率最佳;仅在安全上下文下被浏览器接受
GzipHTTP交付、传统CDN通用兼容
无压缩本地开发 / file://协议体积最大;请勿用于发布
服务器配置要求:
  • .br
    文件发送
    Content-Encoding: br
    头。
  • .gz
    文件发送
    Content-Encoding: gzip
    头。
  • .wasm
    设置
    Content-Type: application/wasm
    ,为
    .js
    设置
    application/javascript
  • 启用HTTP/2或HTTP/3以并行获取分片资源。
如果主机无法注入
Content-Encoding
头:仅作为回退方案,设置解压回退 = 开启——这会增加约150 KB的JS代码并减慢启动速度。

Exception handling

异常处理

SettingBuild sizeUse
NoneSmallestRelease builds where uncaught exceptions are acceptable
Explicitly Thrown OnlyModestDefault for projects that catch exceptions
FullLargest, slowestRarely needed; avoid for release
Wasm 2023 introduces a cheaper exception model; switching from Explicitly Thrown Only (legacy) to Wasm exceptions reduces both size and cost when browser targets support it.
设置项构建体积使用场景
最小可接受未捕获异常的发布版本
仅显式抛出中等使用异常捕获的项目默认设置
完整最大、最慢极少需要;发布版本避免使用
Wasm 2023引入了更高效的异常模型;当目标浏览器支持时,从传统的“仅显式抛出”切换为Wasm异常可同时减小体积和性能开销。

Remove unused resources

移除未使用资源

Three categories to audit for build size reduction:
1. Unused packages — Check
Packages/manifest.json
and the Package Manager In Project and Built-in views. Remove or disable packages the project does not use. The Input System package is a significant size contributor if unused.
2. Shader stripping — Configure in
Edit > Project Settings > Graphics
:
SettingRecommendation
Lightmap ModesAutomatic (strips unused lightmap shader variants)
Fog ModesAutomatic (strips unused fog shader variants)
Instancing VariantsStrip Unused
Batch Renderer Group VariantsStrip All (if BRGs are not used)
Always Included ShadersAudit and remove any shaders the project does not reference
Test after stripping — ensure no referenced shaders were removed.
3. Web Stripping Tool (
com.unity.web.stripping-tool
) — Analyzes the WebAssembly binary and identifies unused Unity engine submodules (e.g. 3D graphics in a 2D-only game). Install via Package Manager, profile the build, then configure which submodules to exclude. Can yield substantial size reductions beyond what Managed Stripping Level achieves alone.
可从三类资源入手缩减构建体积:
1. 未使用的包 —— 查看
Packages/manifest.json
以及Package Manager的项目中内置视图。移除或禁用项目未使用的包。若未使用Input System包,它会是体积的主要贡献者之一。
2. 着色器剥离 —— 在
Edit > Project Settings > Graphics
中配置:
设置项建议
光照贴图模式自动(剥离未使用的光照贴图着色器变体)
雾效模式自动(剥离未使用的雾效着色器变体)
实例化变体剥离未使用的变体
批处理渲染组变体全部剥离(若未使用BRG)
始终包含的着色器审核并移除项目未引用的着色器
剥离后进行测试——确保未移除被引用的着色器。
3. Web剥离工具 (
com.unity.web.stripping-tool
) —— 分析WebAssembly二进制文件并识别未使用的Unity引擎子模块(例如纯2D游戏中的3D图形模块)。通过Package Manager安装,分析构建结果,然后配置要排除的子模块。可在托管代码剥离级别之外进一步大幅缩减体积。

Quality settings for Web

Web端质量设置

Lower quality levels reduce load time and improve runtime performance. Set via
Edit > Project Settings > Quality
:
  • Use Very Low or Low as the default Web quality level.
  • Set it with
    eval
    :
    UnityEngine.QualitySettings.SetQualityLevel(0, true);
    where 0 = Very Low.
  • Consider creating a Web-specific quality level that disables features unnecessary in-browser (real-time shadows, post-processing effects, high particle counts).
降低质量级别可减少加载时间并提升运行时性能。通过
Edit > Project Settings > Quality
设置:
  • 使用极低作为Web端默认质量级别。
  • 通过
    eval
    设置:
    UnityEngine.QualitySettings.SetQualityLevel(0, true);
    其中0 = 极低。
  • 考虑创建Web专用质量级别,禁用浏览器中不必要的功能(实时阴影、后期处理效果、高粒子数量)。

Frame rate on Web

Web端帧率

  • Set it with
    eval
    :
    UnityEngine.Application.targetFrameRate = -1;
    — let the browser use
    requestAnimationFrame
    .
  • Note: Safari caps at 60 fps in WebGL; high-refresh targets do not apply.
  • Use
    OnDemandRendering.renderFrameInterval
    to drop to 5–10 fps on static/idle screens to save battery.
  • 通过
    eval
    设置:
    UnityEngine.Application.targetFrameRate = -1;
    ——让浏览器使用
    requestAnimationFrame
  • 注意:Safari在WebGL中帧率上限为60fps;高刷新率设置无效。
  • 在静态/空闲画面中使用
    OnDemandRendering.renderFrameInterval
    将帧率降至5–10fps以节省电量。

KTX / Basis Universal textures

KTX / Basis Universal纹理

KTX2 with Basis Universal supercompression ships a single texture file that transcodes at load time to the optimal GPU format for the browser's device (BC7 on desktop, ASTC on mobile, ETC2 on older Android). This avoids shipping separate texture variants for each GPU family — critical for Web where the target hardware is unknown.
TopicGuidance
PackageInstall
com.unity.cloud.ktx
(KtxUnity) via Package Manager
When to useRuntime-loaded textures via Addressables or asset bundles served to unknown GPU targets
When NOT to useTextures baked into the player build — Unity already selects the correct format at build time
SupercompressionUse ETC1S for smallest size (lossy, good for diffuse/albedo); UASTC for higher quality (near-lossless, better for normals/UI)
EncodingEncode offline with
toktx
or
basisu
CLI; do not encode at runtime
Linear dataSet
--assign_oetf linear
when encoding normal maps, masks, or data textures to avoid incorrect sRGB conversion
Mip mapsGenerate mips at encode time (
--genmipmap
) — browser-side mip generation is expensive
LoadingUse
KtxTexture.LoadFromStreamingAssets
or load bytes via UnityWebRequest and call
KtxTexture.LoadFromBytes
MemoryTranscoded textures are standard GPU textures; memory cost equals the target format, not the KTX2 file size
OrientationAlways include
--lower_left_maps_to_s0t0
to match Unity's UV convention
toktx
CLI examples:
See resources/toktx-examples.sh for commands covering albedo (ETC1S), normals/detail (UASTC), ICC profile errors, and linear data.
带Basis Universal超压缩的KTX2格式可打包单个纹理文件,加载时自动转码为浏览器设备的最优GPU格式(桌面端BC7、移动端ASTC、旧版Android设备ETC2)。这避免了为每个GPU家族单独打包纹理变体——对于目标硬件未知的Web场景至关重要。
主题指导
通过Package Manager安装
com.unity.cloud.ktx
(KtxUnity)
使用场景通过Addressables或资源包加载的运行时纹理,目标为未知GPU设备
不适用场景烘焙到播放器构建中的纹理——Unity会在构建时自动选择正确格式
超压缩使用ETC1S追求最小体积(有损压缩,适用于漫反射/基础色);使用UASTC追求更高质量(接近无损,适用于法线/UI)
编码使用
toktx
basisu
CLI离线编码;请勿在运行时编码
线性数据编码法线贴图、遮罩或数据纹理时设置
--assign_oetf linear
,避免错误的sRGB转换
Mip贴图编码时生成mip贴图(
--genmipmap
)——浏览器端生成mip贴图开销较大
加载使用
KtxTexture.LoadFromStreamingAssets
或通过UnityWebRequest加载字节后调用
KtxTexture.LoadFromBytes
内存转码后的纹理为标准GPU纹理;内存开销等于目标格式大小,而非KTX2文件大小
方向始终添加
--lower_left_maps_to_s0t0
以匹配Unity的UV约定
toktx
CLI示例
:参考resources/toktx-examples.sh中的命令,涵盖基础色(ETC1S)、法线/细节(UASTC)、ICC配置文件错误和线性数据等场景。

Streaming on Web

Web端流式加载

  • Use Addressables with remote groups hosted on a CDN with Brotli / Gzip.
  • Avoid bundling the entire game into the initial download; stream levels on demand.
  • Target < 30 MB initial download for "instant play"; level data follows.
  • For streamed textures targeting mixed GPU hardware, prefer KTX2 bundles over per-platform variants — one bundle serves all browsers.
  • 使用Addressables并将远程组托管在支持Brotli/Gzip压缩的CDN上。
  • 避免将整个游戏打包到初始下载包中;按需流式加载关卡。
  • 初始下载体积目标<30 MB以实现“即点即玩”;关卡数据后续加载。
  • 针对混合GPU硬件的流式纹理,优先选择KTX2包而非按平台拆分的变体——一个包即可适配所有浏览器。

Profiling Web builds

Web构建性能分析

ToolUseNotes
Chrome DevTools > PerformanceCPU flamegraph; main-thread analysisDefault first stop for WebGL hitches; inspect Wasm call stacks
Chrome DevTools > MemoryHeap snapshot; allocation timelineFind JS/Wasm memory leaks; compare snapshots before/after scene load
Firefox ProfilerCross-platform; shareable URLs; native + Wasm viewBetter Wasm symbolication than Chrome in some cases; shareable profile URLs for team review
Safari Web InspectoriOS Safari and macOS Safari debuggingRequired for Safari-specific issues; WebGL/Wasm runtime differs from Chromium
Unity Profiler over WebSocketConnect to a development build; standard markersUse for Unity-side markers (GC, rendering, scripts); does not capture browser-side overhead
Symptom → tool quick reference:
SymptomFirst-line toolSecond-line tool
WebGL hitch / stutterChrome DevTools > PerformanceFirefox Profiler
Memory climbing over timeChrome DevTools > MemoryUnity Memory Profiler (WebSocket)
Slow initial loadChrome DevTools > NetworkBuild Report Inspector
Safari-only rendering issueSafari Web InspectorCompare with Chrome DevTools
Embedding profiling symbols — browser profilers show mangled Wasm function names by default. To get readable C# method names in Chrome/Firefox flamegraphs, either enable
Player Settings > Publishing > Debug Symbols
for dev builds, or add a build processor:
csharp
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;

public class WebProfilingBuildProcessor : IPreprocessBuildWithReport
{
    public int callbackOrder => 0;
    public void OnPreprocessBuild(BuildReport report)
    {
        PlayerSettings.SetAdditionalIl2CppArgs("--compiler-flags=--profiling-funcs");
    }
}
Emscripten built-in profilers — enable one at a time via
PlayerSettings.WebGL.emscriptenArgs
:
FlagWhat it shows
--cpuprofiler
CPU profiler overlay in browser
--memoryprofiler
Visual memory map (white=allocated unused, pink=stack, blue=dynamic, green=fragmented)
--threadprofiler
Thread activity profiler
GPU debugging — No Frame Debugger support on Web. Use Spector.js as a browser-based alternative — it captures draw calls and WebGL state.
Firefox
about:memory
— type
about:memory
as a URL in Firefox, click Measure to see per-tab breakdown: WASM code size, WASM heap, .data file, web audio. Watch for WASM heap > 300 MB (crash risk, especially on iOS Safari).
Editor Play Mode does not represent browser runtime; always measure in browser. Chrome and Safari GC and JIT behavior differ — test both.
工具使用场景说明
Chrome DevTools > PerformanceCPU火焰图;主线程分析WebGL卡顿问题的默认首选工具;检查Wasm调用栈
Chrome DevTools > Memory堆快照;分配时间线查找JS/Wasm内存泄漏;对比场景加载前后的快照
Firefox Profiler跨平台;可分享URL;原生+Wasm视图在某些情况下比Chrome的Wasm符号化效果更好;可生成分享链接供团队评审
Safari Web InspectoriOS Safari和macOS Safari调试排查Safari专属问题的必备工具;WebGL/Wasm运行时与Chromium不同
通过WebSocket连接的Unity Profiler连接到开发构建;标准标记用于分析Unity侧标记(GC、渲染、脚本);无法捕获浏览器端开销
症状→工具快速参考
症状首选工具次选工具
WebGL卡顿/停顿Chrome DevTools > PerformanceFirefox Profiler
内存持续增长Chrome DevTools > MemoryUnity内存分析器(WebSocket)
初始加载缓慢Chrome DevTools > NetworkBuild Report Inspector
Safari专属渲染问题Safari Web Inspector与Chrome DevTools对比
嵌入性能分析符号——浏览器性能分析器默认显示混淆的Wasm函数名。要在Chrome/Firefox火焰图中显示可读的C#方法名,可在开发构建中启用
Player Settings > Publishing > Debug Symbols
,或添加构建处理器:
csharp
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;

public class WebProfilingBuildProcessor : IPreprocessBuildWithReport
{
    public int callbackOrder => 0;
    public void OnPreprocessBuild(BuildReport report)
    {
        PlayerSettings.SetAdditionalIl2CppArgs("--compiler-flags=--profiling-funcs");
    }
}
Emscripten内置性能分析器——通过
PlayerSettings.WebGL.emscriptenArgs
每次启用一个:
标志显示内容
--cpuprofiler
浏览器中的CPU分析器浮层
--memoryprofiler
可视化内存地图(白色=已分配未使用,粉色=栈,蓝色=动态分配,绿色=碎片)
--threadprofiler
线程活动分析器
GPU调试——Web端不支持Frame Debugger。使用Spector.js作为浏览器端替代工具——它可捕获绘制调用和WebGL状态。
Firefox
about:memory
——在Firefox中输入
about:memory
作为URL,点击Measure查看每个标签页的内存细分:WASM代码大小、WASM堆、.data文件、Web音频。注意WASM堆>300 MB时存在崩溃风险,尤其是在iOS Safari中。
编辑器运行模式无法代表浏览器运行时;始终在浏览器中测试。Chrome和Safari的GC和JIT行为不同——需同时测试两者。

Web memory directives

Web内存指令

  • Disable Read/Write Enabled on textures and meshes — it duplicates data into the WASM heap.
  • Reduce
    .data
    file size by moving assets to Addressables or AssetBundles.
  • Use compressed texture formats (KTX2/Basis) to reduce both download and decoded memory cost.
  • 禁用纹理和网格的读写启用——这会将数据复制到WASM堆中。
  • 通过将资源移至Addressables或AssetBundles减小.data文件大小。
  • 使用压缩纹理格式(KTX2/Basis)同时减少下载体积和解码后的内存开销。

iOS Safari memory limits

iOS Safari内存限制

  • iOS < 18: WebContent process limit ~1.5 GB. WASM memory (Gigacage) capped at 2 GB. Typed arrays share this pool. On iPhone X (iOS 16) heap growth caps at ~512 MB, but setting Initial Memory Size to 512 MB–1.5 GB upfront works.
  • iOS 18+: Limits largely lifted; iPhone 11 can allocate ~4 GB.
  • On iOS, set Initial Memory Size to the target peak rather than relying on growth — Safari handles large upfront allocations better than incremental growth.
  • WASM heap > 300 MB risks crashes on older iOS; target < 200 MB for broad compatibility.
  • iOS < 18:WebContent进程限制约1.5 GB。WASM内存(Gigacage)上限为2 GB。类型数组共享该内存池。iPhone X(iOS 16)上堆增长上限约为512 MB,但预先设置初始内存大小为512 MB–1.5 GB可行。
  • iOS 18+:限制大幅放宽;iPhone 11可分配约4 GB内存。
  • 在iOS上,初始内存大小设为目标峰值而非依赖增长——Safari处理大内存预分配比增量增长更友好。
  • WASM堆>300 MB在旧版iOS上易崩溃;为实现广泛兼容,目标应<200 MB。

Video and audio on Web

Web端视频与音频

  • Video: Playback only works from a URL (server with CORS enabled) or from StreamingAssets. On iOS the server must support HTTP range requests for streaming. Use browser-compatible formats (MP4/H.264).
  • Audio: AudioEffects (mixer effects) require compute shaders — not available on WebGL. Mixers and MixerGroups work for volume control only. Set audio to Mono to improve loading. If
    about:memory
    shows web audio > 100 MB, audio is likely uncompressed — switch to Vorbis.
  • 视频:仅支持从URL(启用CORS的服务器)或StreamingAssets播放。在iOS上,服务器必须支持HTTP范围请求才能流式播放。使用浏览器兼容格式(MP4/H.264)。
  • 音频:AudioEffects(混音器效果)需要计算着色器——WebGL不支持。混音器和混音器组仅能用于音量控制。将音频设置为单声道以加快加载速度。若
    about:memory
    显示Web音频>100 MB,音频可能未压缩——切换为Vorbis格式。

Canvas and DPI

Canvas与DPI

If the canvas is scaled up it takes the new resolution. Use
devicePixelRatio
in the web template to offset DPI scaling and avoid rendering at unnecessarily high resolution.
如果Canvas被放大,会采用新分辨率。在Web模板中使用
devicePixelRatio
抵消DPI缩放,避免不必要的高分辨率渲染。

4. Validation

4. 验证

  1. Re-read the Player Settings with the Pre-Flight snippet (compression, stripping, exceptions, targetFrameRate).
  2. Rebuild the player and compare Build Report file sizes with baseline.
  3. Verify in at least Chrome and Safari (GC and JIT behavior differ).
  4. Max 3 iterations before asking the user for feedback.
  1. 使用预检代码片段重新读取Player Settings(压缩、剥离、异常处理、targetFrameRate)。
  2. 重新构建播放器并对比构建报告中的文件大小与基线。
  3. 至少在Chrome和Safari中验证(两者的GC和JIT行为不同)。
  4. 最多进行3次迭代,然后向用户寻求反馈。

5. Troubleshooting

5. 故障排除

Build still large after enabling Strip Engine Code

启用剥离引擎代码后构建体积仍然过大

  1. Is Managed Stripping Level set to Medium or Low? → Set to High for release.
  2. Are plug-ins using reflection to access engine modules that would otherwise be stripped? → Add a
    link.xml
    to preserve needed symbols.
  3. Is Exceptions set to Full? → Full adds the largest code overhead; switch to None or Explicitly Thrown Only.
  1. 托管代码剥离级别是否设为中或低?→ 发布版本设为高。
  2. 是否有插件使用反射访问原本会被剥离的引擎模块?→ 添加
    link.xml
    保留所需符号。
  3. 异常处理是否设为完整?→ 完整模式会带来最大的代码开销;切换为无或仅显式抛出。

Brotli not working — Decompression Fallback required

Brotli无法工作——需要解压回退

  1. Is the server sending
    Content-Encoding: br
    ? → Without this header the browser won't decompress; the fallback JS decompressor is then needed.
  2. Is the build hosted over HTTP (not HTTPS)? → Brotli requires a secure context; degrade to Gzip for HTTP hosting.
  1. 服务器是否发送
    Content-Encoding: br
    头?→ 没有该头,浏览器不会解压;此时需要回退JS解压程序。
  2. 构建是否通过HTTP(而非HTTPS)托管?→ Brotli需要安全上下文;HTTP托管降级为Gzip。

Stutter in Safari but not Chrome

Safari中卡顿但Chrome中正常

  1. Does the project set
    Application.targetFrameRate = 60
    ? → On Safari WebGL this conflicts with browser pacing; set to
    -1
    .
  2. Are there shaders that behave differently on Safari's WebGL implementation? → Test on device; Safari's WebGL/Wasm runtime differs from Chromium — some GLSL constructs are handled differently.
  1. 项目是否设置了
    Application.targetFrameRate = 60
    ?→ 在Safari WebGL中这会与浏览器pacing冲突;设为
    -1
  2. 是否存在在Safari WebGL实现中表现不同的着色器?→ 在设备上测试;Safari的WebGL/Wasm运行时与Chromium不同——部分GLSL语法处理方式有差异。

Memory growth slow path triggered

触发内存增长慢路径

  1. Is Initial Memory Size too small for the project's peak? → Wasm memory growth requires a full buffer copy; set Initial Memory Size to a realistic peak estimate.
  2. Is Memory Growth Mode set to Linear? → Switch to Geometric for saner growth curve.
  1. 初始内存大小是否远小于项目峰值?→ Wasm内存增长需要完整的缓冲区复制;将初始内存大小设为合理的峰值估算值。
  2. 内存增长模式是否设为线性?→ 切换为几何增长以获得更合理的增长曲线。

Frame rate set to 60 but browser runs erratically

帧率设为60但浏览器运行不稳定

  1. Is
    Application.targetFrameRate = 60
    set in code? → On Web this conflicts with
    requestAnimationFrame
    browser pacing. Set to
    -1
    .
  2. Is
    vSyncCount
    non-zero? → Set to 0; the browser handles pacing.
  1. 代码中是否设置了
    Application.targetFrameRate = 60
    ?→ 在Web端这会与
    requestAnimationFrame
    浏览器pacing冲突。设为
    -1
  2. vSyncCount
    是否非零?→ 设为0;由浏览器控制pacing。

Firefox cache rejecting large files

Firefox缓存拒绝大文件

Firefox limits individual cache entries via
browser.cache.disk.max_entry_size
. If the build exceeds this (default ~50 MB), assets won't cache. Solution: use Addressables to split into bundles < 51 MB, or instruct users to increase the setting in
about:config
.
Firefox通过
browser.cache.disk.max_entry_size
限制单个缓存条目大小。如果构建超过该值(默认约50 MB),资源将无法缓存。解决方案:使用Addressables拆分为<51 MB的包,或指导用户在
about:config
中增大该设置。

Local dev server setup

本地开发服务器设置

For testing builds locally with proper MIME types:
bash
undefined
要在本地测试构建并确保正确的MIME类型:
bash
undefined

Python (HTTP)

Python (HTTP)

python -m http.server 55553 -d path/to/build
python -m http.server 55553 -d path/to/build

Node.js (install serve-handler)

Node.js(需安装serve-handler)

npx serve path/to/build -l 3001

For Brotli testing, use HTTPS — Brotli requires a secure context. Generate a self-signed cert with OpenSSL for local testing.
npx serve path/to/build -l 3001

测试Brotli需使用HTTPS——Brotli需要安全上下文。使用OpenSSL生成自签名证书用于本地测试。

6. Completion

6. 完成

  • Summarize: initial download size delta, settings changed (compression, stripping, exceptions, targetFrameRate), server configuration confirmed.
  • List follow-up actions: CDN setup for Addressables remote groups, Safari testing, Wasm 2023 feature set upgrade when browser baseline allows.
  • 总结:初始下载体积变化、修改的设置(压缩、剥离、异常处理、targetFrameRate)、已确认的服务器配置。
  • 列出后续操作:为Addressables远程组配置CDN、Safari测试、当浏览器基线支持时升级到Wasm 2023特性集。

See also

另请参阅

These point at Unity tooling rather than other skills, because the topics they cover are not in this plugin:
  • Addressables package — remote groups served over a CDN, when the download budget needs content moved out of the initial payload.
  • Unity Profiler, connected to the browser — the cross-platform profiling methodology. Section 3 covers the Web-specific part of attaching it.
  • Shader variant stripping (Graphics settings → Shader Stripping, and
    ShaderVariantCollection
    ) — variant count feeds directly into Wasm size, so it is worth checking when stripping alone hasn't moved the number.
  • Project Settings → Player — the same flags this skill reads, if the user would rather see them in the inspector than have them reported.
  • Mobile browser battery behaviour follows the same frame-rate and quality-level guidance in Sections 3 and 4; there is no separate mobile path here.
以下指向Unity工具而非其他技能,因为它们涵盖的主题不在本插件中:
  • Addressables包——当下载预算要求将内容移出初始包时,使用CDN托管远程组。
  • 连接到浏览器的Unity Profiler——跨平台性能分析方法。第3节涵盖了连接它的Web专属部分。
  • 着色器变体剥离(图形设置→着色器剥离,以及
    ShaderVariantCollection
    )——变体数量直接影响Wasm体积,因此当仅靠剥离无法优化时值得检查。
  • Project Settings → Player——本技能读取的所有标志,若用户更愿意在检视面板中查看而非通过报告获取。
  • 移动浏览器电池行为遵循第3和第4节中的帧率与质量级别指导;此处无需单独的移动端流程。