Attribute的一生
Attribute操作指北
定义属性
AttributeSet继承自UAttributeSet
定义辅助宏ATTRIBUTE_ACCESSORS(ClassName, PropertyName)
这个类有一个宏可以快速定义一个Attribute的Get()、Set()、Init(),在AttributeSet.h我们可以看见这个宏的定义与注释
1 | /** |
那么,我们在我们自己的AttributeSet类里边就可以调用这个宏去一键搞定很多事
拿我一直在跟进的Warrior项目举例
1 | // WarriorAttributeSet.h |
在我们定义好这个宏后我们只需要输入
1 | // example |
这样,我们就可以获得
1 | Get属性名() |
1 | // WarriorAttributeSet.h |
我们可以直接在.cpp文件中去调用宏为我们定义好的Init函数去初始化属性
1 | Init属性名(); |
1 | // WarriorAttributeSet.cpp |
当然,所谓任何对属性的更改都应该在GE中进行,这也包括初始化
使用GameplayEffect修改AttributeSet
GE有两种方式修改AttributeSet
方案1(快、常用):GE蓝图+曲线表格(Curve Table)
先直接定义一个GE,连自定义的父类都用不着,就是一个改Attribute值的工具罢了
这个方案主要是在修饰符(Modifiers)里边利用曲线表格的数据改数值
当AttibuteSet定义好后所有的GE都可以直接捕捉,这也导致当一个项目有很多个AttributeSet时会很难找到想改的属性
定义曲线表格(Curve Table)

曲线表格可以根据等级来给不同的属性不同的值,其中行号为等级列标为属性
定义GE
这个方案里边GE就是用来改值的工具,蓝图都不用连,只需要添加GameplayEffect下的修饰符(Modifiers)即可
GE的持续时间策略:
GE有3种持续时间策略:实时、无限、拥有持续时间
1 | 1. Instant(即时) |
修饰符:
- 修饰符可以选取已定义的AttributeSet的所有Attribute
- 可对一个Attribute进行加、乘、除、覆盖、无效

通常初始化属性选择覆盖
要使用曲线表格,我们需要将 幅度计算类型 改为 可扩展浮点


方案二(细、大量计算):GE蓝图+计算类(GameplayEffectExecutionCalculation)
这个方案在创建完GE后不需要用到修饰符了,要用执行(Excutions),然后选择一个计算类GameplayEffectExecutionCalculation
这个计算类太难了到时候新发一个文档介绍
制作GESpec、ContextHandle
要想应用GE(待会的GE就标识GameplayEffect)就需要使用GESpec,这可以大概理解为GE的实例化,但在GE的传输中大多用GESpecHandle,相当于GESpec的标签
一个GESpec由如下部分组成
- GE本体指针(在制作Spec的时候用的是类指针)
- Level:当前实例等级
- Duration:持续时间(策略不同该值不同)
- Context:上下文
- SetByCallerMagnitudes(Map<Tag, float>):动态修改器
- Handle:Spec的标签
这个Context作为元数据又有很多东西
- Instigator:肇事者(逻辑意义上的)
- EffectCauser:来源(物理意义上的)
- TargetData:目标数据
- Tag Snapshots:
- SourceTags:来源标签组
- TargetTags:目标标签组
制作Context
直接用ASC提供的MakeEffectContext()函数即可,不要用new!
1 | FGameplayEffectContextHandle ContextHandle = ASC->MakeEffectContext(); |
MakeEffectContext()函数会进行如下操作
- 调用工厂模式创建实例(?)
- 绑定拥有者(Owner)与同步对象(Sync Object)
- 自动填充默认发起者(Instigator) 和 来源对象(SourceObject)
- 初始化内部容器:没值的全赋空值
但在必要时还需要去手动该值
比如Warrior项目里的
1 | // 添加技能来源 |
制作Spec
有了Context后就要开始制作Spec了、
用ASC提供的MakeOutgoingSpec()
1 | FGameplayEffectSpecHandle SpecHandle = ASC->MakeOutgoingSpec(GEClass, Level, ContextHandle) |
- GEClass就是GE的类引用
- Level可以根据该值选定数值
- ContextHandle即刚刚所创建的ContextHandle
这样,一个GESpec就知道了GE所做的事情,level对应的数值,Context所包含的GE外的附加信息
应用GE
在cpp内提供了两个函数用于应用GE到自身/目标
ApplyGamepalyEffectToSelf/Target()
声明:
1 | FActiveGameplayEffectHandle UAbilitySystemComponent::ApplyGameplayEffectToSelf(const UGameplayEffect *GameplayEffect, [UAbilitySystemComponent *Target], float Level, const FGameplayEffectContextHandle& EffectContext, FPredictionKey PredictionKey) |
形参作用:
- GameplayEffect,注意这里是个GE的对象指针,通常而言我们手上应该只有类引用 若我们需要直接得到GE的对象引用,我们应该使用GE的
1
TSubclassOf<UGameplayEffect> GameplayEffectClass;
CDO (Class Default Object)
CDO可理解为类引用的一个默认对象,每个类引用TSubclassOf<>都仅有一个
要想拿到CDO,我们需要使用GetDefaultObject<>()1
UGameplayEffect* GECDO = GEClass->GetDefaultObject<UGameplayEffect>();
- Target当应用对象为Target时用到,传入Target的ASC
- Level提供等级,可根据等级调整数值
- EffectContext即上下文,提供GE作用域等各种信息
- PredictionKey管理网络同步,调用函数时可以不传该值
调用方法: 通常要传入GECDO, Level, 再使用ASC提供的MakeEffectContext直接创建Context
1 | InASCToGive->ApplyGameplayEffectToSelf(EffectCDO, Level, ASC->MakeEffectContext()); |
返回类型: FActiveGameplayEffectHandle可以追踪已存在的GE,它是用来手动移除 (Remove) 或 查询剩余时间 的唯一凭据
特点: 在这个函数中无需自己创建GESpec,拿到GECDO即可,在函数体里会自动拿传入的GE对象引用、Level、Context制作Spec,最终调用ApplyGamepalyEffectSpecToSelf/Targe()
在函数内,Spec创建、调用写法如下,当然,手写还是用ASC提供的MakeOutgoingSpec即可
1 | FGameplayEffectSpec Spec(GameplayEffect, EffectContext, Level); |
ApplyGamepalyEffectSpecToSelf/Target
特点: 可以高度自定义GESpec,说到底ApplyGamepalyEffectToSelf/Target()就是调用这个函数
声明:
1 | FActiveGameplayEffectHandle UAbilitySystemComponent::ApplyGameplayEffectSpecToTarget(const FGameplayEffectSpec &Spec, [UAbilitySystemComponent *Target], FPredictionKey PredictionKey) |
形参作用:
- Spec
- Target当应用对象为Target时用到,传入Target的ASC
- PredictionKey管理网络同步,调用函数时可以不传该值
至于怎么创建Spec刚刚说过了
返回类型: FActiveGameplayEffectHandle可以追踪已存在的GE,它是用来手动移除 (Remove) 或 查询剩余时间 的唯一凭据