mcpField
S
scenario-unity-gameplay

scenario-unity-gameplay

github.com/scenario-labs开发1.2k267 次安装1 天前更新
代码仓库

关于 scenario-unity-gameplay

面向 Unity 6.3 游戏玩法系统开发的技能,涵盖物理、导航、AI、弹丸、音频与网络同步。它强调为状态指定单一写入方,并通过实测和测试验证系统行为与性能。

主要功能

  1. 01配置物理与碰撞检测
  2. 02构建并验证 NavMesh 导航
  3. 03组织 AI 行为与更新调度
  4. 04实现弹丸查询和对象池
  5. 05路由并限制游戏音频
  6. 06评估扩展性与网络同步需求

适用场景

  1. 01验证高速弹丸是否穿透碰撞体
  2. 02检查 NPC 穿门寻路与状态转换
  3. 03测量大量 Agent 下的帧耗时
  4. 04为多人游戏界定预测与权威模型

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/scenario-labs/skills --skill scenario-unity-gameplay

安装后在 Claude Code 中输入/scenario-unity-gameplay确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
critique.md
expert-notes.md
gui-paths.md
procedures.md
sources.md
scripts/AgentKit/Gameplay/
GameplayAudio.cs
GameplayAudit.cs
GameplayScenes.cs
GameplaySetup.cs
scripts/Runtime/
AgentKit.Gameplay.Runtime.asmdef
AgentLocomotionSync.cs
AIBudget.cs
AIBudgetMeter.cs
CombatAudio.cs
EnemyBrain.cs
EnemyDirector.cs
FloatingCapsule.cs
Health.cs
JobStallProbe.cs
JobWaitMeter.cs
LineOfSightBatch.cs
MixerControl.cs
NetSim.cs
Perception.cs
PerceptionBenchmark.cs
Projectile.cs
ProjectilePool.cs
ProximityBenchmark.cs
Queries.cs
SoundEmitter.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 程序集)。

立场(专家增量)

  1. 先解决频率,再处理碰撞体。 每帧的固定步数会成倍增加物理开销;“调用次数接近 10 可能意味着存在问题”(6.3 手册)。在 50 Hz 下测得:默认 Maximum Allowed Timestep 为 0.333 s 时,400 ms 的帧运行了 17 个追赶步;将上限设为 0.1 s 后,步数降至 5,世界运行速度则降为 0.25 倍。这个上限用慢动作换掉了开销滚雪球。
  2. 观察到穿透后,逐个对象升级 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])。
  3. 每块状态只设一个写入者。 Agent 加上非运动学 Rigidbody 会造成竞争;Agent 加上启用的障碍物会与自身冲突;Agent 和动画必须保持单向流动(AI Navigation 手册)。实测:当运行中的 TransformAccessArray 作业持有某个变换时,主线程对它的写入会等待整个作业完成(22 到 38 ms;在作业外为 0.003 ms)(Survival Kids,ZkvK0mX-id4 [00:26:42])。
  4. 用查询验证 NavMesh,并将其保存为资源。 每种 Agent 类型对应一个 NavMesh Surface(SMWxCpLvrcc [00:06:35]);体素大小设为 Agent 半径的 3 倍,室内设为 4 到 6 倍(手册)。实测:门宽为 Agent 直径的 1.2 倍时,每个半径 3 个体素会导致门无法通过,6 个则可以;没有 HeightMesh 时,楼梯读数会偏离踏面 0.17 到 0.26 m,使用后偏差为 0;雕刻结果会在一帧后反映到查询中。
  5. 采用最轻量的 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%)。
  6. 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])。
  7. NGO 2.x 没有完整的客户端预测与协调功能(没有回滚并重放循环),也不支持服务器回溯。 客户端预期(AnticipatedNetworkVariable、AnticipatedNetworkTransform、OnReanticipate)是手动构建预测功能的基础(NGO 2.7 手册;随附的 2.13.0 文档中也有相同内容)。默认由服务器掌控;优先使用 HasAuthority 而非 IsServer;判断使用 NetworkVariable 还是 RPC 时,问一句“晚加入的客户端是否需要它?”。
  8. 所有声音都要经过混音器组;距离效果在音源处处理。 输出设为 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。

工作流

  1. 项目物理设置。 使用 GameplaySetup.ConfigurePhysics(层、Physics.IgnoreLayerCollision,通过 SerializedObject 设置 Time 和 Physics)。关卡检查:在第二个进程中调用 ReadPhysicsSettings,确认矩阵、0.02 s 和上限(实测:Time.* setter 加上 SaveAssets 无法持久化设置)。
  2. 导航。 使用 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;重新打开后数据仍存在,场景仍为文本格式;已打开覆盖层截图。
  3. 角色与移动。 如果设计中不涉及物理交互,使用 CharacterController(skin >= max(0.01, 半径的 10%));涉及物理交互时使用 FloatingCapsule。使用 AgentLocomotionSync 实现 Agent 移动:由 Agent 驱动(关闭根运动,速度取自 deltaPosition),或由动画驱动(updatePosition = false,超出半径后拉回 0.9)。关卡检查:cc.* 审计错误为 0;记录 ControllerTests 数值;运行漂移测试。
  4. AI。 使用 EnemyBrain + EnemyDirector;先执行成本最低的感知检测,然后使用 QueryTriggerInteraction.Ignore 在遮挡物层掩码上发出一条射线;规模较大时,在 LineOfSightBatch 中批量处理(使用 RaycastCommand 和 QueryParameters);死亡时,先关闭 Agent,再启用障碍物。关卡检查:EnemyLoopTests 依次记录 Patrol > Chase > Attack > Search > Patrol > Dead 的耗时。
  5. 弹丸、查询与对象池。 刚体弹丸:在实际速度和厚度下进行穿透测试,以确定是否启用 CCD;对于速度快或体积大的弹丸,使用 SweepProjectile。通过 Queries 使用 NonAlloc(循环检查数量,标记缓冲区已满;需要顺序时进行排序);在查询通过 transform 移动的碰撞体前调用 Physics.SyncTransforms()。使用带有 collectionCheck 的 ProjectilePool。关卡检查:在目标数值下运行 CcdLadderMeasured 和 QueryTests;确认 CountActive 恢复为 0。
  6. 音频。 使用 GameplayAudio.CreateCombatMixer 或模板创建混音器;将每个音源都路由到混音器;使用限制发声器数量的 CombatAudio;滑块使用不受任何快照控制的参数;音乐优先级设为 0;使用 AudioLowPassFilter 曲线实现随距离变化的声音闷化效果。关卡检查:运行 AudioTests、AudioSpatialTests(垃圾音效刷屏时的音乐频段),确认 audio.* 错误为 0;在压力测试下,分别以 30、15、5 的上限进行人工试听,选出听感饱满的最低上限(BgpqoRFCNOs [00:17:23])。
  7. 规模测试。 按实际数量构建 BuildCrowdScene;使用 AgentProfile.PlayModeTimings、AIBudgetMeter、JobWaitMeter;提出任何 DOTS 方案前,先运行 PerceptionBenchmark、ProximityBenchmark、dots_gate 和 proximity_gate。关卡检查:确认 p95 和最差帧;使用 Profile Analyzer 和原生设备分析器,在开发版 Player 中验证编辑器中的数值(Survival Kids [00:10:27];scenario-unity-performance)。
  8. 网络代码范围界定(仅限多人游戏)。 使用 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 Hzdiscrete_tunnel_speed;实测速度为 20 m/s,墙厚 5 cm
皮肤宽度 >= max(0.01, 0.1 x 半径);步长 0.1 至 0.4 m;最小移动量为 02 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)动画驱动的 AgentAI Navigation 手册
Carve 在下一帧生效;Carve Only Stationary 静止 0.5 秒后重新刻蚀(实测为 0.50 至 0.61 秒)NavMesh 障碍物手册;实测
AI 切片耗时 0.10-0.15 ms(Update),而 10 Hz 调度器约为 0.01 ms200 个 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 DynamicCPU 占用上升,仍会穿过动态平台优先使用推测模式;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
楼梯没有 HeightMeshAgent 沿斜坡滑上去构建 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。

参考资料

评论

支持 Markdown