1. 引入GAS
打开插件Gameplay Abilities
在项目的.Build.cs文件中加入“GameplayAbilities”
、"GameplayTags"
、"GameplayTasks"
模块。
在.uproject文件中加入插件模块
创建GA类
创建根组件
2. GAS的创建并执行流程
整个GAS系统从激活到命中处理最终回收的总体流程如下:
2.1 GA的激活
在Charater中激活GA,使用AbilitySystemComponent->TryActivateAbility(*Handle)
函数
bool ARPGTestCharacter::ActiveSkill(FGameplayTag SkillName)
{
if(AbilitySystemComponent)
{
// 传入FGameplayTag,在Map中寻找
if(const FGameplayAbilitySpecHandle* Handle = skills.Find(FName(*SkillName.ToString())))
{
return AbilitySystemComponent->TryActivateAbility(*Handle);
}
}
return false;
}
再转到UAbilitySystemComponent::InternalTryActivateAbility(FGameplayAbilitySpecHandle Handle)
函数,在函数内部调用Ability->CallActivateAbility(Handle, ActorInfo, ActivationInfo, OnGameplayAbilityEndedDelegate, TriggerEventData)
,其中Ability是GA
void UGameplayAbility::CallActivateAbility(const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo, FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate,
const FGameplayEventData* TriggerEventData)
{
PreActivate(Handle, ActorInfo, ActivationInfo, OnGameplayAbilityEndedDelegate, TriggerEventData);
// GA的激活函数,我们重载了此处的激活函数
ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
}
我们通过在Charater中使用TryActivateAbility(*Handle)
,再在GA中重写ActivateAbility
函数,最终调到了GA。
注意:我们重写的这个激活函数中的信息全是GAS底层传给我们的。
在GA中重写的ActivateAbility
函数需要去播放蒙太奇,而播放蒙太奇是Task的工作,所以在GA中调用静态方法去激活并添加事件绑定。
UAbilityTask_PlayMontageAndWait* URPGGameplayAbility::CreatePlayMontageAndWaitProxy(FName TaskInstanceName,
UAnimMontage* InMontageToPlay, float Rate, FName StartSection, bool bStopWhenAbilityEnds,
float AnimRootMotionTranslationScale, float StartTimeSeconds)
{
/** 调用AbilityTask的静态方法来创建Task,内部创建一个Task对象并且设置对应的参数 */
URPGAbilityTask_PMontageAndWait* InPlayMontageAndWaitTask = URPGAbilityTask_PMontageAndWait::CreatePlayMontageAndWaitDamageEventProxy(
this,
TaskInstanceName,
InMontageToPlay,
AbilityTags,
Rate,
StartSection,
bStopWhenAbilityEnds,
AnimRootMotionTranslationScale,
StartTimeSeconds);
if(InPlayMontageAndWaitTask)
{
// 绑定四个事件
InPlayMontageAndWaitTask->OnCompleted.AddDynamic(this, &URPGGameplayAbility::OnCompleted);
InPlayMontageAndWaitTask->OnBlendOut.AddDynamic(this, &URPGGameplayAbility::OnBlendOut);
InPlayMontageAndWaitTask->OnCancelled.AddDynamic(this, &URPGGameplayAbility::OnCancelled);
InPlayMontageAndWaitTask->OnInterrupted.AddDynamic(this, &URPGGameplayAbility::OnInterrupted);
// 绑定Task执行完之后的委托
InPlayMontageAndWaitTask->DamageEventDelegate.AddDynamic(this, &URPGGameplayAbility::OnDamageGameplayEvent);
// 激活Task
InPlayMontageAndWaitTask->Activate();
return InPlayMontageAndWaitTask;
}
return nullptr;
}
在InPlayMontageAndWaitTask->Activate()
中使用AbilitySystemComponent->AddGameplayEventTagContainerDelegate()
执行任务。
void URPGAbilityTask_PMontageAndWait::Activate()
{
// 判断GA是否存在
if(Ability == nullptr)
{
return;
}
// 判断Component是否存在
if(AbilitySystemComponent != nullptr)
{
// 获取GA中的ActorInfo
const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo();
// 获取ActorInfo的动画实例
UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance();
// 判断动画实例是否为空
if (AnimInstance != nullptr)
{
// 告诉系统,添加委托,开始执行任务
EventHandle = AbilitySystemComponent->AddGameplayEventTagContainerDelegate(
EventTags,
FGameplayEventTagMulticastDelegate::FDelegate::CreateUObject(this, &URPGAbilityTask_PMontageAndWait::OnDamageGameplayEvent));
}
}
Super::Activate();
}
注意:
(1)在AbilitySystemComponent->AddGameplayEventTagContainerDelegate
使用TPair存储代理,并返回Handle
(2)在父类的Activate()
函数中使用PlayMontage播放动画
向GA的回调发生在绑定的OnDamageGameplayEvent()
函数中。
void URPGAbilityTask_PMontageAndWait::OnDamageGameplayEvent(FGameplayTag InGameplayTag, const FGameplayEventData* Payload)
{
// Task执行完了,需要回调GA,告诉GA继续往下执行
if(ShouldBroadcastAbilityTaskDelegates())
{
FGameplayEventData EventData = *Payload;
EventData.EventTag = InGameplayTag;
/** 广播代理 */
DamageEventDelegate.Broadcast(InGameplayTag, EventData);
}
}
回调完成后,GA继续执行ActiveSkill()
直至结束。
2.2 GA的命中
GA的命中,需要执行UAbilitySystemBlueprintLibrary::SendGameplayEventToActor()
这个函数,该命中事件的执行如下:
(1)判断系统组件是否存在。
(2)通过AbilitySystemComponent->HandleGameplayEvent(EventTag, &Payload)
,在内部对EventTag进行解析,寻找出直接的父类标签,并且去寻找之前放在TPair的代理,如果存在代理,将其广播,最终回调AddGameplayEventTagContainerDelegate()
中判定的代理函数,再回调给GA。
GA拿到回调的Payload
值,就能做后续的处理。
GA激活后完成对应的属性设置URPGAttributeSet
冷却时间和Mana的消耗在GE当中就能设置,其对应的AttributeSet
在GE的Modeifiers一栏中设置,因此AttributeSet
是绑定在GE中的。
URPGAttributeSet::URPGAttributeSet() : Health(100.f) // 设置属性默认值
{
}
void URPGAttributeSet::OnRep_Health(const FGameplayAttributeData& OldValue)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, Health, OldValue);
}
void URPGAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(URPGAttributeSet, Health);
}
void URPGAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
// 属性改变后做的事情,比如UI
}
将GE用TMap的形式存储在GA中,同时在Task回调之后,根据拿到的Payload
,进行GE的激活
// 存放GE,GE即GA攻击后造成的效果
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = GameplayEffects)
TMap<FGameplayTag, TSubclassOf<UGameplayEffect>> EffectMap;
void URPGGameplayAbility::OnDamageGameplayEvent(FGameplayTag InGameplayTag, FGameplayEventData Payload)
{
UE_LOG(LogTemp, Log, TEXT("URPGGameplayAbility::OnDamageGameplayEvent"));
/** 最后调用Attribute */
// 创建一个目标数组
FGameplayAbilityTargetData_ActorArray* NewTargetData_ActorArray = new FGameplayAbilityTargetData_ActorArray();
// 将命中中的目标添加进数组
NewTargetData_ActorArray->TargetActorArray.Add(const_cast<AActor*>(Payload.Target.Get()));
// 创建一个目标数据的handle
FGameplayAbilityTargetDataHandle TargetDataHandle;
// 将目标数据添加到TargetDataHandle中
TargetDataHandle.Add(NewTargetData_ActorArray);
for(auto& Tmp : EffectMap)
{
// 根据GEMap中的值,创建GEHandle
FGameplayEffectSpecHandle NewGEHandle = GetAbilitySystemComponentFromActorInfo()->MakeOutgoingSpec(
Tmp.Value, 1, MakeEffectContext(CurrentSpecHandle, CurrentActorInfo));
if(NewGEHandle.IsValid())
{
// 根据GAHandle来找到GA实例
FGameplayAbilitySpec* AbilitySpec = GetAbilitySystemComponentFromActorInfo()->FindAbilitySpecFromHandle(CurrentSpecHandle);
// 把GA的信息应用到GE上
ApplyAbilityTagsToGameplayEffectSpec(*NewGEHandle.Data.Get(), AbilitySpec);
if(AbilitySpec)
{
NewGEHandle.Data->SetByCallerTagMagnitudes = AbilitySpec->SetByCallerTagMagnitudes;
}
}
// 应用GE
TArray<FActiveGameplayEffectHandle> ActiveGameplayEffectHandles = K2_ApplyGameplayEffectSpecToTarget(NewGEHandle, TargetDataHandle);
}
}
2.3 GA的结束
Task继续混合动画,执行OnMontageBlendingOut()
函数,去广播给GA的绑定OnBlendOut()
。
void URPGGameplayAbility::OnBlendOut()
{
K2_OnBlendOut();
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, false);
}
在EndAbility()中标记自己要被销毁SetPaddingKill()
,同时调用Task的销毁。
void URPGAbilityTask_PMontageAndWait::OnDestroy(bool AbilityEnded)
{
if(AbilitySystemComponent != nullptr)
{
// 把自己的Tags移除
AbilitySystemComponent->RemoveGameplayEventTagContainerDelegate(EventTags, EventHandle);
}
Super::OnDestroy(AbilityEnded);
}
执行哪个绑定的EndAbility()
是在Flag里面标记了的,比如这里就是只执行OnBlendOut()
而不会执行OnCompleted()
,具体是在函数ShouldBroadcastAbilityTaskDelegates()
里面进行判断,如果GA销毁了那么就不会执行,因此中断或者完成,都通过之前绑定的四个函数进行绑定,去回调委托进行GA的条件回收。
注意:下一个GA的创建会使上一个GA直接销毁,因此需要传入对应的InGameplayTag
标记