关于 scenario-unity-web
面向 Unity 6.3 Web 发布的开发技能,指导开发者按托管环境配置构建、压缩、内存与页面互操作。流程要求通过真实主机和浏览器验证加载、字节预算、图形 API 与画面。
主要功能
- 01按主机应用 Web 构建设置
- 02检查构建体积与压缩响应头
- 03配置页面与 Unity 互操作
- 04在 Chrome 中验证加载和内存
- 05检查启动预算与加载时间
适用场景
- 01为网站或门户准备 Unity Web 构建
- 02排查压缩或响应头导致的加载问题
- 03验证页面与游戏的消息交互
- 04测量移动端启动内存和下载预算
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-unity-web安装后在 Claude Code 中输入/scenario-unity-web确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/critique.mdexpert-notes.mdgui-paths.mdprocedures.mdsources.mdscripts/ut_web.pyscripts/AgentKit/Web/WebJobs.csscripts/AgentKitOptional/Addressables/WebAddressablesJobs.csscripts/AgentKitOptional/Tests/AgentWeb.EditorTests.asmdefWebReleasePolicyTests.csscripts/WebRuntime/Optional/Addressables/WebDeferredAddressable.csscripts/WebRuntime/Plugins/WebGL/AgentWebBridge.jslibscripts/WebRuntime/Scripts/WebBridge.csWebErrorReporter.csWebLeaderboard.csscripts/WebRuntime/WebGLTemplates/AgentWeb/index.html详细介绍
Unity Web(Web 专家)
Web 方面的专家级水准,意味着要在玩家实际获取游戏的环境中评判构建产物:从开始游戏前的字节数、由配置了正确标头的实际主机提供服务,到在真实浏览器中加载,并能报告运行的是哪种图形 API、堆增长了多少,以及页面与游戏是否能通信。原则是:由主机选择压缩方式,预算以首次可玩的时刻为衡量点;只有经提供服务的构建在无头模式下零错误加载成功,并且截图经过检查,才算达标。目标环境:Unity 6000.3.21f1、URP 17.3、macOS 上的 Chrome headless。如果此处提到的同级技能不在你的可用技能中,请让用户安装(npx skills add scenario-labs/skills --skill <name>);无人值守时,根据工具架构继续操作,并标明这一缺口。
必备背景: scenario-unity-expert(渠道、审查循环、6.3 陷阱)。工具包:
ut_env、ut_run(包括 build(P, "web")、run_tests)、ut_review,以及此技能的
scripts/ut_web.py、
scripts/AgentKit/Web/WebJobs.cs 和
scripts/WebRuntime/(桥接、.jslib、错误报告器、模板)。
立场(专家补充)
- 由托管方决定压缩方式。 自有 HTTPS 服务或 CDN:使用 Brotli,关闭回退;每个文件的
Content-Encoding单独设置;所有.wasm*文件都设置为application/wasm(6.3 Manual)。纯 HTTP:使用 gzip(观察到 Chrome 在局域网地址拒绝了 Brotli,报错ERR_CONTENT_DECODING_FAILED;localhost不受影响)。itch.io:开启 Decompression Fallback(Max O'Didily 8iApGVX--B0 [00:01:40])。Poki:关闭。 - 为开始游戏前的下载量设定预算。 CrazyGames 会统计从初始下载到首次触发
gameplayStart之间的下载量:移动端首页为 20 MB,其他情况为 50 MB,文件数上限为 1,500。延后加载的内容必须在此时刻之后请求(网络日志可证实)。仅为首次可玩时刻所需的资源包添加预加载标签:这会让更多字节计入统计窗口(观察到开始前增加了 0.84 MB)。 - 加载时间才是指标;宽带速度足够后,二进制文件和 CPU 就是主要瓶颈。 加载时间缩短 25%,会使会话数增加 50%(Craven,LlE35onVdmQ [00:08:40]);超过 86 Mbps 后,收益趋于平缓 [00:11:26]。使用 CPU 限速和延迟进行测量;托管覆盖率取决于加载时间(Loveridge,FYsVftoLBP8 [00:16:12])。妥善设计等待体验:显示带有操作说明的加载卡片,而不是一条空白进度条(Craven [00:14:16]);然后让玩家点击开始,同时解锁音频。
- 一个线程、一个堆、浏览器的时钟。 所有 ThreadPool API 都会导致无法恢复的挂起:应使用
Awaitable、协程和UnityWebRequest(观察到Task.Run(...).Result会冻结标签页)。堆会从 Initial Memory Size 开始增长,增长可能导致手机崩溃:根据实测会话设置 Initial(观察到 32 MB 增长到 68 至 74 MB;设置为 80 后不再增长)。将targetFrameRate保持为 -1:Unity 无法查询刷新率(6.3 Manual)。 - WebGL 2 已正式发布;WebGPU 用于计算,且会静默回退。 6.3 至 6.5 中为实验功能;从 6.6 起受支持,但需选择启用。保留 WebGL 2 作为回退,并记录
SystemInfo.graphicsDeviceType(观察到纯 HTTP 页面无报错地使用了 WebGL 2)。WebGL 2 的蒙皮在 CPU 上执行(30 个角色:WebGPU 为 30 fps,WebGL 2 为 7 fps;Duncan 3bu4WZUCGYc [~00:15:13]),且只支持非定向光照贴图。 - 网页和游戏之间的契约很有限。
.jslib使用 ES5,字符串以堆指针形式传递,通过makeDynCall回复静态[MonoPInvokeCallback],使用SendMessage向名称完全匹配的对象发送一个 JSON 字符串。默认情况下,Unity 会接管整个页面的键盘输入:一个页面名称字段收不到任何输入,直到设置WebGLInput.captureAllKeyboardInput = false(观察到)。没有系统字体:CJK 和希腊语名称显示为空白,尽管Font.HasCharacter显示字符存在(观察到)。门户会忽略或重写index.html,因此桥接代码应放在.jslib中。 - 实际发布的内容不一定是你设置的内容。 Strip Engine Code 只会移除资源包实际使用的类(观察到
Could not produce class with ID 182;解决方法:使用 link.xml)。Code Optimization 和纹理子目标设置保存在Library/中:全新克隆会读取 Shorter Build Time(观察到)。Safari 的 iframe 中没有 IndexedDB(6.3 Manual):在 itch.io 上,Safari 玩家可能需要重新加载所有内容。 - 按在线服务的方式发布,并删掉不值得占用字节的内容。 使用哈希名称,保留上一版构建,分阶段向 5% 的用户发布(Loveridge [frame 00:23:29]),并为每个版本保留符号文件和错误收集器。“如果它对游戏画面保真度的提升只有百分之一或两个百分点……就该删掉”[00:11:15]:此处 URP 后处理数据占用了 1.61 MB。
首先确认
一次性询问:每个宿主或门户(压缩、回退、页面所有权、iframe);受众和浏览器最低版本(WebAssembly 2023 需要 Safari 16.4+、Chrome 91+、Firefox 89+,而 Unity 支持 iOS Safari 15;CrazyGames 默认在 iOS 上禁用 Unity);从启动到开始游戏的字节数预算,以及在指定线路速度和地区下的秒数;2D 还是 3D、URP、光照贴图、蒙皮人群;计算需求(WebGPU);页面 API 和任何页面文本字段;脚本可使用的玩家文本(字体);桌面和移动端(双 DXT/ASTC 数据);音频(首次点击);Unity 版本。默认值:仅 WebGL 2;自有 HTTPS + Brotli;门户移动端 20 MB,其他情况 30 MB(Loveridge [00:03:29]);Explicitly Thrown;启用 WebAssembly 2023;迭代时使用 Disk Size,发布时使用带 LTO 的 Disk Size;Maximum Memory 2048 MB,Initial 根据测量结果设置;手机 DPR 设为 1;targetFrameRate 为 -1。
工作流
- 预检与审计。
ReadSettings、AuditWeb(场景、光照、蒙皮、剥离、启动画面、后处理数据、导入)、ut_web.scan_hang_apis(卡死、targetFrameRate)、scan_jslib、package_report。门槛:审计错误为零;场景列表非空且有效(Cam Ayres bF_eUuxGEcA [00:04:02]);每个高开销包都有合理依据。 - 按宿主配置设置。 使用预设(
own-https、plain-http、itch、poki、crazygames、dev)调用WebJobs.ApplySettings,始终传入code_optimization和texture_subtarget;使用build_target="WebGL"启动。使用write_policy和WebReleasePolicyTests(EditMode)在 CI 中进行保护。门槛:回读成功,8 项策略测试通过。 - 互操作。
ut_web.install_runtime(P);接入WebBridge、WebLeaderboard、WebErrorReporter(完整代码见流程 P3)。门槛:编译错误为 0,扫描无问题,桥接对象名称与页面调用时使用的名称一致。 - 构建。
ut_run.build(P, "web", out=...)。资源包:先运行WriteBundleLinkXml。门槛:Succeeded;文件集与设置匹配;记录size_report,并查看build_log_report(以此处排序,按磁盘上的字节数判断)。 - 提供服务并审计标头。 在本地运行
serve和header_audit,然后在实际宿主上执行。门槛:错误为零;传输的字节数等于磁盘上的字节数。 - 在 Chrome 中加载。 使用页面模拟运行
browser_check(..., metrics_seconds=8, reload=True)。门槛:出现ready、gameplay_start和功能事件;横幅为零,意外的控制台错误为零;ready.api符合预期;开始游戏后才发起延迟请求;移动端设置下堆未增长;帧率稳定;热重载不获取.data正文(允许 304);截图已打开。 - 预算与加载时间。 运行
budget_check;对加载进行限速测试(20 Mbps、40 ms、CPU x4),并为远距离地区测试 250 ms 的运行。门槛:通过且留有余量。 - 变体。 itch(回退、zip、iframe 宿主)、门户 SDK、安全和不安全来源上的 WebGPU、双 DXT/ASTC(
merge_texture_variants)、手机模拟(DPR 上限)。每种变体的门槛同第 5 至第 7 步。 - 发布。 使用带 LTO 的 Disk Size,归档符号,设置
AGENTWEB_ERROR_URL或WebErrorReporter.endpoint,保留上一版构建,制定发布计划(P12)。交接包中注明已验证和假设内容。
数值
| 数值 | 对应 | 来源 |
|---|---|---|
| 20 / 50 MB,1,500 个文件 | CrazyGames 首次下载至 gameplayStart 的时间(移动端主页 / 通用场景) | CrazyGames |
| 30 MB | 工作室二进制文件规则(“基本要求”) | Loveridge [00:03:29] |
| +3.6 / +1 / +0.7 MB | URP、URP 后处理、TextMeshPro 资源 | CrazyGames 测试 |
| 9.47 MB,8.1 s | 发布演示构建(LTO):初始下载;在 20 Mbps、40 ms、CPU x4 条件下,至 gameplay_start 的时间 | 实测 |
| +0.83 s | 同一加载过程,延迟从 40 ms 改为 250 ms | 实测 |
| -1.61 MB | 关闭 URP 渲染器后处理数据(从 10.08 MB 降至 8.47 MB) | 实测 |
| 32 至 68-74 MB | 启动期间默认初始内存大小导致的堆增长;设为 80 MB 时无增长 | 实测 |
| 2048 MB | 最大内存大小;Chrome/Firefox 119 之前,超过此值会出现错误 | 6.3 手册 |
| 7 至 30 fps | 30 个蒙皮角色,WebGL(CPU 蒙皮)与 WebGPU 的对比 | Duncan [~00:15:13] |
| 48 / 86 Mbps | 增益趋于平缓时的移动网络 / 宽带中位速度 | Craven [00:10:53] |
更多实测数值:references/expert-notes.md(数值,完整内容)。
质量门槛
- **可测量:**回读和策略测试通过;审计和扫描无异常;
BuildReport状态为 Succeeded; 在实际主机上header_audit显示 0 个错误;browser_check与预期 API 配合正常;启动后延迟执行; 堆保持平稳;错误会传递至收集器;budget_check通过;限速加载未超出预算。 - **视觉:**已打开画布截图:场景已渲染;UI 在 900x500、DPR 1 和 390 px 纵向画布上清晰可读; 所有玩家可输入的脚本各有一行;加载卡片和横幅清晰可读。
常见错误
| 错误 | 表现 | 修复 |
|---|---|---|
| 用快速本地服务运行压缩构建 | SyntaxError、“Unable to parse ...framework.js.br” | 使用 ut_web.serve,或按后缀设置 Content-Encoding |
| 在纯 HTTP 上使用 Brotli | ERR_CONTENT_DECODING_FAILED | 使用 HTTPS,或改用 gzip 并允许通过 HTTP 下载 |
使用 Task.Run、HttpClient、线程、计时器 | 工作始终不会运行;.Result 会冻结标签页 | 使用 Awaitable、协程、UnityWebRequest |
“为了移动设备”设置 targetFrameRate = 30 | Unity 会将自身循环计时所依据的刷新率假定为 60 Hz | 保持为 -1;仅在有意限帧时进行限制 |
| 初始内存设得较小,并在手机上增长 | 启动时堆内存从 32 增至 74 MB;没有连续内存块时发生崩溃 | 初始值 = 实测峰值(heap_advice) |
| 高级剥离 + 资源包 | Could not produce class with ID,组件缺失 | 使用 WriteBundleLinkXml,然后重新构建 |
| 在 CI 上信任 Release 设置 | 全新克隆会读取 Shorter Build Time 和默认纹理格式 | 在每个作业中设置这两项,或提交一个 Build Profile |
| 画布旁的页面字段 | 按键输入被吞掉 | 设置 WebGLInput.captureAllKeyboardInput = false(SetKeyboardCapture) |
| 在任意脚本中显示玩家名称,使用内置字体 | 字形为空;Font.HasCharacter 仍返回 true | 附带备用字体,或在 HTML 中绘制名称;逐个脚本截图 |
| 相信 Graphics API 列表或版本传闻 | WebGPU 构建运行在 WebGL 2 上;“WebGPU 已在 6.3 中可用于生产环境” | 按来源记录 API;6.3 到 6.5 为实验性功能,6.6 需选择启用 |
在 .jslib 中使用 {{{ }}} 或 ES6 | 构建失败 | 使用 ES5,注释中不要使用宏(scan_jslib) |
在门户网站的 index.html 中编写页面逻辑 | CrazyGames 上缺失,在 Poki 上被改写 | 使用 .jslib 或门户网站 SDK |
| 发布模板默认设置 | Shorter Build Time、Minimal stripping、WASM 2023 关闭:原始 wasm 为 46 MB | 使用发布预设 |
交接
- 接收自 scenario-unity-architecture:不含挂起 API 的代码(扫描结果会反馈);scenario-unity-pipeline-automation:Addressables 布局、CI、Build Profiles(此技能提供策略测试、LZ4、延迟加载、link.xml);scenario-unity-mobile:ASTC 与 DXT、触控、安全区域;scenario-unity-ui:画布在 900x500、DPR 1 和纵向模式下清晰可读,为玩家文本提供备用字体;scenario-unity-rendering-lighting:Web URP 档位(1 到 2 级级联、精简或不使用后处理、非定向光照贴图或实时光照);scenario-unity-shaders 和 scenario-unity-vfx:支持 WebGPU 的计算着色器及 WebGL 2 回退方案;scenario-unity-performance:帧率和内存预算。
- 交付给 scenario-unity-expert 和用户:按宿主划分的构建文件夹、标头表或服务器配置、
size_report、budget_check、browser_check证据(API、事件、启动前的字节数、堆内存、fps、截图)、策略文件、zip、符号、Verified 和 Assumed 列表。
Unity 6.3 说明
- 菜单中显示“Web”,代码仍使用
BuildTarget.WebGL、PlayerSettings.WebGL、UNITY_WEBGL、Assets/WebGLTemplates;6.5 之前没有公开 API 可创建 Build Profile。WebGLInput位于UnityEngine.WebGLModule中,仅在 Web Player 构建中存在:使用UNITY_WEBGL && !UNITY_EDITOR对其进行条件编译。 - URP 模板默认启用 Brotli 和 Shorter Build Time,关闭 WebAssembly 2023,并使用 Minimal stripping(观察所得);WebAssembly 2023 在 6.5 中成为默认选项(Emscripten 4.0.19;6.3 报告为
3.1.39-git)。 - WebGPU:在 6.3 中处于实验阶段,且仅支持 URP;PSO tracing 和 Microphone 在 6.4 中推出;在 6.6 中获得支持,并提供可选启用、Device Filter 和兼容模式。
- Build Profile 的纹理压缩设置会覆盖 Player Settings(单个纹理的设置优先级高于两者),并且与 Code Optimization 一样,存储在 ProjectSettings 之外。
unityInstance.GetMetricsInfo()(堆内存、fps、卡顿帧)在发布构建中也能使用,无需 Diagnostics Overlay(其 JS 内存读取在 Safari 和 iOS 上显示 N/A);6.3 可通过 IP 对运行中的 Web 构建进行性能分析;Frame Debugger 不适用于 Web。
参考资料
references/procedures.md:24 个操作步骤,包含完整代码(含桥接代码)、实时测试和记录结果。references/expert-notes.md:专家评估,包含时间戳、完整数据和意见分歧表。references/critique.md:自我审查标准。references/gui-paths.md:通过窗口和菜单完成相同工作的步骤。references/sources.md:所有来源、凭据、时间戳和修订历史。scripts/ut_web.py、scripts/AgentKit/Web/WebJobs.cs、scripts/AgentKitOptional/(Addressables 作业、EditMode 策略测试)、scripts/WebRuntime/(桥接代码、错误报告器、.jslib、AgentWeb 模板)。