【UEC++学习】GAS系统创建与执行流程

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标记

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值