GAS 文档阅读摘抄笔记
Ability System Component
- 所有期望使用GameplayAbility, 包含Attribute, 或者接受GameplayEffect的Actor都必须附加ASC.
- ASC附加的Actor被引用作为该ASC的OwnerActor, 该ASC的物理代表Actor被称为AvatarActor. 比如MOBA游戏中玩家控制的英雄, 其中OwnerActor是PlayerState, AvatarActor是英雄的Character类.
- 绝大多数Actor的ASC都附加在其自身, 如果你的Actor会重生并且重生时需要持久化Attribute或GameplayEffect(比如MOBA中的英雄), 那么ASC理想的位置就是PlayerState.
- 如果ASC位于PlayerState, 那么你需要提高PlayerState的NetUpdateFrequency, 其默认是一个很低的值, 因此在客户端上发生Attribute和GameplayTag改变时会造成延迟或卡顿. 确保启用Adaptive Network Update Frequency(net.UseAdaptiveNetUpdateFrequency=1),之后对Actor的NetUpdateFrequency和MinNetUpdateFrequency的设置就可以自适应的改变复制频率。
- 使用ABILITYLIST_SCOPE_LOCK()来防止在它的作用域里移除列表里的Ability.
ABILITYLIST_SCOPE_LOCK(); for (FGameplayAbilitySpec& Spec : ActivatableAbilities.Items){...}
同步模式
ASC定义了三种不同的同步模式用于同步GameplayEffect, GameplayTag和GameplayCue: Full, Mixed和Minimal. Attribute由其AttributeSet同步.
同步模式 | 如何使用 | 描述 |
---|---|---|
Full | 单人 | 所有GameplayEffect都同步到客户端. |
Mixed | 多人, 玩家控制的Actor | GameplayEffect只同步到其所属客户端, 只有GameplayTag和GameplayCue同步到所有客户端. |
Minimal | 多人, AI控制的Actor | GameplayEffect从不同步到任何客户端, 只有GameplayTag和GameplayCue同步到所有客户端. |
- Mixed同步模式需要OwnerActor的Owner是Controller. PlayerState的Owner默认是Controller但是Character不是. 如果OwnerActor不是PlayerState时使用Mixed同步模式, 那么需要在OwnerActor中调用SetOwner()设置Controller.
设置和初始化
- ASC一般在OwnerActor的构建函数中创建并且需要明确标记为Replicated. 这必须在C++中完成.
- OwnerActor和AvatarActor的ASC在服务端和客户端上均需初始化, 你应该在Pawn的Controller设置之后初始化(Possess之后), 单人游戏只需参考服务端的做法.
- 对于玩家控制的Character且ASC位于Pawn, 我一般在服务端Pawn的PossessedBy()函数中初始化, 在客户端PlayerController的AcknowledgePossession()函数中获取Pawn的ASC来初始化.
- 于玩家控制的Character且ASC位于PlayerState, 我一般在服务端Pawn的PossessedBy()函数中初始化, 在客户端PlayerController的OnRep_PlayerState()函数中初始化, 这确保了PlayerState存在于客户端上.
Gameplay Tags
- FGameplayTag是由GameplayTagManager注册的形似Parent.Child.Grandchild...的层级类标签, 这些标签对于分类和描述对象的状态非常有用
- 当给某个对象设置标签时, 如果它有ASC的话, 我们一般添加标签到ASC以与其交互.
- 多个GameplayTag可被保存于一个FGameplayTagContainer中, 相比TArray
,GameplayTagContainer做了一些很有效率的优化。 - 应该在项目设置里开启GamePlayTag的快速复制功能。此外GameplayTag编辑器可以选择填写普遍需要同步的GameplayTag以对其深度优化.
- 如果GameplayTag由GameplayEffect添加, 那么其就是可同步的. ASC允许你添加不可同步的LooseGameplayTag且必须手动管理.
- 对于标签的访问和比较,应该使用UGameplayTagManager提供的方法,相比直接使用字符串比较,GameplayTagManager实际上使用关系节点(父, 子等等)保存GameplayTag以获得更快的处理速度.
- UPROPERTY宏Meta = (Categories = "GameplayCue")用于在蓝图中过滤标签而只显示父标签为GameplayCue的GameplayTag
- 如果你想过滤函数中的GameplayTag参数, 使用UFUNCTION宏Meta = (GameplayTagFilter = "GameplayCue")
- 响应Tag的添加和删除:
AbilitySystemComponent->RegisterGameplayTagEvent(FGameplayTag::RequestGameplayTag(FName("State.Debuff.Stun")), EGameplayTagEventType::NewOrRemoved).AddUObject(this, &AGDPlayerState::StunTagChanged);
Attribute
- 如果某项数值是属于某个Actor且游戏相关的, 你就应该考虑使用Attribute
- Attribute一般应该只能由GameplayEffect修改, 这样ASC才能预测(Predict)其改变
- Attribute也可以由AttributeSet定义并存于其中. AttributeSet用于同步那些标记为replication的Attribute.
- Attribute的类型FGameplayAttributeData由BaseValue和CurrentValue组成,CurrentValue用于记录当前值,BaseValue用于备份和恢复上一个值。
- 在PreAttributeChange中处理对CurrentValue的修改以及取值范围问题, 在PostGameplayEffectExecute中处理GE对BaseValue的修改.
- InstantGE可以永久性修改BaseValue,Duration&InfiniteGE可以修改CurrentValue,PeriodicGE被视为InstantGE并且可以修改BaseValue.
- Meta Attribute是临时的、可被任意GE修改、不可同步的,将于任意Attribute交互的临时属性。 比如伤害值作为Meta Attribute占位符, 而不是使用GE直接修改生命值Attribute, 使用这种方法, 伤害值就可以在GameplayEffectExecutionCalculation中由buff和debuff修改, 并且可以在AttributeSet中进一步操作。
UAbilitySystemComponent::GetGameplayAttributeValueChangeDelegate
可以为一个属性返回一个委托,这个委托会在属性值发生改变时广播。- 如果一个Attribute的值基于其他的Attribute,当其他属性变化,自己也自动变化,可以使用一个基于n个Attribute或MMC的InfiniteGE来让它自动推导.
TestAttrA = (TestAttrA + TestAttrB) * ( 2 * TestAttrC)
AttributeSet
- AttributeSet用于定义, 保存以及管理对Attribute的修改。开发者应该继承UAttributeSet. 在OwnerActor的构造函数中创建AttributeSet会自动注册到其ASC. 这必须在C++中完成.
- 你可以使用多个AttributeSet来表示按需添加到Actor的Attribute分组, 例如, 你可以有一个生命相关的AttributeSet, 一个魔法相关的AttributeSet等等。
- Attribute在内部被引用为AttributeSetClassName.AttributeName, 当你继承AttributeSet时, 所有父类的Attribute仍将保留父类名作为前缀.
- ASC中不应该有多个同一类的AttributeSet,否则ASC会随机选择一个操作。
- AttributeSet拥有一个Attribute, 并不意味着必须要使用它, 未使用的Attribute只占用很少的内存。
- AttributeSet可以在运行时从ASC上添加和移除,但是移除是有客户端奔溃的风险的。
- TODO:Item Attribute(武器弹药)