关于 scenario-unity-gameplay
面向 Unity 6.3 游戏玩法系统开发的技能,涵盖物理、导航、AI、弹丸、音频与网络同步。它强调为状态指定单一写入方,并通过实测和测试验证系统行为与性能。
主要功能
- 01配置物理与碰撞检测
- 02构建并验证 NavMesh 导航
- 03组织 AI 行为与更新调度
- 04实现弹丸查询和对象池
- 05路由并限制游戏音频
- 06评估扩展性与网络同步需求
适用场景
- 01验证高速弹丸是否穿透碰撞体
- 02检查 NPC 穿门寻路与状态转换
- 03测量大量 Agent 下的帧耗时
- 04为多人游戏界定预测与权威模型
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-unity-gameplay安装后在 Claude Code 中输入/scenario-unity-gameplay确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/critique.mdexpert-notes.mdgui-paths.mdprocedures.mdsources.mdscripts/AgentKit/Gameplay/GameplayAudio.csGameplayAudit.csGameplayScenes.csGameplaySetup.csscripts/Runtime/AgentKit.Gameplay.Runtime.asmdefAgentLocomotionSync.csAIBudget.csAIBudgetMeter.csCombatAudio.csEnemyBrain.csEnemyDirector.csFloatingCapsule.csHealth.csJobStallProbe.csJobWaitMeter.csLineOfSightBatch.csMixerControl.csNetSim.csPerception.csPerceptionBenchmark.csProjectile.csProjectilePool.csProximityBenchmark.csQueries.csSoundEmitter.cs详细介绍
Unity 游戏玩法系统(游戏玩法系统程序员)
在 Unity 6.3 中进行专家级游戏玩法开发,意味着每个系统都由一个所有者管理其状态,以明确选定的速率运行,并通过测量加以验证:每帧步数、真实速度下的穿隧测试、穿过真实门的路径查询、计时转换,以及真实 Agent 数量下的最差帧耗时。
这项技能涵盖来自 6.3 手册、AI Navigation 2、NGO 和已发行游戏(Survival Kids、Very Very Valet)的专家经验增量,并使用 6000.3.21f1 运行作业和测试。如果此处提到的同级技能不在你的可用技能中,请让用户安装它
(npx skills add scenario-labs/skills --skill <name>);若无人值守,请根据工具架构继续操作,并标明这一缺口。
必备背景: scenario-unity-expert(通道、审查循环、6.3 陷阱)。安装:
ut_gameplay.install(P)(核心 AgentKit + AgentKit.Gameplay 作业 + AgentKit.Gameplay.Runtime 程序集)。
立场(专家增量)
- 先解决频率,再处理碰撞体。 每帧的固定步数会成倍增加物理开销;“调用次数接近 10 可能意味着存在问题”(6.3 手册)。在 50 Hz 下测得:默认 Maximum Allowed Timestep 为 0.333 s 时,400 ms 的帧运行了 17 个追赶步;将上限设为 0.1 s 后,步数降至 5,世界运行速度则降为 0.25 倍。这个上限用慢动作换掉了开销滚雪球。
- 观察到穿透后,逐个对象升级 CCD,然后让刚体保持原样。 Discrete、Continuous Speculative(“通常是最佳选择”)、Continuous,最后才是 Continuous Dynamic(6.3 手册)。实测:所有 CCD 模式都能让物体以 300 m/s 撞上静态的 5 cm 墙而不穿透;撞上薄动态板时,Continuous 从 50 m/s 开始穿透,Continuous Dynamic 则从 100 m/s 开始。对投射物自身形状执行扫描(
SweepProjectile)阻止了 300 m/s 的射击;而半径为 0.25 m 的球体扫描击中了中心射线漏掉的柱子:让投射与碰撞体相匹配(LlamAcademy,fJyi7l2tWKo [00:09:54])。 - 每块状态只设一个写入者。 Agent 加上非运动学 Rigidbody 会造成竞争;Agent 加上启用的障碍物会与自身冲突;Agent 和动画必须保持单向流动(AI Navigation 手册)。实测:当运行中的 TransformAccessArray 作业持有某个变换时,主线程对它的写入会等待整个作业完成(22 到 38 ms;在作业外为 0.003 ms)(Survival Kids,ZkvK0mX-id4 [00:26:42])。
- 用查询验证 NavMesh,并将其保存为资源。 每种 Agent 类型对应一个 NavMesh Surface(SMWxCpLvrcc [00:06:35]);体素大小设为 Agent 半径的 3 倍,室内设为 4 到 6 倍(手册)。实测:门宽为 Agent 直径的 1.2 倍时,每个半径 3 个体素会导致门无法通过,6 个则可以;没有 HeightMesh 时,楼梯读数会偏离踏面 0.17 到 0.26 m,使用后偏差为 0;雕刻结果会在一帧后反映到查询中。
- 采用最轻量的 AI 结构,并从一个地方统一调度。 1 到 2 种行为时采用非结构化方案;状态转换较少时采用 FSM(转换数量趋向 N 的平方);需要优先级时采用行为树;需要串联目标时采用 GOAP(LlamAcademy,CZvfuNfdc1M [00:07:13])。由一个 director 调度各个 Brain;不同类型的对象通过一个按类型排序的
UpdateHub注册(Survival Kids:代码相同,平均耗时降低 12%,最差帧从 2.18 ms 降至 1.43 ms [00:23:59];此处在 1,000 和 10,000 个对象下测得平均耗时降低 7 到 14%)。 - DOTS 是一个总称;采用前先测量。 先安排更新顺序,再对 NativeArrays 使用 Burst 作业,并用一个小型只读作业拷出变换数据;接着用 Burst
.Run()填补主线程等待作业的时间(实测:节省 21 到 26 ms 中的 6 到 10 ms);最后才考虑 Entities(Survival Kids [00:08:49]、[00:39:15])。大型 CPU 驱动对象数量、竞技型网络代码或确定性需求,都是尽早决定采用 ECS 的理由(Turbo Makes Games,Bz24Jp30nkM [00:21:58])。 - NGO 2.x 没有完整的客户端预测与协调功能(没有回滚并重放循环),也不支持服务器回溯。 客户端预期(
AnticipatedNetworkVariable、AnticipatedNetworkTransform、OnReanticipate)是手动构建预测功能的基础(NGO 2.7 手册;随附的 2.13.0 文档中也有相同内容)。默认由服务器掌控;优先使用HasAuthority而非IsServer;判断使用 NetworkVariable 还是 RPC 时,问一句“晚加入的客户端是否需要它?”。 - 所有声音都要经过混音器组;距离效果在音源处处理。 输出设为 None(默认值)会绕过混音器;衰减和距离低通在 AudioSource 处处理,然后才进入任何混音器组(手册)。实测:Logarithmic rolloff 会在超过 Max Distance 后继续衰减(到 40 m 时为 1/d);快照参数上的滑块会冻结该混音器组的情绪,直到调用
ClearFloat;默认优先级下,60 个未限频的循环武器声音会盖过音量较小的音乐。将频繁播放的声音限制在 10 到 15 个,并淘汰最早播放的声音(git-amend,BgpqoRFCNOs [00:17:56])。
先确定
只询问一次:游戏类型,以及玩法是否具有竞争性;平台和帧预算(默认桌面端 16.67 ms、移动端 x 0.65、scenario-unity-mobile);Agent、弹丸和音效的峰值数量;玩家是否会推动物体,以及是否会被物理效果推动;移动由动画驱动还是由 Agent 驱动;多人游戏(玩家数量、主机离开、回滚或命中回溯);现有的层、混音器和场景;2D 还是 3D(scenario-unity-2d)。默认设置:3D URP、50 Hz,层为 Environment 8、Player 9、Enemy 10、Projectile 11,其中 Projectile x Projectile 和 Enemy x Enemy 碰撞关闭;Humanoid Agent 类型(半径 0.5);以代码 FSM 实现、频率为 10 Hz 的敌人;一个混音器,结构为 Master > Music、SFX、UI。
工作流
- 项目物理设置。 使用
GameplaySetup.ConfigurePhysics(层、Physics.IgnoreLayerCollision,通过SerializedObject设置 Time 和 Physics)。关卡检查:在第二个进程中调用ReadPhysicsSettings,确认矩阵、0.02 s 和上限(实测:Time.*setter 加上SaveAssets无法持久化设置)。 - 导航。 使用
GameplayScenes.BuildNavArena模式:surface 的layerMask= Environment,voxel = 半径/3,调用BuildNavMesh(),然后像 Inspector 中的 Bake 按钮一样调用AssetDatabase.CreateAsset(surface.navMeshData, ...),并保存。Agent 需要走楼梯时,构建 Height Mesh;在植被上使用 NavMesh Modifier Remove Object;通过 Collect Objects = Volume 或仅 Modifier 限定范围。关卡检查:每扇门的CalculatePath均为 PathComplete;在出生点和台阶上调用SamplePosition;重新打开后数据仍存在,场景仍为文本格式;已打开覆盖层截图。 - 角色与移动。 如果设计中不涉及物理交互,使用 CharacterController(skin >= max(0.01, 半径的 10%));涉及物理交互时使用
FloatingCapsule。使用AgentLocomotionSync实现 Agent 移动:由 Agent 驱动(关闭根运动,速度取自deltaPosition),或由动画驱动(updatePosition = false,超出半径后拉回 0.9)。关卡检查:cc.*审计错误为 0;记录ControllerTests数值;运行漂移测试。 - AI。 使用
EnemyBrain+EnemyDirector;先执行成本最低的感知检测,然后使用QueryTriggerInteraction.Ignore在遮挡物层掩码上发出一条射线;规模较大时,在LineOfSightBatch中批量处理(使用 RaycastCommand 和 QueryParameters);死亡时,先关闭 Agent,再启用障碍物。关卡检查:EnemyLoopTests依次记录 Patrol > Chase > Attack > Search > Patrol > Dead 的耗时。 - 弹丸、查询与对象池。 刚体弹丸:在实际速度和厚度下进行穿透测试,以确定是否启用 CCD;对于速度快或体积大的弹丸,使用
SweepProjectile。通过Queries使用 NonAlloc(循环检查数量,标记缓冲区已满;需要顺序时进行排序);在查询通过 transform 移动的碰撞体前调用Physics.SyncTransforms()。使用带有 collectionCheck 的ProjectilePool。关卡检查:在目标数值下运行CcdLadderMeasured和QueryTests;确认CountActive恢复为 0。 - 音频。 使用
GameplayAudio.CreateCombatMixer或模板创建混音器;将每个音源都路由到混音器;使用限制发声器数量的CombatAudio;滑块使用不受任何快照控制的参数;音乐优先级设为 0;使用AudioLowPassFilter曲线实现随距离变化的声音闷化效果。关卡检查:运行AudioTests、AudioSpatialTests(垃圾音效刷屏时的音乐频段),确认audio.*错误为 0;在压力测试下,分别以 30、15、5 的上限进行人工试听,选出听感饱满的最低上限(BgpqoRFCNOs [00:17:23])。 - 规模测试。 按实际数量构建
BuildCrowdScene;使用AgentProfile.PlayModeTimings、AIBudgetMeter、JobWaitMeter;提出任何 DOTS 方案前,先运行PerceptionBenchmark、ProximityBenchmark、dots_gate和proximity_gate。关卡检查:确认 p95 和最差帧;使用 Profile Analyzer 和原生设备分析器,在开发版 Player 中验证编辑器中的数值(Survival Kids [00:10:27];scenario-unity-performance)。 - 网络代码范围界定(仅限多人游戏)。 使用
ut_gameplay.netcode_fit、权限与 Tick 表;在接入 NGO 前,先通过NetSim在 Edit Mode 中验证预测。关卡检查:协调测试全部通过;设计文档明确说明哪些内容必须手动构建。
数值
| 数值 | 相对项 | 来源 |
|---|---|---|
| 0.02 秒(50 Hz);每个 60 fps 帧执行一步时为 1/60 | 固定时间步长 | 手册;pTz3LMQpvfA [00:01:22] |
| 默认值为 0.333 秒(50 Hz 下约 16 步);0.1 秒 = 5 步 | 最大允许时间步长 | 实测 |
| 每帧接近 10 次调用 = 有问题 | Physics.Processing 调用 | 手册 |
| 离散检测下,速度超过约 (厚度 + 2r + 0.02) / dt 时会穿透 | 50 Hz | discrete_tunnel_speed;实测速度为 20 m/s,墙厚 5 cm |
| 皮肤宽度 >= max(0.01, 0.1 x 半径);步长 0.1 至 0.4 m;最小移动量为 0 | 2 m CharacterController | 手册 |
| 下垂量 = m x g / k;反向时加速度 x2 | 浮动胶囊体 | 实测;Toyful [00:05:32] |
| 每个半径 3 个体素;内部 4-6 个;超过 8 个通常没有帮助;门宽 >= 约 1.5 倍 Agent 直径 | Agent 半径 | 手册;实测 |
| 当漂移 > Agent 半径时,漂移拉回量为 0.9 x delta(实测 r = 0.4 时最大值为 0.41 至 0.45 m,不拉回时为 1.84 m) | 动画驱动的 Agent | AI Navigation 手册 |
| Carve 在下一帧生效;Carve Only Stationary 静止 0.5 秒后重新刻蚀(实测为 0.50 至 0.61 秒) | NavMesh 障碍物 | 手册;实测 |
| AI 切片耗时 0.10-0.15 ms(Update),而 10 Hz 调度器约为 0.01 ms | 200 个 Agent,Editor | 实测 |
| 200 个 Agent 时,Burst 并行处理比托管代码慢;从 2,000 个开始更快 | 感知预筛选 | 实测 |
| 暴力搜索每个项-查询对耗时:托管代码 7-8 ns,Burst 1.2-1.5 ns;10,000 x 200 时,网格耗时 0.3-1.3 ms,对比 14-15 ms | 邻近查询 | 实测(Survival Kids KD 树:5 ms 降至 0.08 ms,查询 1,000 个项 20 次) |
| 200 ms 可被察觉;FPS < 100 ms;30 Hz tick;更新率 > 射击频率 | 网络代码 | NGO 手册 |
| 32 个真实 / 512 个虚拟声部;优先级 0-256,默认 128 | 音频设置 | 实测;手册 |
| 超过 Max Distance 后,增益 = min/d(对数);在 Max Distance 处为 0(线性) | 3D 衰减 | 实测 |
| 低通截止频率曲线为 22000 至 10 Hz,需要 Audio Low Pass Filter | 距离闷音 | 手册;实测:8 m 处为 8 kHz,增益 0.03x |
| 滑块 0.0001..1 -> 20 log10(v);常见音效使用 10-15 个实例 | 混音器、声部 | DU7cgVsU2rM [00:14:30];BgpqoRFCNOs [00:17:56] |
质量门槛
- 可衡量:
compile_errors == [];在新进程中读回设置;AuditGameplayScene错误数为 0(负向对照会标记所有预置陷阱);套件全部通过并解析出GAMEPLAY_METRICS; CCD 和扫掠测试使用实际速度;对象池恢复至基线;人群 CSV 使用实际数量,并包含 p95、最差帧和 AI 分片;code_audit错误数为 0。 - 视觉: NavMesh 覆盖层和人群截图通过
ut_review.image_checks检查,且已打开查看; 当任务要求评估手感时,根据帧画面判断控制器和运动效果。
常见错误
| 错误 | 表现 | 修复 |
|---|---|---|
| 每个快速移动的刚体都使用 Continuous Dynamic | CPU 占用上升,仍会穿过动态平台 | 优先使用推测模式;SweepProjectile |
| 用细射线检测粗大的投射物 | 漏掉半径实际触碰到的物体 | 扫掠碰撞体的形状(Queries.SweepShape) |
| 在 NonAlloc 循环中遍历整个缓冲区 | 命中结果过期或被悄悄丢弃,且顺序不定 | 按数量遍历;当数量 == 长度时设置标记;排序 |
Physics.autoSyncTransforms = true | 每次查询前都隐式同步,出现 CS0618 | 查询前调用一次 Physics.SyncTransforms() |
在编辑器任务中设置 Time.maximumDeltaTime = x | 下次启动时又变回 0.333 | 在 TimeManager.asset 上使用 SerializedObject |
调用 BuildNavMesh() 后保存场景 | 数据嵌入场景,场景文件变成二进制 | 先调用 AssetDatabase.CreateAsset(surface.navMeshData) |
| 覆盖已有场景并保存后再查询 | NavMesh 为空,路径无效 | 使用 GameplayScenes.EnsureLoaded |
| 楼梯没有 HeightMesh | Agent 沿斜坡滑上去 | 构建 Height Mesh,然后重新烘焙 |
| 障碍物刻蚀的同一帧就重新寻路 | 路径穿过新出现的障碍物 | 下一帧再查询 |
| Agent 和动画都在移动角色 | 脚步滑动、抖动 | 只保留一种方式:AgentLocomotionSync |
| 关闭域重载时使用静态单例 | 沿用上次会话的管理器,重复更新 | 在 SubsystemRegistration 中重置;使用 SafeUnregister |
从 FindObjectsByType<Transform> 创建 TransformAccessArray | 帧开销随场景规模增长 | 自行维护列表,在发生变化时重建 |
| 作业运行时写入 TAA 变换 | 主线程因等待作业而停顿 | 只读复制出来;在 Complete() 后写入 |
| 大规模使用触发器检测“附近的任何东西” | 每个移动物体都会触发变换同步 | 自行维护列表,使用 Burst,然后使用网格(proximity_gate) |
| MonoBehaviour 所在文件名与其名称不同 | 保存场景后会悄然消失 | 每个文件只放一个 MonoBehaviour |
用 PlayClipAtPoint 播放战斗音效 | 音量滑块不起作用 | 使用路由到某个组的池化发声器 |
| 在 SFX 组上使用低通“距离”效果 | 所有声音都同样变闷 | 为每个音源设置 AudioLowPassFilter 曲线 |
| 认为 Max Distance 能让 Logarithmic 音源静音 | 远处仍能听见(1/d) | 使用 Linear 或自定义曲线,或者按距离剔除 |
| 在计划中声称“NGO 有客户端预测” | 承诺的循环实际上并不存在 | 使用预测加手工构建的协调机制,或使用 Netcode for Entities |
交接
- **接收:**来自 scenario-unity-architecture 的代码结构、服务和 Input System 操作; 来自 scenario-unity-world-building 的 Environment 层级;来自 scenario-unity-animation 的动画角色;来自 scenario-unity-performance 的帧预算。
- **交付:**运动速度、状态参数和选定的耦合方式,交给
scenario-unity-animation;命中事件交给 scenario-unity-vfx;健康度和滑块钩子交给
scenario-unity-ui(
MixerControl.SetUserVolume);人群 CSV、AI 分片和作业等待时间交给 scenario-unity-performance;2D 物理交给 scenario-unity-2d;音频限制交给 scenario-unity-mobile 和 scenario-unity-web。数据包:场景、作业 ID、审计 JSON、测试指标、CSV 判定结果、已验证 和假设。
Unity 6.3 说明
- 3D 物理:
linearVelocity、linearDamping、PhysicsMaterial、Physics.simulationMode;Physics.autoSyncTransforms已弃用(不只是关闭);RaycastCommand接受QueryParameters;Physics.BakeMesh(int...)已过时。 - AI Navigation 2.0.14:按 NavMesh Surface 烘焙(Navigation 窗口中没有 Bake);OffMesh Link 已弃用。
- Entities 1.4.8(核心功能来自 6.4);6.5 移除
Entities.ForEach;6.6 内置 Burst,新 项目在 Play 时跳过域重载。 - 附带 NGO 2.13.0,1.x 已弃用;
[Rpc(SendTo.Server)];重写OnUpdate,而非NetworkTransform.Update;Multiplayer Services 取代 Lobby 和 Relay。
参考资料
references/expert-notes.md:按来源整理的原则、时间戳、 测量数据。references/procedures.md:以代码形式呈现的流程,附实时测试和 结果(G1 到 G16)。references/critique.md:自我审查评分标准。references/gui-paths.md:上述流程对应的窗口和菜单。references/sources.md:来源、凭据、时间戳、修订历史。scripts/ut_gameplay.py:安装、数值、门槛(DOTS、接近度、 网络代码、AI 结构)、衰减模型、指标、代码审计。scripts/AgentKit/Gameplay/:编辑器作业。scripts/Runtime/:大脑、导演、中心、查询、扫描、耦合、Burst 作业、仪表、对象池、音频、控制器、网络代码模型。