提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
GAS遵循UE网络范式:服务器权威(Authoritative Server)、客户端预测(Client Prediction)、差量复制(Delta Replication)。
主数据的真正生效都在服务器,客户端为提升手感可本地预演,若被服务器否决再回滚。
Client 输入 → 客户端预测执行 → 发送RPC请求
↓ ↑
预测Key标记 服务器授权/否决
↓ ↑
服务器结果复制 → 客户端对齐/回滚
一、能力激活的RPC与预测
激活流程(关键调用链)
- 客户端:ASC->TryActivateAbility(…) →(若需要)发起 ServerTryActivateAbility RPC
- 服务器:校验标签/冷却/资源 → 调 Ability->ActivateAbility()
- 客户端:本地开启预测窗口,先行播放动画/发特效/甚至临时改属性
FScopedPredictionWindow Prediction(ASC, /*bShouldGenerateNewKey*/ true);
ASC->TryActivateAbility(Handle);
// Within window: predicted tasks/GE/cues..
PredictionKey:为本次预测生成唯一标识,服务器确认时回传;若否决,客户端用这个键撤销对应预测操作(如移除已播的GameplayCue/还原属性)。
预测失败回滚
- 常见来源:资源不足、被控制、冷却中、目标非法
- GAS内部基于 PredictionKey 跟踪“预测生成物”,服务器否决→客户端自动撤销
- 你只需确保使用预测安全的API(下面各模块会标注)
二、目标数据(Targeting)同步
目标数据通过 UAbilityTask_WaitTargetData 协调,同步接口核心是:
- 客户端完成选取 → ASC->ServerSetReplicatedTargetData(Handle, PredictionKey, TargetData, …)
- 服务器复核合法性(距离、可见性、阵营等)→ 合法则继续;否则拒绝
数据承载体:FGameplayAbilityTargetDataHandle(结构体+序列化),非UObject,体量小,可预测。
典型用法:射线/地面点击/范围选择在本地即时反馈 → 服务器二次确认。
三、GameplayEffect(GE)复制与模式
激活中的GE存放于 FActiveGameplayEffectsContainer,复制用 Fast Array Serializer(只传增量:新增/更新/移除),并按复制模式裁剪信息量:
UENUM()
enum class EGameplayEffectReplicationMode : uint8 {
Minimal, // 非Owner几乎仅看到GameplayCue和必要最小信息
Mixed, // Owner多信息,旁观者最小信息(常用)
Full // 全量复制给所有人(带宽最高)
};
Minimal/Mixed:减少对非Owner客户端的带宽;Owner仍能拿到完整的Spec(如剩余时间、堆叠等)
GE本体(UGameplayEffect)不复制,复制的是运行时Spec与状态(轻量结构体)
预测GE:客户端可在预测窗口内本地ApplyGameplayEffectSpecToSelf(有的项目封装了预测友好的版本),服务器确认后对齐;否决则回滚。
四、属性(Attribute)复制
属性字段(FGameplayAttributeData)使用NetDeltaSerialize + OnRep_XXX:
只有变更才复制(差量)
推荐做法:
UPROPERTY(ReplicatedUsing=OnRep_Health)
FGameplayAttributeData Health;
UFUNCTION() void OnRep_Health(const FGameplayAttributeData& OldValue);
- 不要直接改变量,一律通过 GE/ASC API 改(否则不会触发聚合/回调/复制)
聚合与快照:计算走 FAggregator(Base/Add/Mult/Override叠加),GE应用时可捕获瞬时快照,保证确定性;复制传值而非引用,带宽更省。
五、标签(GameplayTag)复制
ASC维护OwnedTags/TagCount容器:
- 由GE授予/移除或ASC直接添加的“松散标签”
- 用紧凑格式与FastArray复制“计数变化”
- 常用:Status.Stunned 阻断激活、Cooldown.X 冷却中;
非Owner只需知道“是否被晕/在冷却”,无需所有细节 → 模式裁剪即可省带宽
六、GameplayCue(表现)复制与预测
Cue是“表现层信号”(特效/音效/震屏),复制极度精简:
- 只发CueTag + 少量参数(FGameplayCueParameters),客户端本地查表播放FX
- 支持预测Cue(先播后判),服务器否决→自动终止/回滚
- 建议把重资产交给本地:少传数据,多本地决定
七、能力任务(AbilityTask)与动画蒙太奇
许多Task内置网络/预测处理(如 PlayMontageAndWait、WaitTargetData):
- 客户端本地播蒙太奇(预测),ASC有一套Montage同步RPC(如播放、停止、播放速率、BlendOut),最终以服务器为准
- RootMotion/位移技能:尽量走角色移动预测通道(UE运动分层已做优化),避免大块数据自己复制
八、复制可见性:Owner vs Non-Owner
GAS大量使用OwnerOnly/Everyone/SkipOwner等复制条件:
- Owner得到完整状态(UI/操作需要)
- 其他客户端仅获最小必要(例如只知道对方“在中毒”和大致剩余时长,或仅视觉Cue)
- 配合 EGameplayEffectReplicationMode::Mixed,是实战最常用的带宽折中
九、关键代码片段(常用骨架)
1. 预测窗口 + 激活
if (ASC->CanActivateAbility(Handle)) {
FScopedPredictionWindow Pred(ASC, true);
ASC->TryActivateAbility(Handle);
}
2. 服务器端应用GE(权威)
FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(GEClass, Level, ASC->MakeEffectContext());
ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get()); // Server authoritative
3. 目标数据回传(客户端→服务器)
// Task 内部:Client确定目标后
ASC->ServerSetReplicatedTargetData(Handle, PredKey, TargetData, ApplicationTag, ASC->ScopedPredictionKey);
4. 属性Rep
void UMyAttrSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const {
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttrSet, Health, COND_None, REPNOTIFY_Always);
}
5. 标签(GameplayTag)复制
ASC维护OwnedTags/TagCount容器:
- 由GE授予/移除或ASC直接添加的“松散标签”
- 用紧凑格式与FastArray复制“计数变化”
- 常用:Status.Stunned 阻断激活、Cooldown.X 冷却中;
非Owner只需知道“是否被晕/在冷却”,无需所有细节 → 模式裁剪即可省带宽
6. GameplayCue(表现)复制与预测
Cue是“表现层信号”(特效/音效/震屏),复制极度精简:
- 只发CueTag + 少量参数(FGameplayCueParameters),客户端本地查表播放FX
- 支持预测Cue(先播后判),服务器否决→自动终止/回滚
- 建议把重资产交给本地:少传数据,多本地决定
7. 能力任务(AbilityTask)与动画蒙太奇
许多Task内置网络/预测处理(如 PlayMontageAndWait、WaitTargetData):
- 客户端本地播蒙太奇(预测),ASC有一套Montage同步RPC(如播放、停止、播放速率、BlendOut),最终以服务器为准
- RootMotion/位移技能:尽量走角色移动预测通道(UE运动分层已做优化),避免大块数据自己复制
8. 复制可见性:Owner vs Non-Owner
GAS大量使用OwnerOnly/Everyone/SkipOwner等复制条件:
- Owner得到完整状态(UI/操作需要)
- 其他客户端仅获最小必要(例如只知道对方“在中毒”和大致剩余时长,或仅视觉Cue)
- 配合 EGameplayEffectReplicationMode::Mixed,是实战最常用的带宽折中
9. 关键代码片段(常用骨架)
- 预测窗口 + 激活
if (ASC->CanActivateAbility(Handle)) {
FScopedPredictionWindow Pred(ASC, true);
ASC->TryActivateAbility(Handle);
}
- 服务器端应用GE(权威)
FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(GEClass, Level, ASC->MakeEffectContext());
ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get()); // Server authoritative
- 目标数据回传(客户端→服务器)
// Task 内部:Client确定目标后
ASC->ServerSetReplicatedTargetData(Handle, PredKey, TargetData, ApplicationTag, ASC->ScopedPredictionKey);
- 属性Rep
void UMyAttrSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const {
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttrSet, Health, COND_None, REPNOTIFY_Always);
}
十、带宽与一致性优化要点
- 选择合适复制模式:Mixed 多数项目够用;PvP观战密集可用 Minimal
- 减少非Owner信息:只让旁观者看到“结果/表现”,不看内部细节
- Cue轻量化:尽量用Tag驱动本地FX,参数只传必要的(位置/归属/标量)
- 分层预测:移动→UE Movement;技能→GAS预测;动画→Montage同步;不要自造大包
- 避免直接改属性:必须通过GE/ASC,才能触发聚合、Rep、UI回调
- Target合法性在服端再判:客户端只做手感,不要信任它
- 合理使用冷却/成本GE而非自写RPC判断(数据驱动 + 自动复制)
十一、常见坑位清单
- 忘了 InitAbilityActorInfo(Owner, Avatar) → 能力拿不到ActorInfo,网络异常
- Attribute未 ReplicatedUsing 或没 DOREPLIFETIME → 客户端UI不更新
- 直接写 Health.SetCurrentValue() → 不走GE管线,无回调无复制
- 用 Full 模式在多人战斗里 → 带宽爆表
- 预测API与普通API混用不当 → 回滚残留(务必在 FScopedPredictionWindow 内做预测)
- 目标数据未做服务器合法性校验 → 作弊风险、状态错乱
十二、心智模型总结
- 服权:一切“结果”以服务器为准
- 预测:客户端先演,键控回滚
- 差量:只传变化,少传大对象
- 裁剪:Owner全量、他人最少
- 数据驱动:GE/Tag/Attr/Cue各司其职,自动带来复制与一致性