optimize-web
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePerformance 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 skill owns getting you there — installing the CLI, confirming a connected
Editor, adding the project's 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.
unity-clicom.unity.pipelineTwo things it can't know for you:
- You need 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.
eval - Player Settings can be read from 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.
ProjectSettings/ProjectSettings.asset
Run C# with . defaults to a 30 second
timeout.
unity command eval --code '<snippet>'unity command以下每一步中涉及读取或写入Player Setting的操作,都需要通过Unity CLI在运行中的编辑器内执行。技能负责帮你完成前置准备——安装CLI、确认已连接编辑器、添加项目的包、区分真正未启动的编辑器和卡在安全模式的编辑器,以及发现编辑器的命令目录。请先遵循该技能的指引;此处不再重复相关内容。
unity-clicom.unity.pipeline有两件事它无法为你判断:
- 你特别需要命令,而不只是能连接到编辑器。请确认它出现在命令目录中。它是否存在取决于Pipeline包的版本,而非CLI,因此即使安装正常也可能缺少该命令——如果缺失,请告知用户并停止操作。
eval - 必要时可以从读取Player Setting,但请勿通过该方式写入。序列化名称与API名称不匹配,其中多项设置是按构建目标区分的,手动编辑的值可能与构建实际使用的值不一致且无提示。如果无法连接到编辑器,写入步骤无法进行。
ProjectSettings/ProjectSettings.asset
使用运行C#代码。默认超时时间为30秒。
unity command eval --code '<snippet>'unity commandPassing C# to eval
eval向eval
传递C#代码
evaleval- No directives. The compiler reads
usingas a resource-disposal statement and rejects it (using UnityEditor;).CS0210 - Types must be fully qualified. A bare does not resolve (
PlayerSettings), and a bareCS0246is ambiguous withObject(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:
| Setting | Correct form | Does NOT exist |
|---|---|---|
| Managed stripping level | | |
| Wasm code optimization | | |
| IL2CPP code generation | | a bare property |
UserBuildSettings一次调用即可返回完整的预检信息。已在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名称,因为直观的拼写并不存在,会导致编译失败:
| 设置项 | 正确写法 | 不存在的写法 |
|---|---|---|
| 托管代码剥离级别 | | |
| Wasm代码优化 | | |
| IL2CPP代码生成 | | 直接使用属性 |
UserBuildSettingsApplying 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 , so it is a project file, not input — a class declaration cannot be
flattened into a statement block. Save it under , let Unity compile, then invoke it
in one line:
[MenuItem]evalAssets/Editor/csharp
UnityEditor.EditorApplication.ExecuteMenuItem("Tools/Apply Web Release Settings");Keep its directives; they are correct in a file. For one-off changes — a single quality
level, a frame-rate flip — an inline statement is fine.
usingeval本技能中的大多数写入操作可批量完成,resources/WebOptimizer.cs已实现该批量操作。它声明了一个带有的类,因此是项目文件,而非输入——类声明无法简化为语句块。将其保存到目录,等待Unity编译完成后,通过一行代码调用:
[MenuItem]evalAssets/Editor/csharp
UnityEditor.EditorApplication.ExecuteMenuItem("Tools/Apply Web Release Settings");保留文件中的指令;这些指令在文件中是有效的。对于单次修改——比如调整单个质量级别、切换帧率——使用内联语句即可。
usingeval0. Pre-Flight
0. 预检
- Confirm Web build target: Read, with the Pre-Flight snippet above, — must be
EditorUserBuildSettings.activeBuildTarget; if not, warn the user.WebGL - Read compression and stripping settings: Read ,
compressionFormat,decompressionFallbackand the managed stripping level with the Pre-Flight snippet above. Note the stripping level isstripEngineCode— there is noPlayerSettings.GetManagedStrippingLevel(NamedBuildTarget.WebGL)property.PlayerSettings.managedStrippingLevel - Read exception and optimization settings: Read from the Pre-Flight snippet above. For the wasm code optimization level use
PlayerSettings.WebGL.exceptionSupport— theUnityEditor.WebGL.UserBuildSettings.codeOptimizationandPlayerSettings.WebGL.codeOptimizationspellings do not exist and will not compile.optimizationLevel - Read frame rate settings: Read, with the Pre-Flight snippet above, and
Application.targetFrameRate.QualitySettings.vSyncCount - Read additional player settings: Read, with the Pre-Flight snippet above, ,
PlayerSettings.WebGL.dataCaching,PlayerSettings.WebGL.debugSymbolMode, andPlayerSettings.WebGL.maximumMemorySize.PlayerSettings.GetApiCompatibilityLevel - Proceed only after compression, stripping, frame rate, and player settings are confirmed.
- 确认Web构建目标:使用上述预检代码片段读取——必须为
EditorUserBuildSettings.activeBuildTarget;若不是,请向用户发出警告。WebGL - 读取压缩与剥离设置:使用上述预检代码片段读取、
compressionFormat、decompressionFallback以及托管代码剥离级别。注意剥离级别需使用stripEngineCode——不存在PlayerSettings.GetManagedStrippingLevel(NamedBuildTarget.WebGL)属性。PlayerSettings.managedStrippingLevel - 读取异常与优化设置:使用上述预检代码片段读取。对于wasm代码优化级别,使用
PlayerSettings.WebGL.exceptionSupport——UnityEditor.WebGL.UserBuildSettings.codeOptimization和PlayerSettings.WebGL.codeOptimization这两种写法不存在,会导致编译失败。optimizationLevel - 读取帧率设置:使用上述预检代码片段读取和
Application.targetFrameRate。QualitySettings.vSyncCount - 读取额外的Player设置:使用上述预检代码片段读取、
PlayerSettings.WebGL.dataCaching、PlayerSettings.WebGL.debugSymbolMode以及PlayerSettings.WebGL.maximumMemorySize。PlayerSettings.GetApiCompatibilityLevel - 仅在确认压缩、剥离、帧率和Player设置后,再继续后续操作。
1. Assess Current State
1. 评估当前状态
- Check Build Report: Instruct the user to open after a build and identify the largest asset and code size contributors.
Window > General > Build Report - Verify server configuration: Ask the user to confirm whether the hosting server sends (Brotli) or
Content-Encoding: brheaders, and whetherContent-Encoding: gzipis set forContent-Type: application/wasmfiles..wasm - Check frame rate config: Confirm, with the Pre-Flight snippet above, — should be
Application.targetFrameRatefor Web (let the browser drive).-1 - Check memory settings: Read, with the Pre-Flight snippet above, and
PlayerSettings.WebGL.initialMemorySize.PlayerSettings.WebGL.memoryGrowthMode - Report findings before making recommendations.
- 查看构建报告:指导用户在构建完成后打开,识别体积最大的资源和代码贡献项。
Window > General > Build Report - 验证服务器配置:询问用户确认托管服务器是否发送(Brotli)或
Content-Encoding: br头信息,以及是否为Content-Encoding: gzip文件设置了.wasm。Content-Type: application/wasm - 检查帧率配置:使用上述预检代码片段确认——Web端应设为
Application.targetFrameRate(由浏览器控制)。-1 - 检查内存设置:使用上述预检代码片段读取和
PlayerSettings.WebGL.initialMemorySize。PlayerSettings.WebGL.memoryGrowthMode - 在给出建议前,先报告评估结果。
2. Understand Request
2. 理解用户需求
| User Says | Default 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" | |
| "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 |
| "浏览器中电池消耗过高" | 静态画面启用 |
| "异常处理体积过大" | 发布版本禁用异常处理;若浏览器基线支持则使用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 — see resources/WebOptimizer.cs for the template.
Assets/Editor/WebOptimizer.csAdapt the script to the user's project needs (e.g. keep exceptions if they use , 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.
try/catch始终主动为用户生成该脚本。Unity官方Web优化文档提供了一个编辑器菜单脚本,可一次性应用所有推荐的发布设置。将其放置在目录——模板可参考resources/WebOptimizer.cs。
Assets/Editor/WebOptimizer.cs根据用户项目需求调整脚本(例如,如果项目使用则保留异常处理,针对HTTP托管将Brotli切换为Gzip)。对于新的Web项目,该脚本是最有效的操作——可避免遗漏设置。
try/catchPlayer Settings audit
Player Settings审计
Verify and set these values through :
eval| Setting | Release recommendation |
|---|---|
| Compression Format | Brotli (HTTPS hosting); Gzip for HTTP |
| Decompression Fallback | Off when server is correctly configured |
| Strip Engine Code | On |
| Managed Stripping Level | High (release) / Medium (dev) |
| Code Optimization | Disk Size with LTO (release) / Build Times (dev) |
| WebAssembly Language Features | 2023 if browser baseline allows |
| Enable Exceptions | None (smallest); Explicitly Thrown Only if |
| Initial Memory Size | Tune to peak estimate; too small causes expensive growth |
| Memory Growth Mode | Geometric |
| API Compatibility Level | .NET Standard 2.1 — smaller than .NET Framework |
| IL2CPP Code Generation | Optimize Size — smaller Wasm at slight runtime cost |
| Debug Symbols | Off for release; on for development builds only |
| Data Caching | On — caches asset data in browser IndexedDB for faster repeat loads |
| Strip Unused Mesh Components | On — removes unused vertex attributes |
| Maximum Memory Size | 2048 MB default; up to 4096 for complex 3D (Firefox and Chrome < 119 have issues above 2048) |
| vSyncCount | 0 (browser handles pacing) |
| targetFrameRate | -1 (use |
通过验证并设置以下值:
eval| 设置项 | 发布版本建议 |
|---|---|
| 压缩格式 | Brotli(HTTPS托管);HTTP托管使用Gzip |
| 解压回退 | 服务器配置正确时关闭 |
| 剥离引擎代码 | 开启 |
| 托管代码剥离级别 | 高(发布版本)/ 中(开发版本) |
| 代码优化 | 磁盘大小优化+LTO(发布版本)/ 构建速度优先(开发版本) |
| WebAssembly语言特性 | 浏览器基线支持时启用2023 |
| 启用异常处理 | 无(体积最小);若需要 |
| 初始内存大小 | 根据峰值估算调整;过小会导致昂贵的内存增长操作 |
| 内存增长模式 | 几何增长 |
| API兼容性级别 | .NET Standard 2.1 —— 比.NET Framework体积更小 |
| IL2CPP代码生成 | 优化体积 —— 牺牲少量运行时性能以减小Wasm体积 |
| 调试符号 | 发布版本关闭;仅在开发构建中开启 |
| 数据缓存 | 开启 —— 在浏览器IndexedDB中缓存资源数据,加快重复加载速度 |
| 剥离未使用网格组件 | 开启 —— 移除未使用的顶点属性 |
| 最大内存大小 | 默认2048 MB;复杂3D项目可设为4096 MB(Firefox和Chrome 119以下版本超过2048 MB会有问题) |
| vSyncCount | 0(由浏览器控制 pacing) |
| targetFrameRate | -1(使用 |
Compression and server configuration
压缩与服务器配置
| Compression | Use when | Notes |
|---|---|---|
| Brotli | HTTPS or localhost | Best ratio; browsers accept only over secure contexts |
| Gzip | HTTP delivery, legacy CDNs | Universal |
| None | Local dev / file:// | Largest payload; do not ship |
Configure the server to:
- Serve files with
.br.Content-Encoding: br - Serve files with
.gz.Content-Encoding: gzip - Set for
Content-Type: application/wasm,.wasmforapplication/javascript..js - Enable HTTP/2 or HTTP/3 to parallelize chunk fetches.
If the host cannot inject : set Decompression Fallback = On as a fallback only — it adds ~150 KB JS and slows startup.
Content-Encoding| 压缩方式 | 使用场景 | 说明 |
|---|---|---|
| Brotli | HTTPS或本地主机 | 压缩率最佳;仅在安全上下文下被浏览器接受 |
| Gzip | HTTP交付、传统CDN | 通用兼容 |
| 无压缩 | 本地开发 / file://协议 | 体积最大;请勿用于发布 |
服务器配置要求:
- 为文件发送
.br头。Content-Encoding: br - 为文件发送
.gz头。Content-Encoding: gzip - 为设置
.wasm,为Content-Type: application/wasm设置.js。application/javascript - 启用HTTP/2或HTTP/3以并行获取分片资源。
如果主机无法注入头:仅作为回退方案,设置解压回退 = 开启——这会增加约150 KB的JS代码并减慢启动速度。
Content-EncodingException handling
异常处理
| Setting | Build size | Use |
|---|---|---|
| None | Smallest | Release builds where uncaught exceptions are acceptable |
| Explicitly Thrown Only | Modest | Default for projects that catch exceptions |
| Full | Largest, slowest | Rarely 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 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.
Packages/manifest.json2. Shader stripping — Configure in :
Edit > Project Settings > Graphics| Setting | Recommendation |
|---|---|
| Lightmap Modes | Automatic (strips unused lightmap shader variants) |
| Fog Modes | Automatic (strips unused fog shader variants) |
| Instancing Variants | Strip Unused |
| Batch Renderer Group Variants | Strip All (if BRGs are not used) |
| Always Included Shaders | Audit and remove any shaders the project does not reference |
Test after stripping — ensure no referenced shaders were removed.
3. 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.
com.unity.web.stripping-tool可从三类资源入手缩减构建体积:
1. 未使用的包 —— 查看以及Package Manager的项目中和内置视图。移除或禁用项目未使用的包。若未使用Input System包,它会是体积的主要贡献者之一。
Packages/manifest.json2. 着色器剥离 —— 在中配置:
Edit > Project Settings > Graphics| 设置项 | 建议 |
|---|---|
| 光照贴图模式 | 自动(剥离未使用的光照贴图着色器变体) |
| 雾效模式 | 自动(剥离未使用的雾效着色器变体) |
| 实例化变体 | 剥离未使用的变体 |
| 批处理渲染组变体 | 全部剥离(若未使用BRG) |
| 始终包含的着色器 | 审核并移除项目未引用的着色器 |
剥离后进行测试——确保未移除被引用的着色器。
3. Web剥离工具 () —— 分析WebAssembly二进制文件并识别未使用的Unity引擎子模块(例如纯2D游戏中的3D图形模块)。通过Package Manager安装,分析构建结果,然后配置要排除的子模块。可在托管代码剥离级别之外进一步大幅缩减体积。
com.unity.web.stripping-toolQuality 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 :
evalwhere 0 = Very Low.UnityEngine.QualitySettings.SetQualityLevel(0, true); - 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其中0 = 极低。UnityEngine.QualitySettings.SetQualityLevel(0, true); - 考虑创建Web专用质量级别,禁用浏览器中不必要的功能(实时阴影、后期处理效果、高粒子数量)。
Frame rate on Web
Web端帧率
- Set it with :
eval— let the browser useUnityEngine.Application.targetFrameRate = -1;.requestAnimationFrame - Note: Safari caps at 60 fps in WebGL; high-refresh targets do not apply.
- Use to drop to 5–10 fps on static/idle screens to save battery.
OnDemandRendering.renderFrameInterval
- 通过设置:
eval——让浏览器使用UnityEngine.Application.targetFrameRate = -1;。requestAnimationFrame - 注意:Safari在WebGL中帧率上限为60fps;高刷新率设置无效。
- 在静态/空闲画面中使用将帧率降至5–10fps以节省电量。
OnDemandRendering.renderFrameInterval
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.
| Topic | Guidance |
|---|---|
| Package | Install |
| When to use | Runtime-loaded textures via Addressables or asset bundles served to unknown GPU targets |
| When NOT to use | Textures baked into the player build — Unity already selects the correct format at build time |
| Supercompression | Use ETC1S for smallest size (lossy, good for diffuse/albedo); UASTC for higher quality (near-lossless, better for normals/UI) |
| Encoding | Encode offline with |
| Linear data | Set |
| Mip maps | Generate mips at encode time ( |
| Loading | Use |
| Memory | Transcoded textures are standard GPU textures; memory cost equals the target format, not the KTX2 file size |
| Orientation | Always include |
toktx带Basis Universal超压缩的KTX2格式可打包单个纹理文件,加载时自动转码为浏览器设备的最优GPU格式(桌面端BC7、移动端ASTC、旧版Android设备ETC2)。这避免了为每个GPU家族单独打包纹理变体——对于目标硬件未知的Web场景至关重要。
| 主题 | 指导 |
|---|---|
| 包 | 通过Package Manager安装 |
| 使用场景 | 通过Addressables或资源包加载的运行时纹理,目标为未知GPU设备 |
| 不适用场景 | 烘焙到播放器构建中的纹理——Unity会在构建时自动选择正确格式 |
| 超压缩 | 使用ETC1S追求最小体积(有损压缩,适用于漫反射/基础色);使用UASTC追求更高质量(接近无损,适用于法线/UI) |
| 编码 | 使用 |
| 线性数据 | 编码法线贴图、遮罩或数据纹理时设置 |
| Mip贴图 | 编码时生成mip贴图( |
| 加载 | 使用 |
| 内存 | 转码后的纹理为标准GPU纹理;内存开销等于目标格式大小,而非KTX2文件大小 |
| 方向 | 始终添加 |
toktxStreaming 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构建性能分析
| Tool | Use | Notes |
|---|---|---|
| Chrome DevTools > Performance | CPU flamegraph; main-thread analysis | Default first stop for WebGL hitches; inspect Wasm call stacks |
| Chrome DevTools > Memory | Heap snapshot; allocation timeline | Find JS/Wasm memory leaks; compare snapshots before/after scene load |
| Firefox Profiler | Cross-platform; shareable URLs; native + Wasm view | Better Wasm symbolication than Chrome in some cases; shareable profile URLs for team review |
| Safari Web Inspector | iOS Safari and macOS Safari debugging | Required for Safari-specific issues; WebGL/Wasm runtime differs from Chromium |
| Unity Profiler over WebSocket | Connect to a development build; standard markers | Use for Unity-side markers (GC, rendering, scripts); does not capture browser-side overhead |
Symptom → tool quick reference:
| Symptom | First-line tool | Second-line tool |
|---|---|---|
| WebGL hitch / stutter | Chrome DevTools > Performance | Firefox Profiler |
| Memory climbing over time | Chrome DevTools > Memory | Unity Memory Profiler (WebSocket) |
| Slow initial load | Chrome DevTools > Network | Build Report Inspector |
| Safari-only rendering issue | Safari Web Inspector | Compare 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 for dev builds, or add a build processor:
Player Settings > Publishing > Debug Symbolscsharp
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| Flag | What it shows |
|---|---|
| CPU profiler overlay in browser |
| Visual memory map (white=allocated unused, pink=stack, blue=dynamic, green=fragmented) |
| 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 — type 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).
about:memoryabout:memoryEditor Play Mode does not represent browser runtime; always measure in browser. Chrome and Safari GC and JIT behavior differ — test both.
| 工具 | 使用场景 | 说明 |
|---|---|---|
| Chrome DevTools > Performance | CPU火焰图;主线程分析 | WebGL卡顿问题的默认首选工具;检查Wasm调用栈 |
| Chrome DevTools > Memory | 堆快照;分配时间线 | 查找JS/Wasm内存泄漏;对比场景加载前后的快照 |
| Firefox Profiler | 跨平台;可分享URL;原生+Wasm视图 | 在某些情况下比Chrome的Wasm符号化效果更好;可生成分享链接供团队评审 |
| Safari Web Inspector | iOS Safari和macOS Safari调试 | 排查Safari专属问题的必备工具;WebGL/Wasm运行时与Chromium不同 |
| 通过WebSocket连接的Unity Profiler | 连接到开发构建;标准标记 | 用于分析Unity侧标记(GC、渲染、脚本);无法捕获浏览器端开销 |
症状→工具快速参考:
| 症状 | 首选工具 | 次选工具 |
|---|---|---|
| WebGL卡顿/停顿 | Chrome DevTools > Performance | Firefox Profiler |
| 内存持续增长 | Chrome DevTools > Memory | Unity内存分析器(WebSocket) |
| 初始加载缓慢 | Chrome DevTools > Network | Build Report Inspector |
| Safari专属渲染问题 | Safari Web Inspector | 与Chrome DevTools对比 |
嵌入性能分析符号——浏览器性能分析器默认显示混淆的Wasm函数名。要在Chrome/Firefox火焰图中显示可读的C#方法名,可在开发构建中启用,或添加构建处理器:
Player Settings > Publishing > Debug Symbolscsharp
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| 标志 | 显示内容 |
|---|---|
| 浏览器中的CPU分析器浮层 |
| 可视化内存地图(白色=已分配未使用,粉色=栈,蓝色=动态分配,绿色=碎片) |
| 线程活动分析器 |
GPU调试——Web端不支持Frame Debugger。使用Spector.js作为浏览器端替代工具——它可捕获绘制调用和WebGL状态。
Firefox ——在Firefox中输入作为URL,点击Measure查看每个标签页的内存细分:WASM代码大小、WASM堆、.data文件、Web音频。注意WASM堆>300 MB时存在崩溃风险,尤其是在iOS Safari中。
about:memoryabout:memory编辑器运行模式无法代表浏览器运行时;始终在浏览器中测试。Chrome和Safari的GC和JIT行为不同——需同时测试两者。
Web memory directives
Web内存指令
- Disable Read/Write Enabled on textures and meshes — it duplicates data into the WASM heap.
- Reduce file size by moving assets to Addressables or AssetBundles.
.data - 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 shows web audio > 100 MB, audio is likely uncompressed — switch to Vorbis.
about:memory
- 视频:仅支持从URL(启用CORS的服务器)或StreamingAssets播放。在iOS上,服务器必须支持HTTP范围请求才能流式播放。使用浏览器兼容格式(MP4/H.264)。
- 音频:AudioEffects(混音器效果)需要计算着色器——WebGL不支持。混音器和混音器组仅能用于音量控制。将音频设置为单声道以加快加载速度。若显示Web音频>100 MB,音频可能未压缩——切换为Vorbis格式。
about:memory
Canvas and DPI
Canvas与DPI
If the canvas is scaled up it takes the new resolution. Use in the web template to offset DPI scaling and avoid rendering at unnecessarily high resolution.
devicePixelRatio如果Canvas被放大,会采用新分辨率。在Web模板中使用抵消DPI缩放,避免不必要的高分辨率渲染。
devicePixelRatio4. Validation
4. 验证
- Re-read the Player Settings with the Pre-Flight snippet (compression, stripping, exceptions, targetFrameRate).
- Rebuild the player and compare Build Report file sizes with baseline.
- Verify in at least Chrome and Safari (GC and JIT behavior differ).
- Max 3 iterations before asking the user for feedback.
- 使用预检代码片段重新读取Player Settings(压缩、剥离、异常处理、targetFrameRate)。
- 重新构建播放器并对比构建报告中的文件大小与基线。
- 至少在Chrome和Safari中验证(两者的GC和JIT行为不同)。
- 最多进行3次迭代,然后向用户寻求反馈。
5. Troubleshooting
5. 故障排除
Build still large after enabling Strip Engine Code
启用剥离引擎代码后构建体积仍然过大
- Is Managed Stripping Level set to Medium or Low? → Set to High for release.
- Are plug-ins using reflection to access engine modules that would otherwise be stripped? → Add a to preserve needed symbols.
link.xml - Is Exceptions set to Full? → Full adds the largest code overhead; switch to None or Explicitly Thrown Only.
- 托管代码剥离级别是否设为中或低?→ 发布版本设为高。
- 是否有插件使用反射访问原本会被剥离的引擎模块?→ 添加保留所需符号。
link.xml - 异常处理是否设为完整?→ 完整模式会带来最大的代码开销;切换为无或仅显式抛出。
Brotli not working — Decompression Fallback required
Brotli无法工作——需要解压回退
- Is the server sending ? → Without this header the browser won't decompress; the fallback JS decompressor is then needed.
Content-Encoding: br - Is the build hosted over HTTP (not HTTPS)? → Brotli requires a secure context; degrade to Gzip for HTTP hosting.
- 服务器是否发送头?→ 没有该头,浏览器不会解压;此时需要回退JS解压程序。
Content-Encoding: br - 构建是否通过HTTP(而非HTTPS)托管?→ Brotli需要安全上下文;HTTP托管降级为Gzip。
Stutter in Safari but not Chrome
Safari中卡顿但Chrome中正常
- Does the project set ? → On Safari WebGL this conflicts with browser pacing; set to
Application.targetFrameRate = 60.-1 - 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.
- 项目是否设置了?→ 在Safari WebGL中这会与浏览器pacing冲突;设为
Application.targetFrameRate = 60。-1 - 是否存在在Safari WebGL实现中表现不同的着色器?→ 在设备上测试;Safari的WebGL/Wasm运行时与Chromium不同——部分GLSL语法处理方式有差异。
Memory growth slow path triggered
触发内存增长慢路径
- 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.
- Is Memory Growth Mode set to Linear? → Switch to Geometric for saner growth curve.
- 初始内存大小是否远小于项目峰值?→ Wasm内存增长需要完整的缓冲区复制;将初始内存大小设为合理的峰值估算值。
- 内存增长模式是否设为线性?→ 切换为几何增长以获得更合理的增长曲线。
Frame rate set to 60 but browser runs erratically
帧率设为60但浏览器运行不稳定
- Is set in code? → On Web this conflicts with
Application.targetFrameRate = 60browser pacing. Set torequestAnimationFrame.-1 - Is non-zero? → Set to 0; the browser handles pacing.
vSyncCount
- 代码中是否设置了?→ 在Web端这会与
Application.targetFrameRate = 60浏览器pacing冲突。设为requestAnimationFrame。-1 - 是否非零?→ 设为0;由浏览器控制pacing。
vSyncCount
Firefox cache rejecting large files
Firefox缓存拒绝大文件
Firefox limits individual cache entries via . 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 .
browser.cache.disk.max_entry_sizeabout:configFirefox通过限制单个缓存条目大小。如果构建超过该值(默认约50 MB),资源将无法缓存。解决方案:使用Addressables拆分为<51 MB的包,或指导用户在中增大该设置。
browser.cache.disk.max_entry_sizeabout:configLocal dev server setup
本地开发服务器设置
For testing builds locally with proper MIME types:
bash
undefined要在本地测试构建并确保正确的MIME类型:
bash
undefinedPython (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 ) — variant count feeds directly into Wasm size, so it is worth checking when stripping alone hasn't moved the number.
ShaderVariantCollection - 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专属部分。
- 着色器变体剥离(图形设置→着色器剥离,以及)——变体数量直接影响Wasm体积,因此当仅靠剥离无法优化时值得检查。
ShaderVariantCollection - Project Settings → Player——本技能读取的所有标志,若用户更愿意在检视面板中查看而非通过报告获取。
- 移动浏览器电池行为遵循第3和第4节中的帧率与质量级别指导;此处无需单独的移动端流程。