Skip to main content

Basic Interactable Actor

Type: Plugin/Engine only

This is the baseline pattern for a normal actor that becomes interactable by adding UEDInteractableComponent.

#pragma once

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

class UArrowComponent;
class UEDInteractableComponent;
class USceneComponent;
class UStaticMeshComponent;

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

public:
AMyBasicInteractableActor();

protected:
virtual void BeginPlay() override;

UPROPERTY(VisibleAnywhere)
TObjectPtr<USceneComponent> Root;

UPROPERTY(VisibleAnywhere)
TObjectPtr<UStaticMeshComponent> VisualMesh;

UPROPERTY(VisibleAnywhere)
TObjectPtr<USceneComponent> WidgetHolder;

UPROPERTY(VisibleAnywhere)
TObjectPtr<UArrowComponent> CenterArrow;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<UEDInteractableComponent> InteractableComponent;

UFUNCTION()
void HandleInteractionSuccessful(const FEDInteractionEventContext& Context);
};

Source

#include "MyBasicInteractableActor.h"

#include "Components/ArrowComponent.h"
#include "Components/EDInteractableComponent.h"
#include "Components/SceneComponent.h"
#include "Components/StaticMeshComponent.h"

AMyBasicInteractableActor::AMyBasicInteractableActor()
{
PrimaryActorTick.bCanEverTick = false;

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

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

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

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

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

void AMyBasicInteractableActor::BeginPlay()
{
Super::BeginPlay();

if (IsValid(InteractableComponent))
{
InteractableComponent->OnInteractionSuccessful.AddDynamic(this, &AMyBasicInteractableActor::HandleInteractionSuccessful);
}
}

void AMyBasicInteractableActor::HandleInteractionSuccessful(const FEDInteractionEventContext& Context)
{
UE_LOG(LogTemp, Log, TEXT("Interacted by %s on %s"),
*GetNameSafe(Context.Instigator),
*GetNameSafe(Context.InteractableActor));
}

Why this setup is useful

  • WidgetHolderComponent gives the plugin a clean point for widget placement.
  • CenterComponent gives it a stable focus center.
  • DimensionComponent helps the look-at collider and trace bounds logic.

Best practice

  • Keep bAutoInitialize enabled unless you have a strong reason to initialize manually.
  • Declare every AddDynamic(...) target as UFUNCTION().