Skip to main content

optimize-web

Optimizes Unity WebGL and WebGPU builds for smaller downloads and faster load. Use when a web build is too large or stutters, or the user asks about compression, stripping, shader variants, or web profiling.

소스 정보

저장소
Unity-Technologies/unity-agent-plugin
최근 소스 활동
2026년 9월 29일 18:32
감지된 SKILL.md 언어
영어
스타
391
포크
39

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
4 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
optimize-web
description
Optimizes Unity WebGL and WebGPU builds for smaller downloads and faster load. Use when a web build is too large or stutters, or the user asks about compression, stripping, shader variants, or web profiling.
## Performance Notes - Take your time to do this thoroughly. - Quality is more important than speed. ## Running C# in the Editor 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 --caller plugin --skill optimize-web --code '<snippet>'`. `unity command` defaults to a 30 second timeout. ### Passing C# to `eval` `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`). ### 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 | `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. **`codeOptimization` is the one setting here that does not live in the project file.** It persists to `Library/EditorUserBuildSettings.asset`, and `Library/` is gitignored by every standard Unity `.gitignore`, so this value is per-machine and does not travel: teammates and CI do not inherit it. Two consequences. Read it back through the API and do not look for it in `ProjectSettings/ProjectSettings.asset` — it is absent there even on a successful apply, so its absence is not a failure. And if the release build runs in CI, apply it there as a build step rather than assuming the repository carries it. ### Applying the settings Most of the writes in this skill are a single batch, and [resources/WebOptimizer.cs](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. ### Verify from disk, not from the objects you just wrote **Applying a setting and reading it back in the same session proves nothing.** Player Settings are in-memory objects until something saves them, so every read-back returns the value you just assigned whether or not it ever reached `ProjectSettings/ProjectSettings.asset`. A run that skips the save reports success, and when the Editor session ends the whole change is gone. This was observed, not theorised: a run applied everything, read back Brotli / High / None, said it was done, and the file on disk never changed. So after any write: 1. **Save.** `UnityEditor.AssetDatabase.SaveAssets()`. `WebOptimizer.cs` now does this itself; an inline `eval` write has to do it explicitly. 2. **Read the values back and report them.** Not "applied successfully" but the actual values, so the user can see what is stored. `WebOptimizer.cs` logs all nine. 3. **For anything per-build-target, name the target you read.** Several of these settings exist once per target, so a value can be correct for one target and unset for the one being built. The same trap exists on the file-editing route in reverse: a hand-edited `ProjectSettings.asset` reads back fine while the running Editor and the build still use the old value. Verifying after a save and a reimport is what catches both. **Do not try to reproduce this in batch mode.** A batch Editor invoked with `-quit` saves settings on exit, so an unsaved write persists anyway and the run looks correct. Both a saving and a non-saving version of the script pass under `-quit`. The bug only appears in a live Editor session, which is where it was found. Concluding from a green batch run that the save is unnecessary is the wrong conclusion from a test that cannot see the defect. ## 0. Pre-Flight 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. Assess Current State 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. ## 2. Understand Request | 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" | `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 | ## 3. Web Build Optimization Workflow ### 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](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. ### Player Settings audit 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 `try/catch` required | | **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 `requestAnimationFrame`) | ### 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 `.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. ### Exception 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. ### 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`: | 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** (`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. ### Quality settings for 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). ### Frame rate on 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. ### KTX / Basis Universal textures 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 `com.unity.cloud.ktx` (KtxUnity) via Package Manager | | **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 `toktx` or `basisu` CLI; do not encode at runtime | | **Linear data** | Set `--assign_oetf linear` when encoding normal maps, masks, or data textures to avoid incorrect sRGB conversion |
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기