Getting the bound key from inside a Gas ability

Hello, I’m struggling to retrieve the bound key inside my interact Gas ability. My project is a fork of Lyra for context. I want to display an indicator above the interactable actor that display the key to press to interact.

Just a tip from us. If you would like us to assist you, I suggest making an effort to provide some screenshots—specifically, your “Gas” system. As of now, we do not know how to help you.

Sure see my full code for ref but what I want to achieve is in this:

UEdenIndicatorDescriptor* Indicator = NewObject<UEdenIndicatorDescriptor>();
UEdenInteractionsIndicatorData* IndicatorData = NewObject<UEdenInteractionsIndicatorData>();
IndicatorData->Options = InteractiveOptions;

I want to retrieve somehow the input action that triggered the ability to pas it to IndicatorData.InputAction = ActionBoundToThisAbility

To be able to display the bound key in my indicator widget.

Since the ability is triggered by a gameplay tag I’m first trying to get the tag that triggered it but without success. TriggerEventData is null.

Full code for reference
// Fill out your copyright notice in the Description page of Project Settings.


#include "Abilities/EdenInteractionsGameplayAbility_Interact.h"


#include "AbilitySystemComponent.h"
#include "EdenGameplayAbilitySystemComponent.h"
#include "EdenInteractionsLog.h"
#include "EdenInteractionsTraceProfiles.h"
#include "EnhancedInputSubsystems.h"
#include "NativeGameplayTags.h"
#include "Abilities/EdenInteractionsAbilityTargetData.h"
#include "Blueprint/UserWidget.h"
#include "Character/EdenPawnExtensionComponent.h"
#include "EdenIndicators/Public/EdenIndicatorDescriptor.h"
#include "EdenIndicators/Public/EdenIndicatorManagerComponent.h"
#include "EdenInteractions/Public/EdenInteractionsComponent.h"
#include "EdenInteractions/Public/EdenInteractionsStatics.h"
#include "EdenInteractions/Public/EdenInteractionsTarget.h"
#include "EdenInteractions/Public/Tasks/EdenInteractionsAbilityTask_GrantNearbyInteraction.h"
#include "EdenInteractions/Public/Tasks/EdenInteractionsAbilityTask_WaitForInteractableTargets_SingleLineTrace.h"
#include "GameplayAbilities/Public/Abilities/Tasks/AbilityTask_WaitInputPress.h"
#include "GameplayAbilities/Public/Abilities/Tasks/AbilityTask_WaitGameplayEvent.h"
#include "HUD/EdenInteractionsIndicatorData.h"
#include "Input/EdenInputComponent.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(EdenInteractionsGameplayAbility_Interact)

class UUserWidget;

UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_Ability_Interaction_Activate, "Ability.Interaction.Activate");
UE_DEFINE_GAMEPLAY_TAG(TAG_INTERACTION_DURATION_MESSAGE, "Ability.Interaction.Duration.Message");

UEdenInteractionsGameplayAbility_Interact::UEdenInteractionsGameplayAbility_Interact(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
    ActivationPolicy = EEdenGameplayAbilityActivationPolicy::OnSpawn;
    InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
    NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
}

void UEdenInteractionsGameplayAbility_Interact::ActivateAbility(const FGameplayAbilitySpecHandle Handle,
    const FGameplayAbilityActorInfo* ActorInfo,
    const FGameplayAbilityActivationInfo ActivationInfo,
    const FGameplayEventData* TriggerEventData)
{
    Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);

    UAbilitySystemComponent* AbilitySystem = GetAbilitySystemComponentFromActorInfo();
    if (AbilitySystem && AbilitySystem->GetOwnerRole() == ROLE_Authority)
    {
       UEdenInteractionsAbilityTask_GrantNearbyInteraction* Task = UEdenInteractionsAbilityTask_GrantNearbyInteraction::GrantAbilitiesForNearbyInteractors(this,
          InteractionScanRange, InteractionScanRate);
       Task->ReadyForActivation();
    }

    LookForInteractable();
    WaitForKeyPress();
}

void UEdenInteractionsGameplayAbility_Interact::UpdateInteractions(const TArray<FEdenInteractionsOption>& InteractiveOptions)
{
    /*UE_LOG(LogTemp, Display, TEXT("UEdenInteractionsGameplayAbility_Interact::UpdateInteractions: Update Interactions:"));
    if (InteractiveOptions.Num() > 0)
    {
       for (const FEdenInteractionsOption& Option : InteractiveOptions)
       {
          UE_LOG(LogTemp, Display, TEXT("Option.Text = %s"), *Option.Text.ToString());
       }
    }*/

    AActor* Actor = GetAvatarActorFromActorInfo();
    if (UEdenInteractionsComponent* IC = UEdenInteractionsComponent::FindComponent(Actor))
    {
       IC->OnOptionsChanged.Broadcast(Actor, InteractiveOptions);
    }

    // Indicator management:
    if (APlayerController* PC = Cast<APlayerController>(CurrentActorInfo->PlayerController.Get()))
    {
       if (UEdenIndicatorManagerComponent* IndicatorManager = UEdenIndicatorManagerComponent::GetComponent(PC))
       {
          for (UEdenIndicatorDescriptor* Indicator : Indicators)
          {
             IndicatorManager->RemoveIndicator(Indicator);
          }
          Indicators.Reset();

          if (InteractiveOptions.Num() > 0)
          {
             EDEN_LOG_INFO("Adding indicator");
             
             //InteractionOption.InputAction = GetCurrentAbilitySpec().
             AActor* InteractableTargetActor = UEdenInteractionsStatics::GetActorFromInteractableTarget(
                InteractiveOptions[0].InteractableTarget);
             
             UEdenIndicatorDescriptor* Indicator = NewObject<UEdenIndicatorDescriptor>();
             UEdenInteractionsIndicatorData* IndicatorData = NewObject<UEdenInteractionsIndicatorData>();
             IndicatorData->Options = InteractiveOptions;
             Indicator->SetDataObject(IndicatorData);
             Indicator->SetSceneComponent(InteractableTargetActor->GetRootComponent());
             Indicator->SetIndicatorClass(DefaultInteractionWidgetClass);
             IndicatorManager->AddIndicator(Indicator);
             Indicators.Add(Indicator);
          }
       }
       else
       {
          EDEN_LOG_WARN("No UEdenIndicatorManagerComponent found on PC %s", *PC->GetName());
          //TODO This should probably be a noisy warning.  Why are we updating interactions on a PC that can never do anything with them?
       }
    }
    else
    {
       EDEN_LOG_WARN("No PlayerController found on Actor %s", *Actor->GetName());
    }

    CurrentOptions = InteractiveOptions;
}

void UEdenInteractionsGameplayAbility_Interact::TriggerInteraction()
{
    if (CurrentOptions.Num() == 0)
    {
       return;
    }

    UAbilitySystemComponent* AbilitySystem = GetAbilitySystemComponentFromActorInfo();
    if (AbilitySystem)
    {
       const FEdenInteractionsOption& InteractionOption = CurrentOptions[0];

       AActor* Instigator = GetAvatarActorFromActorInfo();
       AActor* InteractableTargetActor = UEdenInteractionsStatics::GetActorFromInteractableTarget(
          InteractionOption.InteractableTarget);

       // Allow the target to customize the event data we're about to pass in, in case the ability needs custom data
       // that only the actor knows.
       FGameplayEventData Payload;
       Payload.EventTag = TAG_Ability_Interaction_Activate;
       Payload.Instigator = Instigator;
       Payload.Target = InteractableTargetActor;
       Payload.OptionalObject = InteractionOption.OptionalObject;

       FEdenInteractionsAbilityTargetData* TargetData = new FEdenInteractionsAbilityTargetData();
       TargetData->InteractionOption = InteractionOption;
       Payload.TargetData.Add((FGameplayAbilityTargetData*)(TargetData));

       // If needed we allow the interactable target to manipulate the event data so that for example, a button on the wall
       // may want to specify a door actor to execute the ability on, so it might choose to override Target to be the
       // door actor.
       InteractionOption.InteractableTarget->CustomizeInteractionEventData(TAG_Ability_Interaction_Activate, Payload);

       // Grab the target actor off the payload we're going to use it as the 'avatar' for the interaction, and the
       // source InteractableTarget actor as the owner actor.
       AActor* TargetActor = const_cast<AActor*>(ToRawPtr(Payload.Target));

       // The actor info needed for the interaction.
       FGameplayAbilityActorInfo ActorInfo;
       ActorInfo.InitFromActor(InteractableTargetActor, TargetActor, InteractionOption.TargetAbilitySystem);

       UE_LOG(LogTemp, Display, TEXT("Trigger Interaction option: %s"), *InteractionOption.ID.ToString());

       // Trigger the ability using event tag.
       const bool bSuccess = InteractionOption.TargetAbilitySystem->TriggerAbilityFromGameplayEvent(
          InteractionOption.TargetInteractionAbilityHandle,
          &ActorInfo,
          TAG_Ability_Interaction_Activate,
          &Payload,
          *InteractionOption.TargetAbilitySystem
       );
    }
}

void UEdenInteractionsGameplayAbility_Interact::LookForInteractable()
{
    AActor* Actor = GetAvatarActorFromActorInfo();
    AController* Controller = Cast<AController>(GetOwningActorFromActorInfo());
    FEdenInteractionsQuery Query;
    Query.RequestingAvatar = Actor;
    Query.RequestingController = Controller;
    FCollisionProfileName Profile = TraceProfiles::CollisionProfileName;
    FGameplayAbilityTargetingLocationInfo LocationInfo = MakeTargetLocationInfoFromOwnerActor();

    UEdenInteractionsAbilityTask_WaitForInteractableTargets_SingleLineTrace* TraceTask =
       UEdenInteractionsAbilityTask_WaitForInteractableTargets_SingleLineTrace::WaitForInteractableTargets_SingleLineTrace(
          this, Query, Profile, LocationInfo, 200, 0.1, true);
    TraceTask->InteractableObjectsChanged.AddDynamic(this, &ThisClass::OnInteractableObjectsChanged);
    TraceTask->ReadyForActivation();
}

void UEdenInteractionsGameplayAbility_Interact::OnInteractableObjectsChanged(const TArray<FEdenInteractionsOption>& InteractionOptions)
{
    UpdateInteractions(InteractionOptions);
}

void UEdenInteractionsGameplayAbility_Interact::OnKeyPressed(float TimeWaited)
{
    TriggerInteraction();
    WaitForKeyPress();
}

void UEdenInteractionsGameplayAbility_Interact::WaitForKeyPress()
{
    if (auto* Task = UAbilityTask_WaitInputPress::WaitInputPress(this, false))
    {
       Task->OnPress.AddUniqueDynamic(this, &ThisClass::OnKeyPressed);
       Task->ReadyForActivation();
    }
}

Hi there,

      if (FGameplayAbilitySpec* Spec = AbilitySystem->FindAbilitySpecFromHandle(Handle))
        {
            int32 InputID = Spec->InputID;
         }

However as i understand you are not assigning an ID to abillity when granting right? If you assigned it should turn valid and you can do bindings etc.

switch (InputID)
{
    case 0: IndicatorData->InputAction = IA_Interact; break;
    case 1: IndicatorData->InputAction = IA_Use; break;
}

If you didn’t assign any binding to GAS nothing will happen and GAS doesn’t have context about the Input Actions and its bound keys.

You can use events if you like same in C and pass payload

and catch inside the ability

Probably you can also write a middlleware somewhere to hold last input action and resole it to GAS however it would be less elegant way..

Another thing catch my attention is? I want to display an indicator above the interactable actor that display the key to press to interact. not sure about your interaction architecture but imo interactable actor doesn’t have to even know about the abillity, its just an actor, it can have its own input mapping to hardware or gameplay context.

Once it’s activated it can talk with whatever interface or GAS

So if your problem is around the displaying the UI Key for it maybe we don’t need it. Actor saying I am interactable is just enough and it can query its own key.

PS : InputID for abillity also can be set in runtime dynamically as unique and you can wait for those inputs if you prefer.. and as far as i understood. There are many GAS actors around which are interactablles, these are scanned and tallked with , if that so you can pass payloads to that as input. To do that your scanner has to know about what trigger it and you can send that with payload to interacted actor

right now its

       // Trigger the ability using event tag.
       const bool bSuccess = InteractionOption.TargetAbilitySystem->TriggerAbilityFromGameplayEvent(
          InteractionOption.TargetInteractionAbilityHandle,
          &ActorInfo,
          TAG_Ability_Interaction_Activate,
          &Payload,
          *InteractionOption.TargetAbilitySystem

which payload has no info in it

       FGameplayEventData Payload;
       Payload.EventTag = TAG_Ability_Interaction_Activate;
       Payload.Instigator = Instigator;
       Payload.Target = InteractableTargetActor;
       Payload.OptionalObject = InteractionOption.OptionalObject;

Is this somekind of gamepad/keyboard/ etc hardware switch or some interacted actors can be activated by X Y ? If so interactable actor just query keys from enhanced input subsystem and when something triggers the interactable actor , actor itself doesn’t have to check if the correct key is pressed, something is triggered it (system) and its ok. If you have different interactions in one actor, that also can be done via payload even interacted actor doesn’t have to have a GAS it can just call and event with some variablles or the interface to do branching on actor side.