关于 scenario-unity-vfx
面向 Unity 实时特效创作的技能,供游戏 VFX 艺术家及开发者使用。它将游戏可读性、性能预算与逐帧验证结合,并涵盖 Shuriken 和 VFX Graph 工作流。
主要功能
- 01按玩法重要性设计特效层级
- 02选择 Shuriken 或 VFX Graph
- 03构建材质、纹理与特效
- 04逐帧检查并测量开销
- 05验证批处理、剔除与粒子数量
适用场景
- 01制作移动端冲击波与爆炸
- 02检查多份特效同时出现时的可读性
- 03分析过绘制、粒子数与帧预算
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-unity-vfx安装后在 Claude Code 中输入/scenario-unity-vfx确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/critique.mdexpert-notes.mdgui-paths.mdprocedures.mdsources.mdscripts/ut_vfx.pyscripts/AgentKit/Vfx/VfxAudit.csVfxBudget.csVfxFireballKit.csVfxGraphJobs.csVfxMaterials.csVfxMeshes.csVfxMeshFx.csVfxSequence.csVfxShuriken.csVfxStress.csVfxTextures.csVfxUber.csscripts/EditorGraph/Unity.Testing.VisualEffectGraph.EditorTests.asmdefVfxGraphAuthoring.csscripts/EditorGraph/Sdf/AgentKit.Vfx.GraphTools.asmdefVfxSdf.csscripts/Runtime/AgentKit.Vfx.Runtime.asmdefscripts/Runtime/Vfx/FireballProjectile.csVfxStressSpawner.csVfxTier.csscripts/Shaders/VfxOverdraw.shaderVfxUber.shader详细介绍
Unity VFX(实时特效美术)
专业特效首先服务于游戏:在目标设备上,以屏幕上实际显示的副本数量,让玩家看清发生了什么、危险在哪里、何时结束。这里会同时衡量制作水准(层次、对比度、运动、纹理)和预算(填充率、粒子、绘制调用、内存):每个特效都会在屏幕外逐帧运行、检查并计数。目标环境:Unity 6000.3.21f1、URP 17.3、VFX Graph 17.3.0、macOS Apple Silicon。如果此处提到的同级技能不在你的可用技能中,请让用户安装它(npx skills add scenario-labs/skills --skill <name>);如果无法等待,请根据工具架构继续操作并标明这一缺口。
必备背景: scenario-unity-expert(通道、审查循环、6.3 陷阱、ut_run、ut_review、ut_stat、AgentKit)。
状态(2026-09-24,盲评重构后): 所有流程均已通过 tests/code/unity-vfx/ 在 Unity 6000.3.21f1 中运行(结果见 archive/tests/unity-vfx/live_results.jsonl,数据见 references/procedures.md)。
核心立场(专家之见)
- 玩法优先于美观(Jason Keyser,前 Riot)。主形状就是玩法边界;亮度应按重要性分配预算(待机和普通攻击应低调,改变战局的效果和终极技能应明亮);效果应在玩法状态结束时一并结束;应使用 N 个副本进行评审,而非只看一个。为每个根节点标记
VfxTier,并检查层级(ut_vfx.importance_ladder;观测值:斩击 0.39 < 枪口火光 0.46 < 命中 1.50 < 冲击波 2.60 < 爆炸 5.99)、人群可读性(crowd_readability:在符合玩法间距的情况下,20 个爆炸看起来是 20 个形状;间距压缩到 2.2 m 时则只有 6 个),以及停止后的尾迹。 - 在移动端,让纹理在网格上移动胜过粒子云(Nikola Damjanov,Nordeus:已发布效果中约 90% 如此;粒子只是点缀,每个发射器最多 100 个;过度绘制的影响大于绘制调用)。观测结果:同一个冲击波使用单个环形网格时,到处都只绘制 1 层,峰值为 0.037 个全屏等效值(FSE);而在覆盖区域内会绘制 5 到 7 层,36 个喷溅则达到 0.35 FSE。应测量每帧的填充量,而非粒子数量。
- 根据平台和数据流选择系统。 对于每个目标手机、WebGL、逐粒子 C#、Unity 物理碰撞:使用 Particle System,并逐个模块脚本化。对于数千到数百万个 GPU 粒子、深度缓冲区碰撞、贴花、六向烟雾、GPU 事件:使用 VFX Graph;它需要 compute,仅能在部分高端 URP Android 设备(电子书)上运行,在 Web 上则只能通过 WebGPU 运行,该功能在 6.3 中仍处于实验阶段。
- 使用一个超级材质,通过 Custom Data 提供逐粒子数据。 Nordeus 的超级着色器:灰度 R 通道通过 LUT 渐变着色,G 通道用于自发光,B 通道用于覆盖 Alpha,另有线性淡出、侵蚀、第二 Alpha、每种材质各自的 Blend 和 Cull。
AgentKit/VFX/Uber是以文本形式提供的该着色器;每个实例的变化(侵蚀曲线、随机 UV 偏移、擦除)通过 Custom Data 模块和自定义顶点流提供(Hovl Studio:“无法在着色器中生成随机数”),因此所有变体都能共用一种材质,并保持 SRP Batcher 启用。流顺序是审计检查的约定。 - VFX Graph 是带有公开 API 的黑盒(VFX Graph 电子书;Orson Favrel,Unity)。复制模板,公开少量属性和事件,并通过缓存的 ID 驱动;命令会延迟执行,而
aliveParticleCount在首次读取前会返回 -1,此后每 60 帧刷新一次(观测结果)。内存用量为容量 × 存储属性;包围盒决定剔除;排序需要一次 dispatch;若粒子只需要位置则使用点缓存,若需要贴合或碰撞则使用 SDF(两者均可脚本化,P8、P12)。 - 分层设计,每层各司其职,并注意对比、顺序和运动(Sirhaian、Gabriel Aguiar、Nordeus)。将深色 Alpha 主体置于加法混合的热度层之下,并为堆叠的 Alpha 层设置不同排序顺序;除循环外,不使用线性曲线;混合快慢元素;循环开始时加入爆发效果;始终让某些元素保持运动;变体贴图表应让每个粒子随机选取一个固定帧播放,绝不作为动画播放;火花应逐渐缩小,而火焰应逐渐增大;每层都应淡入淡出。
- 能借用或计算的就不要绘制;按画面显示尺寸导入(Simon Trümpler;Nordeus 常用 256,最大 512)。借用的翻页纹理应进行混合,并采用减速的帧曲线;简单形状应直接生成;精灵应通过运动模糊而非高斯模糊获得柔和边缘,并避免让取值落在边框上;通道打包的数据纹理应以线性方式导入(若按 sRGB 处理,0.5 灰度读出的值为 0.21,观测结果);LUT 应保持未压缩。
- 在 6.3 中,Shader Graph 粒子着色器会悄然关闭网格粒子的 GPU 实例化(Fred Moreau,Unity):5,000 个立方体的主线程耗时增加 69%,但绘制调用相同(观测结果)。审计会标记此问题;修复方案来自 scenario-unity-shaders。
先明确需求
只询问一次:目标档位和帧预算(移动端默认 30 fps、65% 占比 = 21.7 ms);可用算力;最低档位是否启用后处理(不使用 Bloom 时,需通过叠加层伪造辉光);游戏摄像机和距离;每种特效的重要性档位、频率、伤害半径和持续时间;最坏情况下屏幕上的副本数(默认 20);三个特效支柱(Keyser);世界是否受光照,以及一天中的时段。默认值:URP、Shuriken、移动端优先使用网格、低档位不启用后处理、预算为 ut_vfx.BUDGETS["mobile"]。
工作流
- 用数字说明需求。 档位、半径、持续时间、副本数、预算。GATE:后续每项检查都有阈值(
ut_vfx.BUDGETS,标记为来源值或 [added])。 - 选择系统和骨架(Stance 2、3)。Shuriken:每个层级调用一次
VfxShuriken.Build(parent, Layer, seed);网格层级设置Layer.mesh(VfxMeshes.Ring、Cone、QuadXZ、Color32 顶点 alpha)。VFX Graph:调用VfxGraphJobs.CopyTemplate,然后在 GUI 中公开属性(references/gui-paths.md);若不受支持,则调用VfxGraphJobs.Author。GATE:VfxGraphJobs.Contract列出游戏逻辑使用的每个属性和事件。 - 纹理和材质。 使用
VfxTextures.ImportVfxTexture(Clamp、按档位设置尺寸;数据纹理关闭 sRGB);通过VfxMaterials.Particle创建 URP 粒子材质(通过BaseShaderGUI.SetMaterialKeywords执行检查器逻辑);通过VfxUber.Create(path, UberSpec)创建 uber 材质,并在资源上设置关键字,绝不在运行时切换。火焰、火花、拖尾和魔法特效保持无光照;烟雾和尘土使用 Simple Lit,或在光照会变化时使用六向着色;URP 粒子着色器不投射阴影。GATE:渲染队列 3000、预期的关键字、无洋红色、无 Built-in 着色器(审计)。 - 构建特效。 完整示例:
VfxFireballKit.Build(投射物、枪口、带焦痕和炽热核心的撞击、爆炸)和VfxMeshFx.Build(网格冲击波、粒子冲击波、使用 Custom Data 的斩击)。Custom Data:Layer.custom1X/custom1Y/custom2X/custom2Y(曲线或随机范围)设置模块和VfxUber.Streams。生成代码调用VfxSeed.Reseed(go):固定种子仅用于截图。GATE:VfxAudit.AuditPrefabs报告 0 个错误;说明所有警告。 - 逐帧查看并评审效果。 使用
VfxSequence.CaptureEffect(适用于任意预制体:副本使用不同种子,循环使用stop_t,着色器时间固定为模拟时间)或CaptureFireball;运行ut_review.review_images;打开联系表。GATE:没有空白帧或洋红色帧;references/critique.md中的 A 和 B 均通过。 - 测量。 使用
VfxBudget.Overdraw(每像素层数、FSE、热力图)、Counts、VisualRadius(考虑网格)、ScreenTexel;运行ut_vfx.effect_verdict或sequence_verdict、texel_verdict、对整组效果运行importance_ladder,以及在游戏间距下对VfxStress.StressOverdraw运行crowd_readability、对间隔均匀的循环帧运行motion_gaps;使用AgentKit.AgentProfile.PlayModeTimings对照 0 个副本的场景运行压力测试档案。GATE:判断结果通过,或以书面理由说明警告;帧时间增量在 VFX 占用预算内。 - VFX Graph 运行时检查(
VfxGraphJobs.PlayTest):离屏摄像机目标、属性 + 事件、延迟帧后的数量、批处理的 N 个实例、转向后被剔除;运行ut_vfx.graph_hygiene(vfx_yaml_facts(...), peak_alive)。GATE:数量与公开的速率成正比,unbatchedInstanceCount == 0,两个方向均能正确剔除,容量接近存活数量。 - 修复一项问题,重新运行第 5 至 7 步并进行对比(
ut_review.compare,并排查看热力图)。提交时附上证据(采用 scenario-unity-expert 报告格式)。
数值
| 数值 | 对应 | 来源 |
|---|---|---|
| 约 90% | 在网格上移动的纹理效果(移动端) | Nordeus |
| 100 | 每个发射器的粒子数上限(移动端) | Nordeus |
| 8 到 10 / 5 到 6 / 2 到 3 | 高级 / 低级 / 多目标 SPELL 的绘制调用数(按玩法等级,而非设备档次) | Nordeus |
| 典型值 256,最大值 512;LUT 未压缩 | VFX 纹理导入(移动端) | Nordeus |
| 0.1 到 0.2 s;0.15 到 0.2 s | 枪口和闪光的持续时间;快速弹丸的拖尾时间 | Gabriel Aguiar |
| 约 0.3 s | 斩击持续时间(即挥砍动作) | Hovl Studio |
| 焦痕 1.75 到 2,热核心 0.75 到 1.3 | 命中表面上的两个叠加痕迹 | Gabriel Aguiar |
| 50 units/s | VFX Graph 的按距离生成阈值 | 17.3 source |
| 0.5 s;0.5 到 4 | 玩法停止后的拖尾时长;粒子上的纹素数 / 其屏幕像素数(详细纹理) | [added] |
| 1 层对比 5 到 7 层,0.037 对比 0.35 FSE | 峰值时的环形网格与 36 团烟尘冲击波,分辨率为 960 x 540 | observed |
| 0.51 FSE,p95 11,98 个粒子 | 火球套件在峰值时的表现(移动端档次) | observed |
| 1.05 FSE,540 个粒子;CPU +0.22 ms | 20 次爆炸在峰值时的表现;同时回放与不回放的对比(编辑器中,仅供参考) | observed |
| 0.89 对比 1.51 ms | 5,000 个网格粒子:URP Particles/Unlit 对比 6.3 Shader Graph 粒子模板 | observed |
质量门槛
- 可测量: 审核
counts.error == 0;效果或序列结论为通过,或对警告作出说明 (填充、粒子、视觉 / 玩法半径 0.85 到 1.2、窗口结束后粒子数为 0、拖尾低于 0.5 s);阶梯测试通过;人群测试在玩法间距下通过;纹素结论;压力测试增量;VFX Graph 契约、 数量、批处理、剔除、整洁性。 - 视觉: 已打开接触表;飞行中的弹头最亮;没有被几何体裁切;各层逐渐淡出; 热力图热点与可见内容相符;连续实例之间有所不同。
常见错误
| 错误 | 表现 | 修复方法 |
|---|---|---|
| 本可使用网格,却使用了粒子云(移动端) | 一个圆环使用了 5 到 7 层重叠效果(观察所得) | 使用圆环或圆弧网格 + 滚动的 uber 材质,1 层 |
| 使用自定义数据,却没有匹配的数据流 | 着色器读取到零值:没有侵蚀效果,也没有热启动(观察所得) | VfxUber.Streams;检查 vfx.stream_mismatch |
| 将通道打包纹理按 sRGB 导入 | 效果变暗、出现侵蚀 | 关闭 sRGB;检查 vfx.data_texture_srgb |
| 网格粒子网格上的顶点颜色使用浮点数 | 呈现暗蓝色、平坦效果,Alpha 为 0.25;MeshRenderer 则正常(观察所得) | Mesh.SetColors(List<Color32>);检查 vfx.mesh_color_format |
| 使用线性尺寸或侵蚀曲线 | 动作机械;v0.1 工具包中有 6 个此类曲线(观察所得) | 使用 EaseOut/EaseIn/Decelerate;检查 vfx.linear_curve |
| 将变体图集作为动画播放 | 所有粒子同时切换形状 | 随机固定帧:Frame over Time 在两个常量之间随机取值 |
| 在透明效果之后绘制其周围的扭曲效果 | 下方的辉光消失(剩余 31%,观察所得) | 先绘制扭曲效果:降低渲染顺序或增大 Sorting Fudge |
| 发布时工具包种子固定 | 每次效果完全相同 | 生成时调用 VfxSeed.Reseed |
| 高射速时每次效果都执行 Instantiate 和 Destroy | 分配峰值 | 使用对象池:调用 Clear + Play 重播(VfxStressSpawner),在加载时预热 |
| 近距离爆炸占满屏幕 | 全屏填充率峰值 | Renderer Max Particle Size(工具包:移动端为 0.35)[已添加] |
| 烟团或闪光效果以命中点为中心 | 与表面之间出现明显的直线切口 | 沿法线偏移(移动端)或使用软粒子(PC) |
| 叠加的 Alpha 图层使用相同的排序顺序 | 闪烁 | 使用不同的顺序,并将辉光置于最上层 |
| URP 中仍保留旧版粒子材质 | 仍能绘制,因此无人察觉;但没有 URP 功能 | 使用 VfxMaterials.MigrateLegacyJob,比较捕获结果 |
| 在发送帧断言 VFX Graph 数量 | 数值为 -1 或已过期 | 等待延迟帧及 60+ 帧 |
| 在未限制帧率的批处理 Play 模式中进行性能分析 | 300 帧仅相当于 0.3 秒的效果 | 设置 Time.captureDeltaTime = 1f / 60f,运行 600 帧 |
更多条目(VFX Graph 组合、事件、拖尾、捕获陷阱):见 references/critique.md 的 F 节。
交接
- 接收:来自 scenario-unity-gameplay:命中点和法线、伤害半径、投射物速度、射速;来自 scenario-unity-shaders:溶解、拖尾、实例化 Shader Graph 粒子、对
AgentKit/VFX/Uber的变体评审;来自 scenario-textures 或 scenario(Scenario MCP):翻页纹理和遮罩,用于加法混合的黑底灰度图;来自 scenario-unity-rendering-lighting:各档 Bloom、各档 Depth 和 Opaque Texture(软粒子、扭曲)、Decal 功能。 - 交付:给 scenario-unity-gameplay:带有
VfxTier的预制件、类似FireballProjectile的衔接代码、效果约定;给 scenario-unity-performance:压力测试场景、CSV、热力图;给 scenario-unity-mobile:各档预算和删减项(优先删减最不重要的层);给 scenario-unity-web:仅在 WebGPU 构建中使用 VFX Graph。 - 数据包:项目、渠道、用数字描述的简报、作业 ID、联系表和热力图、结论行、Verified、Assumed。
Unity 6.3 注意事项
- VFX Graph 17.3.0 是核心包;模板 01_Minimal_System 到 06_Firework 不公开任何内容;已弃用:带模式的 Trigger Event 区块、Set Position Shape、Collision Shape、Set Position From Source、Output Particle URP Lit Decal;YAML 中的输出排序为 Auto、Off 或 On(0、1、2)。
SetActiveVertexStreams取代了EnableVertexStreams;URP 粒子着色器没有 ShadowCaster 通道,并且翻页纹理混合时不使用运动矢量(VFX Graph 有 Flipbook Motion Blend):为主角或慢速元素保留带运动矢量的翻页纹理(Nordeus);URP 扭曲会采样不包含透明物体的 opaque texture。- 6.3 之后:6.5 弃用动态批处理,6.6 将其废弃;6.6 新增 Shader Graph Preprocessor Directives 和粒子节点;WebGPU 在 6.6 中脱离实验阶段。
参考资料
references/expert-notes.md:专家原则及其时间戳、分歧和判定条件。references/procedures.md:P0 到 P19,包含可复制的代码及其实际结果。references/critique.md:自我评审标准(玩法、工艺、预算、VFX Graph、诚信)和完整的错误表。references/gui-paths.md:通过窗口和菜单完成相同工作的步骤。references/sources.md:所有来源、凭据、URL、最佳时间戳、修订历史。scripts/ut_vfx.py(预算、结论、阶梯、群集、纹素、运动间隙、Graph 卫生检查、.vfx事实、pCache);scripts/AgentKit/Vfx/(作业、uber 材质、网格、mesh-first 工具包);scripts/Runtime/(压力生成器、投射物衔接代码、VfxTier、VfxSeed);scripts/Shaders/(VfxUber.shader、VfxOverdraw.shader);scripts/EditorGraph/(不受支持的 Graph 编写、SDF 烘焙)。