Skip to main content

Grant Gameplay Ability Or Effect

Type: Requires external gameplay system

This example uses a complete interactable actor, but the actual GAS grant policy still belongs to your project.

Example

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Data/EDInteractionTypes.h"
#include "MyAbilityConsoleActor.generated.h"

class UAbilitySystemComponent;
class UArrowComponent;
class UEDInteractableComponent;
class UGameplayAbility;
class UGameplayEffect;
class USceneComponent;
class UStaticMeshComponent;

UCLASS()
class AMyAbilityConsoleActor : public AActor
{
GENERATED_BODY()

public:
AMyAbilityConsoleActor();

protected:
virtual void BeginPlay() override;

UPROPERTY(VisibleAnywhere)
TObjectPtr<USceneComponent> Root;

UPROPERTY(VisibleAnywhere)
TObjectPtr<UStaticMeshComponent> ConsoleMesh;

UPROPERTY(VisibleAnywhere)
TObjectPtr<USceneComponent> WidgetHolder;

UPROPERTY(VisibleAnywhere)
TObjectPtr<UArrowComponent> CenterArrow;

UPROPERTY(VisibleAnywhere)
TObjectPtr<UEDInteractableComponent> InteractableComponent;

UPROPERTY(EditAnywhere, Category = "Ability")
TSubclassOf<UGameplayAbility> GrantedAbility;

UPROPERTY(EditAnywhere, Category = "Ability")
TSubclassOf<UGameplayEffect> InstantGameplayEffect;

UFUNCTION()
void HandleGrantAbility(const FEDInteractionEventContext& Context);
};
#include "MyAbilityConsoleActor.h"

#include "AbilitySystemComponent.h"
#include "Components/ArrowComponent.h"
#include "Components/EDInteractableComponent.h"
#include "Components/SceneComponent.h"
#include "Components/StaticMeshComponent.h"
#include "GameFramework/Pawn.h"

AMyAbilityConsoleActor::AMyAbilityConsoleActor()
{
Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
SetRootComponent(Root);

ConsoleMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ConsoleMesh"));
ConsoleMesh->SetupAttachment(Root);
ConsoleMesh->ComponentTags.Add(FName(TEXT("DimensionComponent")));

WidgetHolder = CreateDefaultSubobject<USceneComponent>(TEXT("WidgetHolder"));
WidgetHolder->SetupAttachment(Root);
WidgetHolder->SetRelativeLocation(FVector(0.f, 0.f, 80.f));
WidgetHolder->ComponentTags.Add(FName(TEXT("WidgetHolderComponent")));

CenterArrow = CreateDefaultSubobject<UArrowComponent>(TEXT("CenterArrow"));
CenterArrow->SetupAttachment(ConsoleMesh);
CenterArrow->ComponentTags.Add(FName(TEXT("CenterComponent")));

InteractableComponent = CreateDefaultSubobject<UEDInteractableComponent>(TEXT("InteractableComponent"));
}

void AMyAbilityConsoleActor::BeginPlay()
{
Super::BeginPlay();
InteractableComponent->OnInteractionSuccessful.AddDynamic(this, &AMyAbilityConsoleActor::HandleGrantAbility);
}

void AMyAbilityConsoleActor::HandleGrantAbility(const FEDInteractionEventContext& Context)
{
APawn* InstigatorPawn = Context.Instigator ? Context.Instigator->GetPawn() : nullptr;
if (!InstigatorPawn)
{
return;
}

UAbilitySystemComponent* ASC = InstigatorPawn->FindComponentByClass<UAbilitySystemComponent>();
if (!ASC)
{
return;
}

// Project-specific policy starts here. EDInteractionSystem does not manage GAS ownership rules.
if (GrantedAbility)
{
ASC->GiveAbility(FGameplayAbilitySpec(GrantedAbility, 1, INDEX_NONE, this));
}

if (InstantGameplayEffect)
{
FGameplayEffectContextHandle EffectContext = ASC->MakeEffectContext();
EffectContext.AddSourceObject(this);

const FGameplayEffectSpecHandle SpecHandle = ASC->MakeOutgoingSpec(InstantGameplayEffect, 1.f, EffectContext);
if (SpecHandle.IsValid())
{
ASC->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get());
}
}
}

External dependency note

  • GAS APIs are real Unreal APIs.
  • The choice of what to grant, when to grant it, and whether it should stack is still project logic.