## UAbilityTask UAbilityTask继承自UGameplayTask(UGameplayTask可以用来写一些行为树中的一些节点),可以用来实现一些异步功能。比如播放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(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(OwningAbility, TaskInstanceName); MyObj->MontageToPlay = MontageToPlay; MyObj->EventTags = EventTags; MyObj->Rate = Rate; MyObj->StartSection = StartSection; MyObj->AnimRootMotionTranslationScale = AnimRootMotionTranslationScale; MyObj->bStopWhenAbilityEnds = bStopWhenAbilityEnds; return MyObj; } ```