OK I’m sure this is basic C++ but I’ve been wracking my head over how to do this for a couple days…
I have a UMouseWidget class of base type UUserWidget that I’m using for my mouse pointer.
I specified it under Project Settings and it all works like it should … it gets instantiated by the engine somewhere when the game starts up.
My problem is I need to be able to call a member function on the object from another class object ( UMouseWidget::SetType(int32 Type) )
If I had a pointer to it I assume I could just call something like MouseWidget->SetType(1);
But how can I get the pointer if Unreal is instantiating the thing elsewhere?
I thought about passing “this” in the Constructor to a pointer in GameInstance, and then grabbing it from there, but it looks like the UMouseWidget starts up before anything else.
You would need to find where the instance is being created and held onto by the engine - that’s the only way to get a pointer to it.
For the mouse widget specifically though, I’m pretty sure nothing holds onto it directly. The cursors are instantiated in the GameViewportClient which is easy enough to access, but it does not hold a pointer to the UserWidget, it only holds the internal Slate Widget.
For a workaround, you could broadcast a static delegate from your widgets’ NativeOnInitialized() function which passes itself in, and another static class can listen for it and provide access to the cursor. Alternatively you can try to get all widgets in the viewport, then filter through them until you find the Cursor widget and keep a reference to it somewhere. This is probably unsafe though, since the viewport may often create/destroy the cursor.
Generally speaking though, it seems as though the better option is to have the cursor manage itself.
Update : I was able to solve this problem, in case this helps anyone else.
I set a 1 second repeating timer inside the mouse class NativeConstruct override, so that it will call a method that checks every second to see if my GameInstance object (Globals) has become available.
When it becomes available I store “this” in a MouseWidget pointer on a public member of the GameInstance object and cancel the timer.
My other “user interface” widget can then check to see if the pointer is null, and if not, it can call Globals->MouseWidget->SetMousePointer(Type).
I know this is somewhat hacky, but it works, and I don’t have to do anything inside NativeTick.
No one is going to do anything that needs a mouse cursor change within the first second or two the game starts up, and I’ve been able to make one class object aware of the other.
What do you mean with type? Just the visuals of the curser? There is an option in every UMG widget that changes the curser when hovered. Though I don’t know if that works for a custom widget.
Yes, well sorta. I’m switching cursors while dragging on the header of the “window”. I’m making a draggable window system. Yes its true there is a lot I don’t know about Unreal Engine or “best practices”, I’m just trying to learn and make it work. I’ve only been working with it about 10 days. I was doing a course on Udemy and got sidetracked into widgets and UI.
A better approach would be to find out what widget is currently under the cursor (there is a static function for this in the Slate Application I believe), and get it’s “Cursor Type” value and have the cursor update itself.
All widgets have a “Cursor” property which defines what cursor should be used when it’s over that widget and it’s interactable.
Yes, that’s sort of what I’m doing, but I only check “hovered” on a click event, I don’t poll whats under the cursor every frame. This is my code for determining what button is under the cursor. It returns the X value of the buttons pivot transform from the Blueprint, which is unused by me.
int32 UCombinedWidget::GetHoveredWidgetIndex()
{
FSlateApplication& MySlateApplication = FSlateApplication::Get();
FWidgetPath WidgetsUnderCursor = MySlateApplication.LocateWindowUnderMouse(
MySlateApplication.GetCursorPos(),
MySlateApplication.GetInteractiveTopLevelWindows());
if (WidgetsUnderCursor.IsValid())
{
for (int32 Index = 0; Index < WidgetsUnderCursor.Widgets.Num(); Index++)
{
FArrangedWidget& Widget = WidgetsUnderCursor.Widgets[Index];
if (Widget.Widget->IsDirectlyHovered())
{
FVector2D Pivot = Widget.Widget->GetRenderTransformPivot();
return int32(Pivot.X);
}
}
}
return 0;
}
I discovered that a common request (and a feature request) for OnClicked was to be able to pass a parameter (so you know which button was clicked) but I discovered that I could store a unique button identifier (much like the way the Win32 API handles buttons) in the transform pivot X (or Y) value inside the Blueprint, and then do a GetRenderTransformPivot to pull it back out. The advantage to this of course is I don’t have to register a different OnClicked function for every single button or mess with delegates, and my code is smaller, yay! I can just have one function for OpenWindow, DragWindow, etc.
Anyway back to the point, I didn’t want to change my mouse cursor based on a constantly polled method in Tick, and I didn’t want to change the cursor on “hovered”, I just wanted to change it when I initiated a drag on a window, or released, or entered a “picking” mode.
Being able to know the mouse widget object pointer (my original issue) also lets me easily set a brush when I’m dragging inventory items between windows.
Again I’m absolutely sure there are other methods and I know I’m still learning.
We do exactly that by wrapping lots of widgets in a custom UMG widget. E.g. the default UMG button is placed in a custom UMG widget that exposes the dispatchers (Clicked, pressed, hovered,…) with a reference to the object. That way we can easily create radio buttons and such stuff. Other advatages is consistent design. If you use Unreals default button you have to design it everytime you place it down. With the wrapped button we design it once. A design change is super easy.
While it works and gives opportunities, we create tons of additional UObjects with that approach. The cost for the wrapped dispatchers does not interest, as it only applies when the button is clicked/hovered/…
.