关于 scenario-unreal-gameplay
面向 Unreal Engine 游戏玩法开发的专业技能,指导 Agent 规划数据归属、C++ 与 Blueprint 分工、GAS、AI、输入和 UI,并通过隔离测试与质量门检查实现。
主要功能
- 01按生命周期与网络状态放置数据
- 02规划 C++ 与 Blueprint 职责
- 03配置 GAS、输入与 AI 工作流
- 04检查 StateTree、UI 和游戏逻辑
- 05通过调试与自动化测试验证机制
适用场景
- 01规划多人游戏中的复制状态与 ASC 初始化
- 02搭建角色能力、输入映射和 AI 状态树
- 03验证跳跃、冲刺、冷却与生命值 UI
- 04检查性能指标并评审游戏玩法实现
MCP 相关
README 将 Epic 的 MCP 工具集列为处理 Blueprint 图内容的途径之一;未说明该技能自身提供 MCP 服务。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-unreal-gameplay安装后在 Claude Code 中输入/scenario-unreal-gameplay确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/critique.mdexpert-notes.mdgui-paths.mdprocedures.mdsources.mdscripts/ue_gameplay_cpp.pyue_gameplay.py详细介绍
虚幻游戏玩法(框架、Blueprint 与 C++、输入、GAS、AI、UI)
专家级实践意味着,每项数据都存放在其生命周期和网络状态与之匹配的类中;逻辑以文本形式编写(C++ 或小型函数),位于仅存放数据的 Blueprint 和由 Python 配置的数据资产之下;每项机制都要先单独验证(注入输入、调试命令、追踪),再在游戏中评估。Agent 不会假装能创作自己无法操作的图表:它会说明每部分最终落在哪种载体上,并且只对剩余部分升级处理。如果此处提到的同级技能缺少某项,请用户安装它
(npx skills add scenario-labs/skills --skill <name>);如果用户未能及时处理,则根据工具架构继续,并标明这一缺口。
必备背景: scenario-unreal-expert(通道、审查循环、5.8 陷阱)。
状态(2026-09-24): 尚未安装 Unreal。离线层已通过
(python3 tests/code/unreal-gameplay/run_all.py --offline)。所有编辑器内代码片段和生成的 C++ 文件尚未在 Unreal 中运行(C++ 尚未编译);job_00_gameplay_probe.py 会给出
[verify] 名称。
核心立场(专家经验补充)
- 根据数据生命周期和网络可见范围来放置数据。 GameMode 仅存在于服务器,负责规则;GameState 和 PlayerState 会复制给所有人;PlayerController 仅对所属客户端可见;Pawn 是可重生的身体;GameInstance 子系统的生命周期长于地图(Forsythe IaU2Hue-ApI [00:13:26])。状态放入带 RepNotify 的复制属性中,绝不使用 multicast;C++ RepNotify 不会在服务器上触发(JOJP0CvpB8w [00:10:50]、[00:14:48])。在 World Partition 下,BeginPlay 可能运行多次:务必保证其幂等(Shao KBn62trwkLw [00:07:29])。
- 以 C++ 为基类,Blueprint 子类只存数据;Blueprint 可以依赖 C++,反之则不行(Forsythe VMZftEVDuCE [00:24:10])。调优项用
EditDefaultsOnly,资产选项用TSubclassOf插槽,外观效果用BlueprintImplementableEvent钩子:逻辑用便于比较差异的文本编写,Blueprint 作为默认值配置。广播者通过单向委托触发事件(OnDied),由控制器、生成器和 HUD 绑定([00:19:25])。 - Blueprint 的开销按节点计算;真正的成本在于硬引用。 与 CharacterMovement 更新相比,Tick 开销微不足道;转换为 Blueprint 类会使其与转换者一同加载(Arnbjörnsson S2olUc9zcB8 [00:06:07]、[00:28:01])。只有当 Insights 跟踪显示某段代码位于热点集中时,才将其移至 C++(Forsythe [00:16:33])。
- 效果说明是什么,能力说明何时发生。 新功能应使用新的响应式资产,通过标签监听;响应逻辑查询 GE 的资产标签;使用一个带 SetByCaller 的伤害 GE;使用 Wait Delay,而非 Delay;始终调用 End Ability;使用
PlayMontageAndWait;Gameplay Cues 仅用于外观效果(Shao 8bi0rnXnRj4 [00:19:25]、[00:47:35]、[00:21:43]、[00:49:12]、[00:13:02];tranek 9.3)。只有在代码设置后,SetByCaller 值才存在,因此控制台隔离测试要使用固定幅度的对应版本[新增]。 - 在两端连接 ASC。 将 ASC 放在 Pawn 上,除非属性需要在重生后保留(此时放在 PlayerState 上);在服务器端的
PossessedBy中,以及所属客户端的 PlayerController 的AcknowledgePossession(或OnRep_PlayerState)中调用InitAbilityActorInfo;玩家使用 Mixed,AI 使用 Minimal,并尽早设置;绝不预测伤害(tranek 4.1、4.1.2、7.3、4.10)。 - 输入是带间接层的数据。 输入 Action 映射到输入标签,再映射到能力,因此 Pawn 不包含任何能力逻辑(Noland,Lyra Fj1zCsYydD8 [00:34:33]);不同游戏状态的映射上下文应互斥(框架文档)。
- StateTree 会在任何转换发生前选择一个叶节点,并且只在转换要求时重新检查进入条件(Mononen YEmq4kcblj4 [00:11:22])。每个应该结束状态的条件都需要事件或转换;每个状态都应明确设置成功和失败的退出方式,否则会回退到根节点([00:14:13]);通过绑定提供数据,将调优项设为树参数([00:16:40]);动作委托给 Gameplay Ability,并设置超时(Bruno XKQfMZOXFv0 [00:17:37]、[00:19:15])。
- UI 通过事件更新,绝不使用属性绑定;开启 Global Invalidation,然后排查被冻结的控件(Albert VxX1aah6TZM [00:24:09])。控件 Tick 从 Paint 触发,文本更改会导致布局失效,Hidden 只会重绘,而 Collapsed 会重新布局([00:10:09]、[00:13:56]、[00:12:53])。
先明确
| 输入 | 为何会改变计划 | 未说明时的默认值 |
|---|---|---|
| 单人或多人、监听服务器或专用服务器 | ASC 放置、复制模式、预测、测试矩阵 | 单人,从第一天起就进行权威性检查(网络文档) |
| C++ 工具链 | 已安装 Xcode 26.6;Epic 列出的最低版本为 26.0,推荐 26.1.1,26.4 不兼容,未列出 26.6 | 在承诺使用 C++ 前先探测构建(P3);否则采用 Blueprint 加数据的备用方案 |
| 规模 | 数十个 Actor、数百个静态可交互对象,还是数千个移动对象 | Actor;然后是 Instanced Actors,再然后是 Mass(KBn62trwkLw [00:01:06]) |
| 手感目标 | 跳跃高度、冲刺距离、冷却时间、行走和追逐速度 | 询问;不调整 CharacterMovement 默认值会「感觉像 Unreal 教程项目」(IaU2Hue-ApI [00:23:39]) |
| 帧预算、平台 | Tick 和 AI 上限 | 60 fps;数值参考 scenario-unreal-performance |
| 角色和关卡输入 | 骨架、蒙太奇、通知;导航边界、玩家起始点、流送 | 参考 scenario-unreal-animation 和 scenario-unreal-world-building |
创作方式
可脚本化:C++ 类、Blueprint 子类及其 CDO 默认值、数据资产和数据表、Enhanced Input 资产、Gameplay Effect 外壳、StateTree 资产和参数、控件树
(EditorUtilityLibrary.add_source_widget)。图表内容(事件图表、GE 修改器〔Python 无法实例化〕、StateTree 状态和转换、Behavior Tree 节点)应通过按变体复制的模板、Epic 的 MCP 工具集、调用 help() 后使用 5.8 BlueprintGraphEditor,或一次精准的 GUI 操作来完成。完整表格:流程 P15。
工作流
- 每个引擎探测一次。
job_00_gameplay_probe.py(无头模式)会列出技能标记为 [verify] 的类、属性和 cvar,包括AbilitySystem.Fix...资产标签 cvar,以及一个dumpticks示例。门槛:已写入probe.json。 - 做出决策。
ue_gameplay.decide(spec):数据放置位置、使用 Blueprint 还是 C++、是否使用 GAS、AI 系统、ASC 放置位置、初始化和复制、通信、UI,以及各自的决策条件。门槛:每项数据都有所有者、复制方式和存续情况(重生、地图切换、流式加载)。 - 搭建项目和配置。
ue_env.enable_plugins;使用ini_set设置bUseDebugTargetFromHud、将资产标签 cvar 设为 0、配置标签;为showdebug设置一个 GameMode HUD 类。门槛:config_verdict无问题。 - C++ 层。
cpp_scaffold()(角色基类、玩家控制器、冲刺和近战能力、AI 控制器、StateTree 任务、生命值小组件、本机标签),patch_build_cs()、build_command();新增反射成员时关闭编辑器。门槛:构建退出码为 0;cpp_lint无错误(包含ASC_CLIENT_INIT_MISSING)。 - 通过 Python 构建数据层。 Blueprint 子类和 CDO 默认值:移动、
JumpMaxCount = 2、GameMode 上的player_controller_class,以及每个 AI pawn 上的ai_controller_class= 持有树的控制器 Blueprint,并设置auto_possess_ai为 placed 或 spawned。通过input_plan()创建输入资产,通过ge_plan_verdict检查计划中的 GE 资产,以及 StateTree 资产和小组件树。门槛:ge_plan_verdict和audit_gameplay()无问题。 - 清理图表残留。 将每棵树写为 spec,通过
statetree_spec_verdict检查;通过 MCP 工具集或一次 GUI 操作从statetree_outline连接各节点(references/gui-paths.md);按敌人设置树参数。门槛:spec 检查通过;树编译时没有未绑定的 Inputs;ai_setup_verdict无问题。 - 先单独测试,再测试整个链路。 在控制台使用固定数值的调试 GE,使用
showdebug abilitysystem(3 页),对每个 AI 使用游戏性调试器;通过gas_log_triage读取log LogAbilitySystem VeryVerbose;然后使用pie_scenario或 PythonAutomationTest 注入Input.+key,并由阈值根据参数推导出的分析器判定(analyze_jump、dash_expected_cm、analyze_cooldown、state_sequence_verdict、health_bar_verdict)。门槛:每个场景均通过,没有卡住的能力,也没有未计划的根状态进入。 - 收集证据并审查。 截取调试页面、
showdebug enhancedinput、受伤前后的生命值条、各状态下的敌人和导航网格覆盖范围;查看前先运行ue_review.image_checks。门槛:没有空白画面;UI 保持同步。 - 测量。 使用开发版本或在新窗口中运行 PIE:
stat game、stat unit、dumpticks(tick_verdict)、在关闭和开启 Global Invalidation 时运行stat slate,并在任何重写之前记录场景的 Insights 跟踪(rewrite_gate)。门槛:tick 清点有充分依据;仅重写热集函数;将跟踪交给 scenario-unreal-performance。
数值
| 数值 | 相对于 | 来源 |
|---|---|---|
| 每个 Actor 的 Tick 开销:BP 在编辑器中为 3.59 us,打包后为 1.84 us;C++ 约为 0.64 至 0.70 us | 100,000 个 Actor,UE 5.4 | Arnbjörnsson [00:05:00] |
| CMC 为 241 us,PlayerController 每帧为 125 us | 同一 Lyra 帧 | Arnbjörnsson [00:06:07] |
| BP 节点打包后为 0.1 us(编辑器中为 0.5);C++ 调用为 0.7 us(编辑器中为 0.8) | 每次调用 | Arnbjörnsson [00:18:48] |
| Print String 每次调用为 88.8 至 272.5 us | Development 构建 | Arnbjörnsson 演示文稿 00:20:10 |
| Fortnite:客户端每帧约 80 次 Tick,服务器为 700 次 | Tick 函数 | Arnbjörnsson [00:11:03] |
| 每帧处理几十个结果时,GetAllActorsOfClass 没问题;WithTag 和 WithInterface 会扫描每个 Actor | 世界规模 | Arnbjörnsson [00:37:17],[00:38:56] |
| 空 Tick:从 5.6 起免费 | 编译器会将其禁用 | myth article |
| 最多 20 个活跃敌人;异步路径偏移为 0.2 至 0.5 s;攻击者为 1 个危险敌人或最多 3 个弱敌人 | Lords of the Fallen,主机平台,每位玩家 | Bruno [00:16:25],[00:15:20],[00:38:40] |
| Lyra 模态框弹出时重绘了 207 个 Widget;修复后为 25 个 | 绘制次数 | Albert [00:35:03] |
| 跳跃顶点高度 h = v^2 / 2g;二段跳会重置 Velocity.Z,按下时增加高度 h | JumpZVelocity,重力 980 cm/s^2 x GravityScale | 物理 [新增] |
| 地面冲刺 = 速度 x 持续时间 + 退出滑行距离(以 600 cm/s 计算约为 24 cm) | GroundFriction 8,BrakingFrictionFactor 2,BrakingDecelerationWalking 2048 | 引擎模型 [新增,待验证] |
质量门槛
- 可测量:
cpp_lint(资源路径和依赖方向、多播状态、Tick 中的 Reliable RPC、服务器上的 OnRep、客户端 ASC 初始化、Widget Tick、Collapsed 切换、重复 BeginPlay);ge_plan_verdict;statetree_spec_verdict;ai_setup_verdict;audit_gameplay(Tick 清点、 硬引用闭包、冷却标签、Widget 绑定);带派生阈值的场景 verdict;gas_log_triage为空;showdebug abilitysystem未显示卡住的能力;StateTree 跟踪中没有计划外的根节点进入; 每次重写前都经过rewrite_gate。 - 视觉: 从固定摄像机观察跳跃弧线和冲刺;敌人的朝向和间距;生命值条与冷却 UI 同步; 开启 Global Invalidation 时没有冻结的 Widget;多人游戏中的双客户端截图一致。
常见错误
| 错误 | 表现 | 修正 |
|---|---|---|
| 在 GameMode 上保存临时客户端数据 | 客户端什么也看不到 | 使用 GameState 或 PlayerState |
| 使用多播同步门的状态 | 后加入的玩家看到门是关着的 | 使用复制属性 + RepNotify |
| 从玩家强制转换到可选 Blueprint | 加载时间长,占用内存 | 使用 C++ 基类、接口、IsA(软引用) |
| “不要使用 Tick”,却让 Timeline 每帧运行 | 开销相同,代码更难 | 减少 Tick 中的 Actor,聚合处理,设置间隔 |
只在 PossessedBy 中初始化 ASC | 客户端出现“Can't activate LocalOnly or LocalPredicted ability” | 使用 AcknowledgePossession 或 OnRep_PlayerState |
| 技能永不结束;在技能中使用 Delay | 无法再次触发;取消后等待仍会继续 | 确保所有路径都结束技能;使用 Wait Delay |
| 在技能中使用 PlayMontage | 其他机器上看不到蒙太奇 | 使用 PlayMontageAndWait |
| 在控制台应用 SetByCaller GE | 伤害为零,并出现缺少 magnitude 的日志 | 使用固定 magnitude 的对应项(GE_Damage_Debug) |
| 查询 GE 授予的标签 | 对错误的效果触发反应 | 使用资产标签;将 asset-tag cvar 设为 0 |
| 假设进入条件会被轮询 | 敌人在攻击范围内仍会继续追击 | 在运行状态中使用事件或 On Tick 转换 |
| 状态没有失败转换 | “为什么会转到根状态” | 明确设置成功和失败转换 |
| 在同一状态中将 Delay 放在 MoveTo 旁 | 移动时一直等待 | 使用独立的 Wait 状态;5.6 Task Control Flow |
| 在敌人的 CDO 上设置 C++ 控制器类 | 敌人站着不动 | 使用持有该行为树的控制器 Blueprint;将其放置到场景中或生成 |
| 使用 UMG 属性绑定和 widget Tick | 产生逐帧开销;失效期间会冻结 | 使用属性委托发送事件 |
| 经常切换的条使用 Collapsed | 每次切换都会使布局失效 | 使用 Hidden |
| 在视口 PIE 中测试失效 | 看不到性能提升 | 使用新窗口 PIE |
| 游戏运行时同步保存 | 出现卡顿,有认证风险 | 使用 AsyncSaveGameToSlot |
| 凭感觉认为统计数据有问题就重写 | 浪费精力,没有收益 | 使用 Insights trace 和 rewrite_gate |
| 手动挑选测试阈值 | 正确的构建也会失败 | 根据参数推导阈值 |
| Agent Blueprint 中将计时器写成 Delay 链 | 150 个 Delay 节点(Deiter [00:56:45]) | 使用 Timeline、C++ 计时器或 GAS 任务 |
交接
- 接收自 scenario-unreal-animation:角色 Blueprint 子类、带有变量契约的 AnimBP、带根运动和扭曲目标的蒙太奇、在命中帧触发的可复用
AN_SendGameplayEvent通知;自 scenario-unreal-world-building:带导航边界、玩家起始点、流送范围、Data Layers 的关卡,以及作为 Asset User Data 的游戏数据。 - 交付给 scenario-unreal-vfx:事件契约(
vfx_event_contract:标签、触发器、载荷、来自游戏数据的半径、窗口、最大速率、生成路径;提示效果仅用于外观);给 scenario-unreal-performance:Development 构建、作为自动化测试的最坏情况场景、其 Insights 跟踪、Tick 统计、AI 和 ASC 计数(perf_handoff);给 scenario-unreal-animation:调优后的移动模型,先于运动匹配数据(DeVoe FLDXtAV7qsw [00:07:01]);给 scenario-unreal-pipeline-automation:用于 CI 的自动化测试。
UE 5.8 说明
- 复制属性自动注册(5.6);Iris 对被许可方已可用于生产,但通用复制仍为默认选项;Mover 仍为 Experimental。
- Enhanced Input 与 Common UI 统一(可用于生产);Combo 触发器已弃用;
UPlayerMappableInputConfig已弃用(5.4)。 - GAS:NonInstanced 能力已弃用(5.5);冷却 GE 验证(5.7);Blueprint 中的
GetAssetTags和GetGrantedTags(5.7);调试命令位于AbilitySystem.Ability和AbilitySystem.Effect下(5.4)。 - StateTree:Task Control Flow 和计划 Tick(5.6);Rewind Debugger 跟踪(5.7);按标签设置起始状态、可逆绑定、
UE::StateTree::EComparisonOperator(5.8)。 - Blueprint:已移除原生化(5.4);IsA (Soft)(5.5);空 Tick 不产生开销(5.6);
BlueprintGraphEditor、不允许局部变量遮蔽成员、bp.MaxAsyncActionCount(5.8)。
参考资料
references/expert-notes.md:专家原则,附时间戳和不同意见。references/procedures.md:每个流程的完整代码、测试路径和状态;创作界面(P15)。references/critique.md:Agent 用于评估自身输出的评分标准。references/gui-paths.md:完成相同工作的编辑器、菜单和设置。references/sources.md:所有来源、凭据、URL 和最佳时间戳。scripts/ue_gameplay.py:决策、脚手架、Lint、GE 和 StateTree 规格检查、跟踪分析、交接;编辑器内辅助工具(尚未运行)。scripts/ue_gameplay_cpp.py:C++ 源代码模板(逻辑放在 C++ 基类中,Blueprint 子类仅包含数据);尚未编译。