美文网首页
七、RPGAbilityTask_PlayMontageAndW

七、RPGAbilityTask_PlayMontageAndW

作者: 珏_Gray | 来源:发表于2019-06-14 09:46 被阅读0次

AbilityTask

官方头文件注释:

/**
 *  AbilityTasks are small, self contained operations that can be performed while executing an ability.
 *  They are latent/asynchronous is nature. They will generally follow the pattern of 'start something and wait until it is finished or interrupted'
 *  
 *  We have code in K2Node_LatentAbilityCall to make using these in blueprints streamlined. The best way to become familiar with AbilityTasks is to 
 *  look at existing tasks like UAbilityTask_WaitOverlap (very simple) and UAbilityTask_WaitTargetData (much more complex).
 *  
 *  These are the basic requirements for using an ability task:
 *  
 *  1) Define dynamic multicast, BlueprintAssignable delegates in your AbilityTask. These are the OUTPUTs of your task. When these delegates fire,
 *  execution resumes in the calling blueprints.
 *  
 *  2) Your inputs are defined by a static factory function which will instantiate an instance of your task. The parameters of this function define
 *  the INPUTs into your task. All the factory function should do is instantiate your task and possibly set starting parameters. It should NOT invoke
 *  any of the callback delegates!
 *  
 *  3) Implement a Activate() function (defined here in base class). This function should actually start/execute your task logic. It is safe to invoke
 *  callback delegates here.
 *  
 *  
 *  This is all you need for basic AbilityTasks. 
 *  
 *  
 *  CheckList:
 *      -Override ::OnDestroy() and unregister any callbacks that the task registered. Call Super::EndTask too!
 *      -Implemented an Activate function which truly 'starts' the task. Do not 'start' the task in your static factory function!
 *  
 *  
 *  --------------------------------------
 *  
 *  We have additional support for AbilityTasks that want to spawn actors. Though this could be accomplished in an Activate() function, it would not be
 *  possible to pass in dynamic "ExposeOnSpawn" actor properties. This is a powerful feature of blueprints, in order to support this, you need to implement 
 *  a different step 3:
 *  
 *  Instead of an Activate() function, you should implement a BeginSpawningActor() and FinishSpawningActor() function.
 *  
 *  BeginSpawningActor() must take in a TSubclassOf<YourActorClassToSpawn> parameters named 'Class'. It must also have a out reference parameters of type 
 *  YourActorClassToSpawn*& named SpawnedActor. This function is allowed to decide whether it wants to spawn the actor or not (useful if wishing to
 *  predicate actor spawning on network authority).
 *  
 *  BeginSpawningActor() can instantiate an actor with SpawnActorDefferred. This is important, otherwise the UCS will run before spawn parameters are set.
 *  BeginSpawningActor() should also set the SpawnedActor parameter to the actor it spawned.
 *  
 *  [Next, the generated byte code will set the expose on spawn parameters to whatever the user has set]
 *  
 *  If you spawned something, FinishSpawningActor() will be called and pass in the same actor that was just spawned. You MUST call ExecuteConstruction + PostActorConstruction
 *  on this actor!
 *  
 *  This is a lot of steps but in general, AbilityTask_SpawnActor() gives a clear, minimal example.
 *  
 *  
 */

上面文字的大意:

AbilityTask是小而完备的行为,通常在Ability执行过程中运行。
异步是其固有特征。实现的功能一般按照“开始做些事,直到结束或被中断”的模式。
官方推荐了学习两个已有的AbilityTask:

  • UAbilityTask_WaitOverlap (very simple)
  • UAbilityTask_WaitTargetData ( much more complex)

为了使用AbilityTask,你必须满足三个基本要求

  1. 在AbilityTask中定义dynamic multicast , BlueprintAssignable类型的delegate,这些委托就是task的输出。当这些委托触发时,我们会回到调用AbilityTask的蓝图中(通常是Ability)。
  2. 你需要定义一个static的工厂函数来实例化AbilityTask。该工厂函数的输入参数表,就是你对ability task的输入。在这个函数中,你应该创建你的task实例并设置你的成员变量初值。不应该在此调用任何委托。
  3. 实现Activate()函数。从这个函数开始你的task。你可以在这里安全地触发委托。

你还有一个check list要检查:

  • Override :: OnDestroy() 来注销你之前在task注册的callback。记得在合适的地方调用EndTask来结束任务。
  • Implemented an Activate function which truly 'starts' the task. Do not 'start' the task in your static factory function!

还有一部分是说在task中spawn actor,这里就不说明了。引擎提供了AbilityTask_SpawnActor()供参考。

关于AbilityTask其他介绍,也可以参考之前写的:
AbilityTask


RPGAbilityTask_PlayMontageAndWaitForEvent.h/cpp

头文件:

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "ActionRPG.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "RPGAbilityTask_PlayMontageAndWaitForEvent.generated.h"

class URPGAbilitySystemComponent;

/** Delegate type used, EventTag and Payload may be empty if it came from the montage callbacks */
/**  我们先声明要使用的Delegate类型,还记得要求1)么? Dynamic multicast !!!
 你可以声明各种不同参数表的delegate*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRPGPlayMontageAndWaitForEventDelegate, FGameplayTag, EventTag, FGameplayEventData, EventData);

/**
 * This task combines PlayMontageAndWait and WaitForEvent into one task, so you can wait for multiple types of activations such as from a melee combo
 * Much of this code is copied from one of those two ability tasks
 * This is a good task to look at as an example when creating game-specific tasks
 * It is expected that each game will have a set of game-specific tasks to do what they want
 */
UCLASS()
class ACTIONRPG_API URPGAbilityTask_PlayMontageAndWaitForEvent : public UAbilityTask
{
    GENERATED_BODY()

public:
    // Constructor and overrides
    URPGAbilityTask_PlayMontageAndWaitForEvent(const FObjectInitializer& ObjectInitializer);

// 要求3),重写Activate()
    virtual void Activate() override;

    virtual void ExternalCancel() override;
    virtual FString GetDebugString() const override;

//Check List , 重写OnDestroy来注销callback
    virtual void OnDestroy(bool AbilityEnded) override;

// 要求1) BlueprintAssignable的delegate提供蓝图支持
// -------------------------------------------------------------------------
    /** The montage completely finished playing */
    UPROPERTY(BlueprintAssignable)
    FRPGPlayMontageAndWaitForEventDelegate OnCompleted;

    /** The montage started blending out */
    UPROPERTY(BlueprintAssignable)
    FRPGPlayMontageAndWaitForEventDelegate OnBlendOut;

    /** The montage was interrupted */
    UPROPERTY(BlueprintAssignable)
    FRPGPlayMontageAndWaitForEventDelegate OnInterrupted;

    /** The ability task was explicitly cancelled by another ability */
    UPROPERTY(BlueprintAssignable)
    FRPGPlayMontageAndWaitForEventDelegate OnCancelled;

    /** One of the triggering gameplay events happened */
    UPROPERTY(BlueprintAssignable)
    FRPGPlayMontageAndWaitForEventDelegate EventReceived;
// -------------------------------------------------------------------------

    /**
     * Play a montage and wait for it end. If a gameplay event happens that matches EventTags (or EventTags is empty), the EventReceived delegate will fire with a tag and event data.
     * If StopWhenAbilityEnds is true, this montage will be aborted if the ability ends normally. It is always stopped when the ability is explicitly cancelled.
     * On normal execution, OnBlendOut is called when the montage is blending out, and OnCompleted when it is completely done playing
     * OnInterrupted is called if another montage overwrites this, and OnCancelled is called if the ability or task is cancelled
     *
     * @param TaskInstanceName Set to override the name of this task, for later querying
     * @param MontageToPlay The montage to play on the character
     * @param EventTags Any gameplay events matching this tag will activate the EventReceived callback. If empty, all events will trigger callback
     * @param Rate Change to play the montage faster or slower
     * @param bStopWhenAbilityEnds If true, this montage will be aborted if the ability ends normally. It is always stopped when the ability is explicitly cancelled
     * @param AnimRootMotionTranslationScale Change to modify size of root motion or set to 0 to block it entirely
   要求1) 静态工厂函数 , 前两个参数必须是UGameplayAbility* , FName
     */
    UFUNCTION(BlueprintCallable, Category="Ability|Tasks", meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE"))
    static URPGAbilityTask_PlayMontageAndWaitForEvent* PlayMontageAndWaitForEvent(
        UGameplayAbility* OwningAbility,
        FName TaskInstanceName,
        UAnimMontage* MontageToPlay,
        FGameplayTagContainer EventTags,
        float Rate = 1.f,
        FName StartSection = NAME_None,
        bool bStopWhenAbilityEnds = true,
        float AnimRootMotionTranslationScale = 1.f);

private:
    /** Montage that is playing */
    UPROPERTY()
    UAnimMontage* MontageToPlay;

    /** List of tags to match against gameplay events */
    UPROPERTY()
    FGameplayTagContainer EventTags;

    /** Playback rate */
    UPROPERTY()
    float Rate;

    /** Section to start montage from */
    UPROPERTY()
    FName StartSection;

    /** Modifies how root motion movement to apply */
    UPROPERTY()
    float AnimRootMotionTranslationScale;

    /** Rather montage should be aborted if ability ends */
    UPROPERTY()
    bool bStopWhenAbilityEnds;

    /** Checks if the ability is playing a montage and stops that montage, returns true if a montage was stopped, false if not. */
    bool StopPlayingMontage();

    /** Returns our ability system component */
    URPGAbilitySystemComponent* GetTargetASC();

    void OnMontageBlendingOut(UAnimMontage* Montage, bool bInterrupted);
    void OnAbilityCancelled();
    void OnMontageEnded(UAnimMontage* Montage, bool bInterrupted);
    void OnGameplayEvent(FGameplayTag EventTag, const FGameplayEventData* Payload);

// 用于绑定到montage的委托
    FOnMontageBlendingOutStarted BlendingOutDelegate;
    FOnMontageEnded MontageEndedDelegate;

// Delegate Handle用来清理对外部注册的delegate
    FDelegateHandle CancelledHandle;
    FDelegateHandle EventHandle;
};

源文件我们主要看这些函数:

  • OnMontageEnded : 触发OnCompleted蓝图输出引脚
  • PlayMontageAndWaitForEvent:静态工厂函数
  • Activate:执行task
  • OnDestroy: 清理注册到外部的delegate
void URPGAbilityTask_PlayMontageAndWaitForEvent::OnGameplayEvent(FGameplayTag EventTag, const FGameplayEventData* Payload)
{
// 在broad cast 委托之前,都要调用 ShouldBroadcastAbilityTaskDelegates检测Ability是否有效
    if (ShouldBroadcastAbilityTaskDelegates())
    {
        FGameplayEventData TempData = *Payload;
        TempData.EventTag = EventTag;
         
// Fire delegate
        EventReceived.Broadcast(EventTag, TempData);
    }
}

静态工厂函数:

URPGAbilityTask_PlayMontageAndWaitForEvent* URPGAbilityTask_PlayMontageAndWaitForEvent::PlayMontageAndWaitForEvent(UGameplayAbility* OwningAbility,
    FName TaskInstanceName, UAnimMontage* MontageToPlay, FGameplayTagContainer EventTags, float Rate, FName StartSection, bool bStopWhenAbilityEnds, float AnimRootMotionTranslationScale)
{
// 在开发阶段使用,控制montage的播放速度
    UAbilitySystemGlobals::NonShipping_ApplyGlobalAbilityScaler_Rate(Rate);

//使用NewAbilityTask来创建ability task实例
    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;
}

任务本体Activate(),在这里绑定各种delegate:

void URPGAbilityTask_PlayMontageAndWaitForEvent::Activate()
{
    if (Ability == nullptr)
    {
        return;
    }

    bool bPlayedMontage = false;
// GetTargetASC的功能只是Cast
    URPGAbilitySystemComponent* RPGAbilitySystemComponent = GetTargetASC();

    if (RPGAbilitySystemComponent)
    {
        const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo();
//获得actor上的skeletal mesh comp的动画实例
        UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance();
        if (AnimInstance != nullptr)
        {
            // Bind to event callback
            EventHandle = RPGAbilitySystemComponent->AddGameplayEventTagContainerDelegate(EventTags, FGameplayEventTagMulticastDelegate::FDelegate::CreateUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnGameplayEvent));

// Play montage
            if (RPGAbilitySystemComponent->PlayMontage(Ability, Ability->GetCurrentActivationInfo(), MontageToPlay, Rate, StartSection) > 0.f)
            {
                // Playing a montage could potentially fire off a callback into game code which could kill this ability! Early out if we are  pending kill.
                if (ShouldBroadcastAbilityTaskDelegates() == false)
                {
                    return;
                }

                CancelledHandle = Ability->OnGameplayAbilityCancelled.AddUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnAbilityCancelled);

// Set  montage delegates
//----------------------------------------------------------------------------------------------------------
                BlendingOutDelegate.BindUObject(this, &URPGAbilityTask_PlayMontageAndWaitForEvent::OnMontageBlendingOut);
                AnimInstance->Montage_SetBlendingOutDelegate(BlendingOutDelegate, MontageToPlay);

                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());
        }
    }
/** Called when the ability task is waiting on ACharacter type of state (movement state, etc). IF the remote player ends the ability prematurely, and a task with this set is still running, the ability is killed. */
// 我们需要明确等待什么东西:
// Task is waiting for the game to do something 
//  WaitingOnGame = 0x01,

    // Waiting for the user to do something 
//  WaitingOnUser = 0x02,

    // Waiting on Avatar (Character/Pawn/Actor) to do something (usually something physical in the world, like land, move, etc) 
//  WaitingOnAvatar = 0x04
*/
    SetWaitingOnAvatar();
}

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)
 // montage的delegate不是multicast的,所以会自动清除

//但是,multicast的delegate要手动清除:
    // This delegate, however, should be cleared as it is a multicast
    if (Ability)
    {
        Ability->OnGameplayAbilityCancelled.Remove(CancelledHandle);
        if (AbilityEnded && bStopWhenAbilityEnds)
        {
            StopPlayingMontage();
        }
    }

    URPGAbilitySystemComponent* RPGAbilitySystemComponent = GetTargetASC();
    if (RPGAbilitySystemComponent)
    {
        RPGAbilitySystemComponent->RemoveGameplayEventTagContainerDelegate(EventTags, EventHandle);
    }

    Super::OnDestroy(AbilityEnded);

}

相关文章

  • 七、RPGAbilityTask_PlayMontageAndW

    AbilityTask 官方头文件注释: 上面文字的大意: AbilityTask是小而完备的行为,通常在Abil...

  • 听书:胡雪岩

    七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七夕七...

  • 人生七态

    人生:七难、七气、七别、七养、七心、七耐、七笑、七然、七稳 人生七难 1、相识容易,相知难 2、从恶容易,为善难 ...

  • 人生:七难、七气、七别、七养、七心、七耐、七笑、七然、七稳

    人生:七难、七气、七别、七养、七心、七耐、七笑、七然、七稳 人生七难 1、相识容易,相知难 2、从恶容易,为善难 ...

  • 七•七,七•七!

    前几天党庆百年,全国各地的政治团体和祖国人民花样翻新的歌唱伟大政党。 今天是“七•七”,却鲜见各级政府和人民来说些...

  • 七月七

    七月七,系七夕。情人节,女儿会。 七月七,鹊桥兮。相约双,牵手对。 七月七,别依依。天涯来,海角去。 七月七,空唧...

  • 初七用典

    新年快乐,“七”历史 水淹七军 七擒七纵 七步之才 竹林七贤 七七事变 “七子均养者,鳲鳩之仁也。”“七”典故: ...

  • 前七日

    ——《创世纪》 七天七座里程碑 七天七篇日记 岁月的河流啊!狡黠地狂奔 七天七次收获 七天七个奇迹 七天之后人类的...

  • 学佛闻思:农历七月真的是鬼月吗?佛弟子能做什么?

    七月是吉祥月 “七”,在中国的数字当中,是一个奇数:七巧、七星、七彩、七律;基督教也有“七天创造宇宙”之说,甚至七...

  • 天上七夕 人间七惜

    天上有七夕,人间有七惜。 七夕 “七惜” 余生, 好好珍惜这七种人, 七夕,是浪漫, 七惜,是真心。 一惜:生...

网友评论

      本文标题:七、RPGAbilityTask_PlayMontageAndW

      本文链接:https://www.haomeiwen.com/subject/imtufctx.html