Today is the last chapter of this tutorial. As mentioned in the previous chapter, the operation of Ability is actually completed within a frame without Task, which is definitely not able to meet the actual situation. Therefore, we need Ability Task to enable Ability to handle different actions in a longer time range.

The functions of PlayMontageAndWait, WaitTargetData, Wait, WaitGameplayEvent that we continuously used before are all Ability Task. In this case, we will also want to implement our own skill tasks, and this tutorial will show you how to implement a simple Ability Task.

Create the Ability of Task

AbilityTask inherits from GameplayTask, which inherits from Uobject, unreal iv’s parent of all things.

Here I’m going to implement a feature that waits for a property to change. So it’s called AT_AttributeChange.

Open the generated class and let’s go to its base class AbilityTask without worrying about the implementation code. EPIC’s official documentation Outlines the requirements for implementing a skill mission, so I’ll give you a brief overview:

  1. Define a multicast delegate, which must be BlueprintAssignable. The delegate will serve as the output of the task. When the delegate is triggered, it will be executed in the blueprint. The output here, which I’ve circled in red in the image below, is actually a delegate, and the transmitted value is just below.
  2. Task input is via a static Factory function, which creates an instance of the current Task. The input is defined in this function. As I circled in red below, my task input is bool, ability System Component and attribute. Try not to bind a delegate or callback in this function.
  3. The last part is to override the Activate() function to actually execute the task logic here, rather than inside the static Factory function.
  4. One last note is to override the OnDestroy() function to do the destruction.
/** * 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! * /
Copy the code

Realize the Ability of Task

With a general understanding of the implementation requirements of AT, let’s start the specific operation process.

Put the whole code in first, and then I’ll walk you through the process.

#pragma once

#include "CoreMinimal.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "AT_AttributeChange.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAttributeChanged, FGameplayAttribute, Attribute, float, NewValue, float, OldValue);

/**
 * 
 */
UCLASS(a)class GAS_LEARN_API UAT_AttributeChange : public UAbilityTask
{
	GENERATED_BODY(a)public:
	UAT_AttributeChange(const FObjectInitializer& ObjectInitializer);
	
	virtual void Activate(a) override;
	
	UPROPERTY(BlueprintAssignable)
	FAttributeChanged AttributeChanged;

	UFUNCTION(BlueprintCallable, Category="Ability|Tasks", meta=(HidePin="OwningAbility", DefaultToSelf="OwningAbility", BlueprintInternalUseOnly="TRUE"))
	static UAT_AttributeChange* ListenForAttributeChange(
		UGameplayAbility* OwningAbility,
		bool TriggerOnce,
		UAbilitySystemComponent* SystemComponent,
		FGameplayAttribute Attribute);

protected:
	bool TriggerOnce;

	FGameplayAttribute Attribute;

	void OnAttributeChanged(const FOnAttributeChangeData& Data);

	virtual void OnDestroy(bool bInOwnerFinished) override;
	
};
Copy the code
// Fill out your copyright notice in the Description page of Project Settings.

#include "AT_AttributeChange.h"

#include "AbilitySystemComponent.h"

UAT_AttributeChange::UAT_AttributeChange(const FObjectInitializer& ObjectInitializer)
	:Super(ObjectInitializer)
{
	TriggerOnce = false;
}

void UAT_AttributeChange::Activate(a)
{
	if(IsValid(AbilitySystemComponent) && Attribute.IsValid())
	{
		AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute).AddUObject(this, &UAT_AttributeChange::OnAttributeChanged);
	}
	
	Super::Activate(a); }UAT_AttributeChange* UAT_AttributeChange::ListenForAttributeChange(
	UGameplayAbility* OwningAbility,
	bool TriggerOnce,
	UAbilitySystemComponent* SystemComponent,
	FGameplayAttribute Attribute)
{
	UAT_AttributeChange* MyObj = NewAbilityTask<UAT_AttributeChange>(OwningAbility);
	MyObj->TriggerOnce = TriggerOnce;
	MyObj->AbilitySystemComponent = SystemComponent;
	MyObj->Attribute = Attribute;

	return MyObj;
}

void UAT_AttributeChange::OnAttributeChanged(const FOnAttributeChangeData& Data)
{
	AttributeChanged.Broadcast(Data.Attribute, Data.NewValue, Data.OldValue);

	if(TriggerOnce)
	{
		EndTask();
	}
}

void UAT_AttributeChange::OnDestroy(bool bInOwnerFinished)
{
	if(AbilitySystemComponent)
	{
		AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute).RemoveAll(this);
	}
	Super::OnDestroy(bInOwnerFinished);
}
Copy the code

Create the delegate

We’ll start by defining a delegate, because we’re accepting changing properties, so we need three variables, properties, new values and old values. This delegate is then created in the class, and the delegate created here will be the output of the task.

DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAttributeChanged, FGameplayAttribute, Attribute, float, NewValue, float, OldValue);

UCLASS(a)class GAS_LEARN_API UAT_AttributeChange : public UAbilityTask
{
public:
	UPROPERTY(BlueprintAssignable)
	FAttributeChanged AttributeChanged;
}
Copy the code

Create Static Factory Function

The function here is to create an instance of Ability Task, and the function name here is the name we actually use to create the Task. The variable of the function is the input to the task. The problem is that Ability Task doesn’t actually require me to manually assign AbilitySystemComponent, so this variable can be removed.

UFUNCTION(BlueprintCallable, Category="Ability|Tasks", meta=(HidePin="OwningAbility", DefaultToSelf="OwningAbility", BlueprintInternalUseOnly="TRUE"))
	static UAT_AttributeChange* ListenForAttributeChange(
		UGameplayAbility* OwningAbility,
		bool TriggerOnce,
		UAbilitySystemComponent* SystemComponent,
		FGameplayAttribute Attribute);

protected:
	bool TriggerOnce;
	FGameplayAttribute Attribute;
Copy the code

The function implementation, which actually assigns the input to a variable defined in protected, is not responsible for the logic.

UAT_AttributeChange* UAT_AttributeChange::ListenForAttributeChange(
	UGameplayAbility* OwningAbility,
	bool TriggerOnce,
	UAbilitySystemComponent* SystemComponent,
	FGameplayAttribute Attribute)
{
	UAT_AttributeChange* MyObj = NewAbilityTask<UAT_AttributeChange>(OwningAbility);
	MyObj->TriggerOnce = TriggerOnce;
	//MyObj->AbilitySystemComponent = SystemComponent;
	MyObj->Attribute = Attribute;

	return MyObj;
}
Copy the code

Implement Ability Task logic

First override the function Activate() to bind the function created below to the property change delegate in the ASC. The actual approach is the same as the one we used to update the UI in real time.

virtual void Activate(a) override;

void OnAttributeChanged(const FOnAttributeChangeData& Data);
Copy the code

The logic of OnAttributeChanged is simple, broadcasting the changed property through a previously created delegate. EndTask() is called if it is set to fire only once

void UAT_AttributeChange::Activate(a)
{
	if(IsValid(AbilitySystemComponent) && Attribute.IsValid())
	{
		AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute).AddUObject(this, &UAT_AttributeChange::OnAttributeChanged);
	}
	
	Super::Activate(a); }void UAT_AttributeChange::OnAttributeChanged(const FOnAttributeChangeData& Data)
{
	AttributeChanged.Broadcast(Data.Attribute, Data.NewValue, Data.OldValue);

	if(TriggerOnce)
	{
		EndTask();
	}
}
Copy the code

Rewrite the OnDestroy

This section overrides the Task destruction method by first removing all bound UObjects and then calling the Destroy() function of the parent class

virtual void OnDestroy(bool bInOwnerFinished) override;

void UAT_AttributeChange::OnDestroy(bool bInOwnerFinished)
{
	if(AbilitySystemComponent)
	{
		AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute).RemoveAll(this);
	}
	Super::OnDestroy(bInOwnerFinished);
}
Copy the code

test

Create a Ability to test with the blueprint below.

Then write a blood GE and test it. You can see that Ability received and printed out the data. This logic can be used to determine damage skills, such as playing an execution animation when health drops below 10%.

conclusion

This is the end of the GAS primer series.

First of all, let’s talk about why I wrote this article. During the learning process of Unreal IV, I watched a lot of tutorial videos and articles, including YouTube videos, github projects and documents, as well as many videos reposted by the host of B website Up. These video and articles on many hits but thousands, but in fact help to me, I have no way to return their economic benefits directly, but I hope this kind of resource sharing, the atmosphere of helping others and action can continue, let the other people in need also can get help, also be to give back to my help.

So I wrote this article series has no interests pursuit, the only hope is the increase of the hits, and others to comment on my encouragement, the sense of achievement can satisfy my little vanity, help me to write a tutorial this demanding behavior continues, because there is no feedback, this can’t see the returns and very demanding things really is quite difficult to stick to it, This makes me even more admire those who have been serializing tutorial articles and videos for a long time.

In many cases, I have to admit that the atmosphere of knowledge sharing on the Chinese Internet is not as good as the English Internet environment. In the process of writing, I really didn’t think I’m a click of a few dozen article will be stolen by others to be original, also saw the article is reproduced many repeat meaningless let people even do not get back, and even some I have seen the Chinese tutorial actually is the direct translation of the English tutorial on the Udemy, almost no change, They didn’t even change the material.

But at the same time, there are many people doing this thankless work, some people are selfless translation of English documents, some people are writing a variety of different tutorials, I hope I can also contribute to such an environment.

It is spring recruitment, I may go to the simulation of intelligent car in the future, may go to the game company, but I hope I can stick to the behavior of writing tutorial, can help people in need, Thanks to (· ω ·) Blue.

GAS Introductory tutorial collection