Select an actor with mouse click and call actor's class function with UMG button c++

I’m trying to select an actor with mouse click and use UMG buttons to for example change material on selected actor. Can someone please guide the right way to implement this in C++ like how to get/save the reference of selected/last clicked actor? Buttons are different materials and I have SetMaterial implemented in cube and sphere classes which takes a material parameter sent by pressed UMG button. Or should it be done some other way?

240263-mouseclick.png

Hi, the first thing you need is to get a reference to your actor. In player controller on mouse click call function Get Hit Result Under Cursor By Channel and store found actor. When clicking button get a reference to a player controller, cast it to your pc, get a reference to that actor, get static or skeletal mesh component of that actor and set desirable material.

1.mouse click ,you can get screen position.
2.use “PlayerController->DeprojectScreenPositionToWorld” can get startposition ,direction,and compute endposition .
3.you can use UKismetSystemLibrary::LineTraceSingle() get actor.
4.UMG Botton call (step.3 get actor) function ,and change material.

Ok I created empty MyUserWidget, MyGameMode, MyPlayerController c++ classes.
Then actors classes MyCube, MySphere with UStaticMeshComponent and a SetMaterial1, SetMaterial2 functions(BlueprintCallable for now).

Then created BP_MyWidget blueprint class based on MyUserWidget where I created buttons. And in event graph → OnClicked(Button1) → GetAllActorsOfClass (MyCube) → SetMaterial1. Here I’m confused how I should use c++ to get selected actor and call SetMaterial on that
Also added the widget to view port in the MainMap level blueprint.* Can I do that in c++?

This works fine. Each button changes the material but if there are several MyCube actors, material changes for all of them. So I want to be able to click on an actor get/save reference → and then pressing buttons should call SetMaterial only on referenced actor.

For testing, I created blueprint BP_MyCube based on MyCube class and in event graph I can simply OnClicked(StaticMesh) → SetMaterial1. And it works with clicking on specific actor to call SetMaterial on that actor.

But I need to get/save reference in c++ so I tried to add OnClicked.AddDynamic to my actor class for mouse click events but seems like I cant use AddDynamic in actors?? Please tell me what should I do in what classes. I’m not using any player controller yet.

Hi, I updated what I have done. Can you please add some pointers what I should do next? My player controller is currently empty.

Hi, I updated what I have done. Can you please add some pointers what I should do next?

Then get a reference to object under the mouse cursor. Do this in a player controller:

.
In buttons click event get cast the first pc to your own and call BlueprintCallable function(or just bp function) that will actually change material.

You shouldn’t use "GetAllActorsOfClass ",because it get all actors,but you only get a specific actor,so you can use “ray detection”,such as “LineTraceSingle of c++” or “LineTraceForObjects,LineTraceByChannel of blueprint”,return HitResult struct,this struct can get a actor,this actor is pickup actor,then save this actor,this actor can add a function that switch materials…I said a lot of chinese english,sorry…

I’m unable to understand… I can’t even get OnClicked working. It works in BP but when I add it to Player controller class or some actor class, it doesn’t recognize OnClicked. And that is where I need to pick and save the actor reference. Can you please add some c++ code snippet instead of BP?

But if I just have a room with objects and no player controller. Isn’t it possible to just read mouse click events on actors and get reference?

If you don’t have playercontroller,engine will use a default playercontroller…so you can write “GetPlayerController” node in pawn or actor,then return node can get DeprojectScreenPositionToWorld function

I know that probably everyone has already decided everything, but let it remain for posterity.
I do not know how to create a TouchEvent, but for the rest:

FHitResult GetHitResultUnderCursorByChannel()
{
if (!World)
{
 // Обработка ошибки - мир не найден
return FHitResult();
}

FVector2D MousePosition;
if (!UGameplayStatics::GetPlayerController(Self, 0)->GetMousePosition(MousePosition.X, MousePosition.Y))
{
    // Обработка ошибки - не удалось получить позицию мыши
    return FHitResult();
}

FVector CameraLocation;
FRotator CameraRotation;
UGameplayStatics::GetPlayerController(Self, 0)->GetPlayerViewPoint(CameraLocation, CameraRotation);

APlayerController* Player = UGameplayStatics::GetPlayerController(Self, 0);
FVector WorldLocation;
FVector WorldDirection;
UGameplayStatics::DeprojectScreenToWorld(Player, MousePosition, WorldLocation, WorldDirection);

FVector StartTrace = CameraLocation;
float TraceDistance = 5000;
FVector EndTrace = StartTrace + (WorldDirection * TraceDistance);

FCollisionQueryParams TraceParams(FName(TEXT("Trace")), true, Self);
TraceParams.bTraceComplex = true;
TraceParams.bReturnPhysicalMaterial = true;
TraceParams.AddIgnoredActor(Self);

FCollisionResponseParams ResponseParam;

FHitResult HitResult;
World->LineTraceSingleByChannel(HitResult, StartTrace, EndTrace, ECollisionChannel::ECC_Visibility, TraceParams, ResponseParam);

return HitResult;
}

it is enough to simply call this function and write the result to a variable

FHitResult Result = GetHitResultUnderCursorByChannel();
AActor* YouObject = Result.Actor;

next, I recommend creating an interface and changing the color through it

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

World = GetWorld();
Self = GetOwner();
if (InputComponent)
{
	InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AActorName::OnTouchPressed);
}
}

void AActorName::OnTouchPressed(ETouchIndex::Type FingerIndex, FVector Location)
{
	// Обработка события Touch
	// ...
}

and add

public:
void OnTouchPressed(ETouchIndex::Type FingerIndex, FVector Location);

to head file