How to know when User Widget is hit in PlayerController by GetHitResultUnderCursorByChannel?

The scenario:

There are several user interface widgets on this screenshot. Desired functionality is that when the user clicks on a unit, it selects (as you see here). If user clicks OFF of a unit (like on the floor) it unselects everything.

If user clicks on background of the card or the unit portrait or a button, the widget can absorb that hit and nothing happens.

Current Code in Player Controller
When left mouse is clicked, we have:
if (!GetHitResultUnderCursorByChannel(UEngineTypes::ConvertToTraceType(ECC_Visibility), false, HitResult))

This is always returning true. Thats fine. The Actor hit is always either the unit hit (good) or the static mesh of the ground if the ground was clicked (also good) but is also the static mesh of the ground if a user widget element is clicked other than an ENABLED button (bad. very bad). If the button is DISABLED, it also registers hitting the ground (bad).

I have set the visibility of pretty much everything to Visible:
image

I was hoping that this enables some type of hit test which is what everything I read alludes to. Unfortunately thats not the case. My user elements other than enabled buttons are ignored.

What do I have to do to have user interface elements such as this (say an image or a disabled button) register?

It could be that GetHitResultUnderCursorByChannel is not what I want and I should be using a different function to get the HitResult back.

It could be that I need a mousedown / up event on the widget itself?

Clicking an active button for sure 100% stops the controller from registering the action.

After a very long several hours pounding my head into the desk I found a workaround. There are a few of these questions floating around with some moderately useful hints but I’m going to give you the step by step for anyone else that has the same issue I ran into (and will likely make this a youtube channel lesson once I am done with my milestone because this one was pretty high on the irritating scale)

Images that you want to block
image

This example is from my HUD blueprint. Here we have a UUserWidget I call a UUnitPowersDialWidget. What I have gathered is that only a scant few UMG elements actually will block the mouse despite what properties you set or what lies the editor tells you about visibility. (that sneaky editor)

Here is the hierarchy for the dial:
image

Dial Image is at the bottom of that stack in the overlay so gets drawn last. As such it will be the first thing to get hit by the mouse cursor. So I need to make this thing block the mouse on click. If you do this on say “UnitImage”, it won’t work. Its like after it hits that first item if that did not block it, it just zooms right on through your UI element. At least - thats what it was doing for me. Anyhow I digress.

DialImage is made a variable. Now lets look in my C++ header file for this widget:

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "UnitPowersDialWidget.generated.h"

/**
 * Dial showing the unit from a render image as well as any powers that are linked to the unit can be fired here.
 */
UCLASS()
class MyGame_API UUnitPowersDialWidget : public UUserWidget
{
	GENERATED_BODY()

protected:
	virtual void NativeConstruct() override;

protected:

	/* Image containing the dial's frame - also the topmost item to block mouse events from */
	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
	class UImage* DialImage;

	UFUNCTION()
	FEventReply ImageOnButtonDown(FGeometry MyGeometry, const FPointerEvent& MouseEvent);
};

I need to have the bound widget DialImage (note its BindWidget directive. This means that there has to be an Image control called DialImage on the widget in the editor or it will throw an exception.

I also am overriding Native Construct as well as creating a function that is going to handle the OnMouseDown event of the image.

Now in the cpp:

#include "UI/UnitPowersDialWidget.h"
#include "Components/Image.h"

void UUnitPowersDialWidget::NativeConstruct()
{
	Super::NativeConstruct();

	// IMPORTANT
	// The UnitOrdersImage is being used because it is the highest image in the hierarchy.  Trapping Mouse Button Down below this hierarchy results in nothing
	// The goal is to catch the event and handle it so the mouse does not continue below the UI element.
	DialImage->OnMouseButtonDownEvent.BindUFunction(this, "ImageOnButtonDown");
}

FEventReply UUnitPowersDialWidget::ImageOnButtonDown(FGeometry MyGeometry, const FPointerEvent& MouseEvent)
{
	return FEventReply(true);
}

All this does is grab the mouse down event (the only thing that the image traps for without going into the engine and modifying it yourself) and then just handle it.

The end result is that this works great and clicking on the unit portrait no longer fires through that widget into the ground below.

A more complicated scenario
image

This is an image on an image with UButtons on top of it. Buttons work out of the box… except for when they are disabled.

When they are disabled they do what every other element does…your click fires right through them causing the same problem.

I couldn’t find a way around this at all so I had to basically not use the ability to disable the button and instead its always enabled, but I have code that swaps the image out for a disabled image when its “disabled” and my C++ will handle what it should do on disabled (nothing, just return out).

I hope that this could save you a few hairs and the sanctity of your work station without head to desk over and over for a few hours.

Cheers.

EDIT: as an extension to illustrate just how silly this can get:

image

Here is another of my HUD widgets that I need to block the mouse for. The background image is set to now block mouse down as shown above and that works great, until the user clicks on any of the labels (and this thing will be full of labels - its kind of empty right now).

The thing is textblocks do NOT have a mouse down event… so you have to basically arrange a set of empty transparent images around your control that block the mouse event. For this example I’m looking at one image over the stats box, but then because I have a button that needs clicked in the top box with Proto Infantry and Medium Infantry, I’m going to have to create a series of images that leave the button exposed to click.

Alternatively which is what a lot of bigger studios will do - they will get into the engine itself and create a mouse down for the textblock or will find a way to rig these elements to block mouse on a channel so that your controllers can do a raycast and see if they hit UI elements and if so - bounce out.

Sadly - I am not yet at that level where I can easily do something like that as my project is not working with the engine source but thats just another aside thrown out there for you to consider should you run into this issue.

2 Likes