[UEC++ learning] GAS system creation and execution process

1. Introduction of GAS

Open the plugin Gameplay Abilities
insert image description here

“GameplayAbilities”Add , "GameplayTags", modules to the .Build.cs file of the project "GameplayTasks".

insert image description here

Add the plugin module to the .uproject file

insert image description here

Create GA class

insert image description here
Create root component

insert image description here

insert image description here

2. GAS creation and execution process

The overall process of the entire GAS system from activation to hit processing and final recovery is as follows:

insert image description here

2.1 Activation of GA

To activate GA in Charater, use AbilitySystemComponent->TryActivateAbility(*Handle)the function

bool ARPGTestCharacter::ActiveSkill(FGameplayTag SkillName)
{
    
    
	if(AbilitySystemComponent)
	{
    
    
		// 传入FGameplayTag,在Map中寻找
		if(const FGameplayAbilitySpecHandle* Handle = skills.Find(FName(*SkillName.ToString())))
		{
    
    
			return AbilitySystemComponent->TryActivateAbility(*Handle);
		}
	}
	return false;
}

Then go to UAbilitySystemComponent::InternalTryActivateAbility(FGameplayAbilitySpecHandle Handle)the function and call it inside the function Ability->CallActivateAbility(Handle, ActorInfo, ActivationInfo, OnGameplayAbilityEndedDelegate, TriggerEventData), where Ability is 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); 
}

We used it in Charater TryActivateAbility(*Handle), rewritten the function in GA ActivateAbility, and finally transferred to GA.
Note: The information in the activation function we rewritten is all passed to us by the underlying GAS.
The function rewritten in GA ActivateAbilityneeds to play the montage, and playing the montage is the job of Task, so call the static method in GA to activate and add event binding.

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()Used in Execute AbilitySystemComponent->AddGameplayEventTagContainerDelegate()tasks.

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();
}

Note:
(1) AbilitySystemComponent->AddGameplayEventTagContainerDelegateUse TPair to store proxy and return Handle (2) Use PlayMontage to play animation in
parent class functionActivate()

Callbacks to GA happen in bound OnDamageGameplayEvent()functions.

void URPGAbilityTask_PMontageAndWait::OnDamageGameplayEvent(FGameplayTag InGameplayTag, const FGameplayEventData* Payload)
{
    
    
	// Task执行完了,需要回调GA,告诉GA继续往下执行
	if(ShouldBroadcastAbilityTaskDelegates())
	{
    
    
		FGameplayEventData EventData = *Payload;
		EventData.EventTag = InGameplayTag;
		/** 广播代理 */
		DamageEventDelegate.Broadcast(InGameplayTag, EventData);
	}
}

After the callback completes, the GA continues to execute ActiveSkill()until it ends.

2.2 GA hits

The GA hit needs to execute UAbilitySystemBlueprintLibrary::SendGameplayEventToActor()this function, and the execution of the hit event is as follows:
(1) Determine whether the system component exists.
(2) Pass AbilitySystemComponent->HandleGameplayEvent(EventTag, &Payload), analyze the EventTag internally, find out the direct parent tag, and look for the proxy placed in TPair before, if there is a proxy, broadcast it, and finally call back the proxy function determined in the callback, and then call back to GA AddGameplayEventTagContainerDelegate().
GA gets the callback Payloadvalue and can do subsequent processing.

After the GA is activated, the corresponding attribute setting of URPGAttributeSet
cooling time and Mana consumption can be set in GE, and the corresponding settings AttributeSetare set in the Modeifiers column of GE, so AttributeSetthey are bound in 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
}

PayloadStore GE in GA in the form of TMap, and at the same time after the Task callback, activate GE according to the obtained

// 存放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 End of GA

Task continues to mix animations, execute OnMontageBlendingOut()functions, and broadcast to GA bindings OnBlendOut().

void URPGGameplayAbility::OnBlendOut()
{
    
    
	K2_OnBlendOut();
	EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, false);
}

Mark yourself to be destroyed in EndAbility() SetPaddingKill(), and call Task's destruction at the same time.

void URPGAbilityTask_PMontageAndWait::OnDestroy(bool AbilityEnded)
{
    
    
	if(AbilitySystemComponent != nullptr)
	{
    
    
		// 把自己的Tags移除
		AbilitySystemComponent->RemoveGameplayEventTagContainerDelegate(EventTags, EventHandle);
	}
	Super::OnDestroy(AbilityEnded);
}

Which binding to execute EndAbility()is marked in the Flag. For example, here it is only executed OnBlendOut()but not executed OnCompleted(). Specifically, it is ShouldBroadcastAbilityTaskDelegates()judged in the function. If the GA is destroyed, it will not be executed. Therefore, the interruption or completion is bound by the four previously bound functions, and the callback delegate is used to perform the conditional recovery of the GA.
Note: The creation of the next GA will directly destroy the previous GA, so the corresponding InGameplayTagtag needs to be passed in

Guess you like

Origin blog.csdn.net/qq_45617648/article/details/131263789