总览
GAS 真正值钱的地方之一是多人预测。玩家按下翻滚或开火时,本地立刻有反馈;服务器仍然保留权威,确认成功或拒绝后再对账。这个能力让手感和反作弊之间有了工程上的折中。
本篇讲 PredictionKey、FScopedPredictionWindow、Net Execution Policy、ReplicationMode、RPC 批处理和“哪些东西不能预测”。
源码依据
关键文件:
GameplayPrediction.hAbilitySystemComponent_Abilities.cppAbilitySystemComponent.hGameplayAbilityTypes.h
GameplayPrediction.h 注释把边界写得很清楚。GAS 可以预测:
- 初始 Ability 激活。
- 触发事件。
- GE 对属性和标签的修改,但不包括复杂 Execution 结果。
- GameplayCue。
- Montage。
- 部分移动。
GAS 不预测:
- GameplayEffect 移除。
- Periodic GameplayEffect。
- 一些复杂服务器权威结果。
PredictionKey 是什么
PredictionKey 是客户端和服务器对账的票据。LocalPredicted Ability 激活时,客户端创建 PredictionKey,本地先执行副作用,同时把激活请求发给服务器。服务器接受后,复制权威结果;服务器拒绝后,客户端回滚预测副作用。
流程:
- 客户端
TryActivateAbility。 - 创建
FScopedPredictionWindow。 - 发送
ServerTryActivateAbility,携带 PredictionKey。 - 客户端立即
ActivateAbility。 - 服务器检查 Cost、Cooldown、Tag、Authority、目标合法性。
- 服务器接受或拒绝。
- 客户端按复制结果对账。
PredictionKey 只对发起客户端有意义。其他客户端看到的是服务器权威复制结果。
Prediction Window 的边界
预测窗口通常只覆盖初始调用栈。也就是说,在 ActivateAbility 里立即应用的 GE、Cue、Montage 可以共享这次 PredictionKey;但如果你开了一个计时器,或者等待输入释放,后续回调已经离开初始窗口。
后续阶段如果还要发送预测事件,需要新的 FScopedPredictionWindow。
void UGA_ChargeShot::HandleInputReleased(float HoldSeconds)
{
UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();
if (!ASC)
{
return;
}
FScopedPredictionWindow PredictionWindow(ASC, true);
SendTargetDataAndApplyPredictedCue(HoldSeconds);
}
没有新的预测窗口,客户端以为自己提交了数据,服务器却无法正确关联预测副作用。
Net Execution Policy
| 策略 | 行为 | 用法 |
|---|---|---|
LocalPredicted |
客户端先执行,服务器确认 | 翻滚、开火、近战起手 |
LocalOnly |
只在本地执行 | UI 和本地辅助 |
ServerInitiated |
服务器发起,客户端可表现 | AI 技能、服务器事件 |
ServerOnly |
只在服务器执行 | 权威管理、敏感结算 |
不要把所有 Ability 都设为 LocalPredicted。只有手感强、可以接受预测失败回滚的行为才适合预测。购买、掉落、任务提交、排行榜结算这些应该 ServerOnly。
ReplicationMode 与可见性
ASC 的 ReplicationMode 决定 Active GE 细节如何复制:
| 模式 | 拥有者 | 非拥有者 |
|---|---|---|
Full |
完整 | 完整 |
Mixed |
完整 | 精简 |
Minimal |
精简 | 精简 |
玩家常用 Mixed。拥有者需要 UI、冷却、Buff 细节;其他客户端多数只需要 Cue 和状态 Tag。AI 大量存在时用 Minimal 减少网络压力。
RPC 批处理
GAS 支持把 Ability 激活、TargetData、End 等 RPC 批在一起,源码 README 提到 FScopedServerAbilityRPCBatcher 和 ShouldDoServerAbilityRPCBatch。这适合“按下开火后立即发送目标数据并结束”的短 Ability。
批处理适合:
- Hitscan 武器。
- 单次近战命中。
- 快速交互。
不适合:
- 长时间引导。
- 多段 Combo。
- 需要多次 TargetData 的复杂技能。
使用前要测高延迟和丢包,不要只看本地 Listen Server。
属性预测对账
预测 Instant GE 时,源码会把它临时当成 Infinite delta,等服务器结果回来后移除预测副本。Attribute 复制必须用:
DOREPLIFETIME_CONDITION_NOTIFY(..., REPNOTIFY_Always)GAMEPLAYATTRIBUTE_REPNOTIFY
否则客户端可能收到了服务器值,但 RepNotify 没触发,UI 或属性聚合没对上。
项目落地测试清单
每个 LocalPredicted Ability 至少测试:
- 0 延迟。
- 100ms 延迟。
- 200ms 延迟。
- 服务器拒绝激活。
- Cost 不足。
- Cooldown 未结束。
- 目标无效。
- 角色死亡时激活。
- 激活中断线重连。
- 远端玩家是否看到正确 Cue。
如果某个技能在 0 延迟看起来没问题,但高延迟下重复播 Cue、UI 闪回、伤害出现两次,说明预测副作用和服务器对账没有设计好。
常见坑
- 所有技能都 LocalPredicted。 敏感结算会变得难控制。
- 离开预测窗口后继续当成本地预测。 异步回调需要新的 PredictionWindow。
- 预测周期 GE。 源码明确不预测 Periodic。
- 预测 GE 移除。 移除由服务器权威复制。
- 属性 RepNotify 写错。 UI 和预测对账会出问题。
- 只测 Listen Server。 Dedicated Server 和网络模拟必须测。
本篇结论:预测不是“客户端也跑一遍”这么简单,而是一套有票据、有窗口、有边界的对账机制。把不能预测的内容留给服务器,把能预测的表现做好回滚,GAS 多人体验才会稳定。
源码路径索引
Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/AbilitySystemComponent.hEngine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AbilitySystemComponent.cppEngine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cppEngine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/Abilities/GameplayAbility.hEngine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffect.hEngine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/AttributeSet.hEngine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayPrediction.h