How-To: Write Your Own Interact Ability

Goal: build the interact ability yourself around this plugin's scan and grant tasks, so you can add hold-to-confirm, timed channels and your own prompt. The included Crimson Basic Interact is this same loop, instant only; write your own when you need more.

Prerequisites
Quick Start steps 1, 3 and 4 done: the plugin enabled, GA_InteractionHandler, and the chest. Do not also grant Crimson Basic Interact to the same pawn - two interact abilities would both fire on one press. You have one IA_Interact Input Action mapped in an Input Mapping Context (leave its Triggers list empty - important later for holds). How-To: Get the Building Blocks covers every node and getter used below.

1. Define your input tags

You need two input-forwarding tags. The interaction event tag Ability.Interaction.Activate is already registered by this plugin (it is Crimson Basic Interact's default event, and CrimsonCore uses the same string), so the handler from the Quick Start keeps working unchanged. Any other string works as long as sender and trigger agree.

Project Settings -> Project -> GameplayTags -> Manage Gameplay Tags -> add Input.Interact.Pressed and Input.Interact.Released (Ability.Interaction.Activate is already registered by the plugin).
Verify
Both input tags appear in the Gameplay Tag picker (e.g. open any Gameplay Tag property and search 'Interact').

2. Author the interact ability shell

One persistent, per-actor, locally-predicted ability owns the whole loop. It must run on both the server (to grant nearby options' abilities) and the owning client (to scan and take input), so it activates itself as soon as it is granted.

Create a second Gameplay Ability Blueprint, GA_Interact. In Class Defaults set Instancing Policy = Instanced Per Actor and Net Execution Policy = Local Predicted.

In My Blueprint, add one variable: CurrentOptions, type Crimson Interaction Option, container type Array. It is the ability's whole memory of what is in range, and every later step reads it.

Blueprint auto-activation
Blueprint cannot override OnAvatarSet, so activate from the pawn instead: after the ability is granted, Event BeginPlay -> Is Locally Controlled -> Get Ability System Component -> Try Activate Ability by Class (GA_Interact). A Local Predicted activation also activates the server instance - step 6 wires this.

3. Start the scan and grant tasks

In ActivateAbility: the server instance spins up Grant Abilities For Nearby Interactors (without it, the scan's CanActivateAbility filter rejects every option - the number-one setup bug); the owning client instance spins up a scan task and listens for the input events from step 4.

All of this hangs off GA_Interact's Event ActivateAbility. Start it with a Sequence node so the server half and the client half are separate wires, then build each one. Inside a Gameplay Ability graph Has Authority and Is Locally Controlled are the ability's own nodes and already carry True / False exec pins - no Branch needed.

  1. Event ActivateAbility -> Sequence. Then 0 is the server work, Then 1 the scan, Then 2 the input listeners.
  2. Then 0 -> Has Authority -> (True) -> Grant Abilities For Nearby Interactors (Interaction Scan Range = 500, Interaction Scan Rate = 0.1, Interaction Trace Channel = Visibility). This node exists only inside a Gameplay Ability graph. Skip it and every press is rejected - the scan's options are never granted, so there is nothing to fire.
  3. Then 1 -> Is Locally Controlled -> (True) -> Make CrimsonInteractionQuery (Requesting Avatar = Get Avatar Actor From Actor Info; Requesting Controller = that same actor -> Cast To Pawn -> Get Controller) -> Wait For Interactable Targets Single Line Trace (Interaction Trace Channel = Visibility, Interaction Scan Range = 500, Interaction Scan Rate = 0.1, Show Debug = unticked).
  4. Expand the task's Start Location pin and set Location Type = Actor Transform, Source Actor = Get Avatar Actor From Actor Info. The pin defaults to Literal Transform, which would start the trace at the world origin instead of on the pawn.
  5. From the scan task's Interactable Objects Changed delegate pin, drag out a Custom Event OnOptionsChanged with an Interactable Options input (Crimson Interaction Option, Array). In it: Set CurrentOptions from that input. This is the one place CurrentOptions is written, and index 0 is always the focused target.
  6. Then 2 -> Is Locally Controlled -> (True) -> Wait Gameplay Event (Event Tag = Input.Interact.Pressed, Only Trigger Once = unticked, Only Match Exact = ticked), and a second Wait Gameplay Event for Input.Interact.Released. Their Event Received pins are where step 5 and the holds How-To pick up.
Verify
Nothing visible yet, but pass bShowDebug = true on the scan call and the forward trace will draw once the ability activates (step 6). Top-down game? Use Wait For Interactable Targets Top Down Trace instead - see How-To: Choose a Scan Strategy.

4. Forward input as gameplay events

Keep input on the pawn (where Enhanced Input already lives) and forward presses to the ability as gameplay events - no input-component plumbing inside the ability, and the pattern is identical in Blueprint and C++.

On the pawn's Event Graph, add the EnhancedInputAction IA_Interact event. From its Started pin: Send Gameplay Event to Actor (Actor = Self, Event Tag = Input.Interact.Pressed, Payload left at its default). Wire both Completed and Canceled into a second Send Gameplay Event to Actor (Actor = Self, Event Tag = Input.Interact.Released).

Both go to Self because the ability lives on this pawn's ability system component. Leave IA_Interact's Triggers list empty - the warning below explains why that matters as soon as you add holds.

No Pressed/Tap trigger on IA_Interact
Leave the Input Action's triggers list empty (implicit Down). Pressed/Tap drop their trigger state one frame after the press, which fires Completed/Canceled while the key is still physically held - that breaks hold interactions later. If the IA must carry a trigger, use one that keeps reporting ETriggerState::Triggered for the whole physical hold - Enhanced Input raises Completed the moment a trigger stops reporting Triggered.

5. Fire the press

On press, take the focused option (CurrentOptions[0]) and fire it with Trigger Interaction Option (UCrimsonInteractionStatics). It back-fills the target ASC and spec handle if the server grant has not resolved locally yet, lets the target customize the event payload, and triggers the granted ability by replicated handle - predicted on the client, authoritative on the server. If the spec has not replicated yet it returns false and the press is safely ignored.

Back in GA_Interact, from the Wait Gameplay Event (Input.Interact.Pressed) task's Event Received pin: Get CurrentOptions -> Length -> Greater (integer) (B = 0) -> Branch. The guard matters - pressing with nothing in range would otherwise read index 0 of an empty array.

On True: Get CurrentOptions -> Get (a ref) (Index = 0) -> Trigger Interaction Option (Instigator = Get Avatar Actor From Actor Info, Interaction Event Tag = Ability.Interaction.Activate). Use the array's Get (a ref) rather than Get (a copy) - the node's Interaction Option pin is by-reference so it can back-fill the target ability system and spec handle for you. Its Return Value is false when the server grant has not replicated yet; ignore it, or branch on it to suppress a click sound.

6. Grant it and play

Grant GA_Interact on the server (grants are authority-only), then play. If you used the Blueprint shell, also activate it from the owning client (the note in step 2).

On the pawn: Event BeginPlay -> Switch Has Authority. From Authority: Get Ability System Component (Actor = the actor that owns your ability system component - the pawn, or its player state) -> Give Ability (Ability Class = GA_Interact, Level = 1, Input ID = -1). Grants are authority-only; the node is marked BlueprintAuthorityOnly and silently does nothing on a client.

Then, on a second wire from Event BeginPlay: Is Locally Controlled (Branch, True) -> Get Ability System Component -> Try Activate Ability by Class (In Ability To Activate = GA_Interact, Allow Remote Activation = ticked). A Local Predicted activation from the owning client also activates the server instance, which is what starts the grant task from step 3.

If your project already hands out startup abilities from an ability set, add GA_Interact there instead and keep only the activation wire.

What's next
Add hold-to-open, timed channels (mining) and an on-screen prompt in How-To: Add Holds, Channels & a Progress Prompt. Give targets per-actor feedback in How-To: World Indicators & Hold-Progress Feedback.