2023-06-29 11:55:02 +08:00

6.5 KiB
Raw Blame History

UAbilityTask

UAbilityTask继承自UGameplayTaskUGameplayTask可以用来写一些行为树中的一些节点可以用来实现一些异步功能。比如播放montage后各种事件的处理。

你可以去GameplayAbilities\Public\Abilities\Tasks\目录下寻找作者编写的task类作为相关参考也可以直接使用GameplayTasks目录下的案例比较少

当然我更加推荐学习actionRPG项目中的PlayMontageAndWaitForEvent原因有1、这个task用得最多2、涉及Task的代码相对较多。

其他推荐学习的UGameplayTask_WaitDelay、UAbilityTask_WaitGameplayEvent、UAbilityTask_WaitGameplayTagAdded、UAbilityTask_WaitGameplayEffectApplied

大致过程

  1. 声明多个动态多播委托用于处理各种事件。
  2. 重写所需的虚函数,并且声明相关变量。
  3. 编写主体函数。

代码分析

在PlayMontageAndWaitForEvent中重写了4个虚函数

//用于在各种委托设置完毕后开始执行真正的Tasks。
virtual void Activate() override;

//从外部取消这个Tasks默认情况下会结束任务。
virtual void ExternalCancel() override;

//返回debug字符串内容为当前播放的Montage名称以及Tasks存储的Montage名称
virtual FString GetDebugString() const override;

//结束并清理Tasks既可以在Tasks内部调用可以从该Tasks拥有者调用。
//注意请不要直接调用该函数你应该调用EndTask()或者TaskOwnerEnded()
//注意重写该函数时请确保最后调用Super::OnDestroy(bOwnerFinished)
virtual void OnDestroy(bool AbilityEnded) override;

Activate()

void URPGAbilityTask_PlayMontageAndWaitForEvent::Activate()
{
	if (Ability == nullptr)
	{
		return;
	}
    
	bool bPlayedMontage = false;
	URPGAbilitySystemComponent* RPGAbilitySystemComponent = GetTargetASC();

	if (RPGAbilitySystemComponent)
	{
		const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo();
		UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance();
		if (AnimInstance != nullptr)
		{
			//绑定事件回调函数
			EventHandle = RPGAbilitySystemComponent->AddGameplayEventTagContainerDelegate(EventTags, FGameplayEventTagMulticastDelegate::FDelegate::CreateUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnGameplayEvent));
            
            //播放montage
			if (RPGAbilitySystemComponent->PlayMontage(Ability, Ability->GetCurrentActivationInfo(), MontageToPlay, Rate, StartSection) > 0.f)
			{
                //播放Montage后其回调函数可能会导致Ability结束所以我们需要提前结束
				if (ShouldBroadcastAbilityTaskDelegates() == false)
				{
					return;
				}
                //绑定OnAbilityCancelled
				CancelledHandle = Ability->OnGameplayAbilityCancelled.AddUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnAbilityCancelled);
                //绑定OnMontageBlendingOut
				BlendingOutDelegate.BindUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnMontageBlendingOut);
				AnimInstance->Montage_SetBlendingOutDelegate(BlendingOutDelegate, MontageToPlay);
                //绑定OnMontageEnded
				MontageEndedDelegate.BindUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnMontageEnded);
				AnimInstance->Montage_SetEndDelegate(MontageEndedDelegate, MontageToPlay);

				ACharacter* Character = Cast<ACharacter>(GetAvatarActor());
				if (Character && (Character->Role == ROLE_Authority ||
								  (Character->Role == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted)))
				{
					Character->SetAnimRootMotionTranslationScale(AnimRootMotionTranslationScale);
				}

				bPlayedMontage = true;
			}
		}
		else
		{
			ABILITY_LOG(Warning, TEXT("URPGAbilityTask_PlayMontageAndWaitForEvent call to PlayMontage failed!"));
		}
	}
	else
	{
		ABILITY_LOG(Warning, TEXT("URPGAbilityTask_PlayMontageAndWaitForEvent called on invalid AbilitySystemComponent"));
	}
    //播放失败处理
	if (!bPlayedMontage)
	{
		ABILITY_LOG(Warning, TEXT("URPGAbilityTask_PlayMontageAndWaitForEvent called in Ability %s failed to play montage %s; Task Instance Name %s."), *Ability->GetName(), *GetNameSafe(MontageToPlay),*InstanceName.ToString());
		if (ShouldBroadcastAbilityTaskDelegates())
		{
			OnCancelled.Broadcast(FGameplayTag(), FGameplayEventData());
		}
	}

	SetWaitingOnAvatar();
}

ExternalCancel()

check(AbilitySystemComponent);
OnAbilityCancelled();
Super::ExternalCancel();

OnAbilityCancelled的代码

if (StopPlayingMontage())
{
	// Let the BP handle the interrupt as well
	if (ShouldBroadcastAbilityTaskDelegates())
	{
		OnCancelled.Broadcast(FGameplayTag(), FGameplayEventData());
	}
}

OnDestroy()

void URPGAbilityTask_PlayMontageAndWaitForEvent::OnDestroy(bool AbilityEnded)
{
	// Note: Clearing montage end delegate isn't necessary since its not a multicast and will be cleared when the next montage plays.
	// (If we are destroyed, it will detect this and not do anything)

	// This delegate, however, should be cleared as it is a multicast
	if (Ability)
	{
		Ability->OnGameplayAbilityCancelled.Remove(CancelledHandle);
		if (AbilityEnded && bStopWhenAbilityEnds)
		{
		    //停止播放Montage
			StopPlayingMontage();
		}
	}

	URPGAbilitySystemComponent* RPGAbilitySystemComponent = GetTargetASC();
	if (RPGAbilitySystemComponent)
	{
	    //移除事件绑定
		RPGAbilitySystemComponent->RemoveGameplayEventTagContainerDelegate(EventTags, EventHandle);
	}
    //这句必须放在最后
	Super::OnDestroy(AbilityEnded);
}

PlayMontageAndWaitForEvent

PlayMontageAndWaitForEvent是Tasks的主体函数。

URPGAbilityTask_PlayMontageAndWaitForEvent* URPGAbilityTask_PlayMontageAndWaitForEvent::PlayMontageAndWaitForEvent(UGameplayAbility* OwningAbility,
	FName TaskInstanceName, UAnimMontage* MontageToPlay, FGameplayTagContainer EventTags, float Rate, FName StartSection, bool bStopWhenAbilityEnds, float AnimRootMotionTranslationScale)
{
    //用于缩放GAS tasks变量的工具函数此为非shipping功能用于交互调试。
	UAbilitySystemGlobals::NonShipping_ApplyGlobalAbilityScaler_Rate(Rate);

    //使用NewAbilityTask来创建Tasks并且设置各个变量。
	URPGAbilityTask_PlayMontageAndWaitForEvent* MyObj = NewAbilityTask<URPGAbilityTask_PlayMontageAndWaitForEvent>(OwningAbility, TaskInstanceName);
	MyObj->MontageToPlay = MontageToPlay;
	MyObj->EventTags = EventTags;
	MyObj->Rate = Rate;
	MyObj->StartSection = StartSection;
	MyObj->AnimRootMotionTranslationScale = AnimRootMotionTranslationScale;
	MyObj->bStopWhenAbilityEnds = bStopWhenAbilityEnds;

	return MyObj;
}