Binding OnClicked on AActor to a function in UActorComponent in UE5?

Hey everyone,

I am trying to implement a functionality that i have in Blueprint in one project similarly in C** for another project.
In Blueprint the Event Graph on an ActorComponent is used to bind an event to “On Clicked” on the Owner Actor like so:

I tried a similar approach in C++ now but am not able to get it right as apparently neither AActor nor UActorComponent “just” have “On Clicked” readily available and i am only able to find tutorials and examples for UE4 that no longer work like that.

My C++ Code:

CB_ClickableComponent.h:

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CB_ClickableComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class CITY_BUILDER_CPP_API UCB_ClickableComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UCB_ClickableComponent();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	UFUNCTION()
	void OnMouseClicked(AActor* ClickComponent, FKey ButtonPressed);
		
private: 
	AActor* Owner;
};

CB_ClickableComponent.cpp:

#include "GameObjects/Components/CB_ClickableComponent.h"

// Sets default values for this component's properties

UCB_ClickableComponent::UCB_ClickableComponent()

{

PrimaryComponentTick.bCanEverTick = true;

// ...

}

// Called when the game starts

void UCB_ClickableComponent::BeginPlay()

{

Super::BeginPlay();

// ...

Owner = GetOwner();

OnClicked.AddDynamic(Owner, &UCB_ClickableComponent::OnMouseClicked);

}

// Called every frame

void UCB_ClickableComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)

{

Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

// ...

}

// Register with OnClicked on Owner

void UCB_ClickableComponent::OnMouseClicked(AActor* LeOwner, FKey ButtonPressed)

{

}

OnMouseClicked not yet implemented because the binding does not even work.

Do I need to do this via Blueprint? Or am I on the right track but not quite there yet.
Any hints, pointers or links to tutorials / documentation are highly appreciated.

Kind Regards
Chris

1 Like

I am having the same issue, did u found a fix?

Nope, sadly not. I am getting the impression that the solution lies more in “Get Hit Result Under Cursor for Objects” and then probably implement it in the PlayerController rather than the actor.
I did not have time to try it out more but wanted to do that latest next weekend and then maybe rephrase my question accordingly.

So, I finally figured it out after taking a look with some distance:

The reference OnClicked is only available on AActor (as stated before).
Using the AddDynamical macro the function that handles the click event can be set to this reference.

Since i am doing this on the ActorComponent, the references need to be past back and forth correctly. Also VS 2022 Intellisense was not able to help me from the get go with this so i had to blindly add the code and hope for the best.
In the end, compilation ran through and the desired outcome was seen in the project.

The Header File:

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CB_ClickableComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class CITY_BUILDER_CPP_API UCB_ClickableComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UCB_ClickableComponent();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	UFUNCTION()
	void OnMouseClicked(AActor* ClickComponent, FKey ButtonPressed);

	UPROPERTY(EditAnywhere)
	UMaterial* Material_Enabled;

	UPROPERTY(EditAnywhere)
	UMaterial* Material_Disabled;

private:
	AActor* Owner;
	bool bIsEnabled = false;
	UStaticMeshComponent* Mesh;
};

And the CPP File

#include "GameObjects/Components/CB_ClickableComponent.h"

// Sets default values for this component's properties
UCB_ClickableComponent::UCB_ClickableComponent()
{
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}


// Called when the game starts
void UCB_ClickableComponent::BeginPlay()
{
	Super::BeginPlay();

	Owner = Cast<AActor>(GetOwner());
	if (Owner)
	{
		UE_LOG(LogTemp, Warning, TEXT("Owner of ClickableComponent is %s"), *Owner->GetName());
		Owner->OnClicked.AddDynamic(this, &UCB_ClickableComponent::OnMouseClicked);
		Mesh = Cast<UStaticMeshComponent>(Owner->GetComponentByClass(UStaticMeshComponent::StaticClass()));
		if (!Mesh)
		{
			UE_LOG(LogTemp, Warning, TEXT("Mesh is null"));
		}
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("Owner of ClickableComponent is null"));
	}

}


// Called every frame
void UCB_ClickableComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...
}

void UCB_ClickableComponent::OnMouseClicked(AActor* ClickComponent, FKey ButtonPressed)
{
	if (Mesh != nullptr)
	{
		if (bIsEnabled)
		{
			Mesh->SetMaterial(0, Material_Enabled);
			bIsEnabled = false;
		}
		else
		{
			Mesh->SetMaterial(0, Material_Disabled);
			bIsEnabled = true;
		}
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("Mesh is null"));
	}
}

This component will enable the player to click on any actor it has been added to and then change the material of the staticmeshcomponent based on the click - i chose Red / Green to visualize it for me.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.