GameplayTasks API – making things happen with GameplayTasks

2021/01 02 14:01

GameplayTasks are used to wrap up some gameplay functionality in a reusable object. All you have to do to use them is derive from the UGameplayTask base class and override some of the member functions that you prefer to implement.

Getting ready

If you have not done so already, complete Steps 1-4 of the GameplayAbilities API – triggering an actor’s gameplay abilities with game controls recipe to link to the GameplayTasks API in your ProjectName.Build.cs file and enable its functionality.

Afterwards, go in the UE4 Editor and navigate to Class Viewer by going to Window | Developer Tools | Class Viewer. Under Filters, uncheck the Actors Only and Placeable Only options.

Ensure that the GameplayTask object type exists:

How to do it…

  1. Click on File | Add C++ Class…. Choose to derive from GameplayTask. To do so, you must first tick Show All Classes, and then type gameplaytask into the filter box. Click on Next:
  1. Name your C++ class (something like GameplayTask_TaskName is the convention), then add the class to your project. The example spawns a particle emitter and is called GameplayTask_CreateParticles:
  1. Once your GameplayTask_CreateParticles.h and .cpp pair are created, navigate to the .h file and update the class to the following:
#pragma once

#include "CoreMinimal.h"
#include "GameplayTask.h"
#include "Particles/ParticleSystem.h"
#include "GameplayTask_CreateParticles.generated.h"

/**
*
*/
UCLASS()
class CHAPTER_11_API UGameplayTask_CreateParticles : public UGameplayTask
{
GENERATED_BODY()

public:
virtual void Activate();

// A static constructor for an instance of a
// UGameplayTask_CreateEmitter instance,

// including args of (what class of emitter, where to
// create it).

UFUNCTION(BlueprintCallable, Category = "GameplayTasks", meta = (AdvancedDisplay = "TaskOwner", DefaultToSelf = "TaskOwner", BlueprintInternalUseOnly = "TRUE"))
static UGameplayTask_CreateParticles* ConstructTask(
TScriptInterface<IGameplayTaskOwnerInterface> TaskOwner,
UParticleSystem* particleSystem,
FVector location);

UParticleSystem* ParticleSystem;
FVector Location;

};
  1. Then, implement the GameplayTask_CreateParticles.cpp file, as follows:
#include "GameplayTask_CreateParticles.h"
#include "Kismet/GameplayStatics.h"

// Like a constructor.
UGameplayTask_CreateParticles* UGameplayTask_CreateParticles::ConstructTask(
TScriptInterface<IGameplayTaskOwnerInterface> TaskOwner,
UParticleSystem* particleSystem,
FVector location)
{
UGameplayTask_CreateParticles* task =
NewTask<UGameplayTask_CreateParticles>(TaskOwner);

// Fill fields
if (task)
{
task->ParticleSystem = particleSystem;
task->Location = location;
}
return task;
}

void UGameplayTask_CreateParticles::Activate()
{
Super::Activate();

UGameplayStatics::SpawnEmitterAtLocation(GetWorld(),
ParticleSystem, Location);

}
  1. Open up your Warrior.h file and add the following interface to the class definition:
UCLASS()
class CHAPTER_11_API AWarrior : public ACharacter, public IAbilitySystemInterface, public IGameplayTaskOwnerInterface
{
GENERATED_BODY()
  1. Afterwards, add the following new properties to it:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats)
UGameplayTasksComponent* GameplayTasksComponent;

// This is the particleSystem that we create with the
// GameplayTask_CreateParticles object.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats)
UParticleSystem* particleSystem;
  1. Below that, add the following function definitions for the GameplayTaskOwnerInterface:
    // <GameplayTaskOwnerInterface>

virtual UGameplayTasksComponent* GetGameplayTasksComponent(const
UGameplayTask& Task) const { return GameplayTasksComponent; }

// This gets called both when task starts and when task gets
// resumed.
// Check Task.GetStatus() if you want to differentiate.
virtual void OnTaskActivated(UGameplayTask& Task) { }
virtual void OnTaskDeactivated(UGameplayTask& Task) { }

virtual AActor* GetOwnerActor(const UGameplayTask* Task) const {
return Task->GetOwnerActor(); // This will give us the
// accurate answer for the Task..
}
// </End GameplayTaskOwnerInterface>
  1. Afterwards, go to the Warrior.cpp file and update the class constructor to the following:
AWarrior::AWarrior()
{
// Set this character to call Tick() every frame. You can
// turn this off to improve performance if you don't need
// it.
PrimaryActorTick.bCanEverTick = true;
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>
("UAbilitySystemComponent");
GameplayTasksComponent = CreateDefaultSubobject<UGameplayTasksComponent>
("UGameplayTasksComponent");
}
  1. Save your scripts, return to the Unreal Editor, and compile your code. Once compiled, open your Actor class derivative (MyWarrior) and then scroll down to the Stats section and set the Particle System property to something you’d like to see, for instance, the P_Fire option if you included the sample content when you created your project:
  1. Which is available in the Full Blueprint Editor in the Components drop-down of the Components tab in the Blueprint editor. Add GameplayTasksComponent to
  2. Create and add an instance of your GameplayTask inside your Actor derivative (MyWarrior) instance using the following code:
    UGameplayTask_CreateParticles* task =
UGameplayTask_CreateParticles::ConstructTask(this,
particleSystem, FVector(200.f, 0.f, 200.f));


if (GameplayTasksComponent && task)
{
GameplayTasksComponent->AddTaskReadyForActivation(*task);
}

This code runs anywhere in your Actor class derivative, any time after GameplayTasksComponent is initialized (any time after PostInitializeComponents()).

  1. Compile your code. Set your level to use MyWarrior as the Default Pawn Type and when the game starts, you should notice that the particle plays:

How it works…

GameplayTasks simply register with the GameplayTasksComponent situated inside an Actor class derivative of your choice. You can activate any number of GameplayTasks at any time during gameplay to trigger their effects.

GameplayTasks can also kick off GameplayEffects to change attributes of AbilitySystemsComponents if you wish.

There’s more…

You can derive GameplayTasks for any number of events in your game. What’s more is that you can override a few more virtual functions to hook into additional functionality.