I recently realized that UE4 has no out-of-the-box solution for controlling UMG Widgets with Keyboard and/or Gamepad.
Searching the forum and the Answer Hub I wasn’t able to find a satisfactory solution that doesn’t require some bottom up redesign of existing HUDs.
So I tried to come up with a lightweight and flexible solution that doesn’t require any C++ and as little modification to existing HUDs as possible.
My solution is based on an HUDManager Actor Component that gets attached to the PlayerController and handles all controls and bookkeeping. It
communicates with Widgets via Blueprint Interfaces that can be implemented by any existing UserWidget class. It doesn’t care about the nature of the
Widget. 3D-Widgets in the form of WidgetComponents work just as well.
Here a short demonstration: https://youtube.com/watch?v=RJxZnYm2vlM
EDIT: The project is available for download here: https://gum.co/JfqJ
PS: It is perfectly fine to put in $0 to get it for free.
The project became a little to big to make a tutorial in a feasible amount of time.
Instead you can download the project here https://gum.co/JfqJ
You can simply pay what you want. It is perfectly fine to put in $0 and get it for free.
I did my best to comment the blueprint appropriately. Please ask if something is not clear.
Thank you very much for giving such a nice gift to the community.
But i just saw that you just uploaded the .uproject file (437 bytes or something like that). You have to upload the entire project folder otherwise you can’t really do much with an uproject file.
The appropriately* named ControllerHUDTestWidget4 is a demo implementation of a WidgetSwitcher. Maybe that answers your question. If not, let me know.
I am not sure what you mean by destroying widgets. I don’t actively destroy widgets. However, ControllerHUD will only navigate to widgets that are visible. If a widget is hidden for some reason, it must be set to visible before it will be accessible by controller. But maybe I misunderstood what you meant.
I will add an update to the original post explaining how ControllerHUD works within the next few days. That might be helpful for creating more sophisticated HUDs.
*The next version will have more expressive names.
You sure about the last part? When using the controls it’s selecting buttons that are currently hidden. Also, is it possible to grab an array of the navigable buttons of a certain panel widget. I want it so that if I can’t navigate to certain widgets(because they’re in a panel widget which isn’t referenced by the vertical interface function) they get a special disabled button style or make sure that panel widgets that are not focused on do not modify their style when selecting buttons from another panel widget.
Oh, what would be nice too is be able to bind Event clicked to “OnButtonPressed” of the selected button, or rather, it would be cool if you could reference a button and call its events manually. What I also noticed is the horizontal navigation is still somewhat limited. but with what I suggested with calling certain button evens can help alleviate that. Because in some scenarios it would be nice switch to a certain horizontal panel depending on what button you’ve selected.
I also don’t really understand the order the buttons are placed in. Also some feedback on the button styles. It’s better to save the the actual button style instead of different slate brush structures. Makes it easier to enable/disable buttons visually.
Something else I’ve noticed is the the first button that gets highlighted is the last in the hierachy instead of the first. Like when I set a new panel widget the most bottom button is highlighted.
My mistake. The visibility of widgets is indeed ignored. I was thinking of the visibility of UserWidgets. The nomenclature is somewhat prone to misunderstandings. I will think of a way to work with visibility at the widget level.
If I understand you correctly, you want an array with all widgets that are interact-able within one panel widget. The ControllerHUDManagerComponent has a ‘GetLayoutSize’ function that simply counts the number of interact-able widgets. It would be straight-forward to modify the function to return an array of widgets.
I don’t know if there is a way to trigger widget events programmatically. If there is a way, a lot of things would be much simpler. My search for a way to do that was not successful unfortunately.
Would it help you if you would cache the widget reference from the ‘HighlightElement’ event and use that reference the next time the ‘MoveHorizontally’ interface function is called?
I imagine you mean a situation like this: (every ‘#’ is a button)
You could check in the ‘MoveHorizontally’ function, if the last highlighted button was the third one in the column layout and if it is, return the inner/horizonal layout as the next vertical navigation layout. I admit that would not be ideal because then Up/Down would also move horizontally in that layout. (I will think about better ways to handle that.)
Or, if you want to jump to another UserWidget when clicking a button, you can use the ‘Click’ interface function’s reference to the ControllerHUDManagerComponent to call its ‘HUD_SetFocusToWidgetWithName’ event. This could maybe solve your problem if your wrap your inner layout in a UserWidget as well. I could add a reference to the ControllerHUDMangerComponent to other interface functions as well if that would be helpful.
I am not sure what the best way to do the highlighting is. Using SlateBrush references has the advantage of being usable for non-button widgets. For example if you want the highlight an button’s parent widget instead of the button itself. If all widgets are
buttons, ButtonStyles are probably the way to go.
Can you give me an example for this. In my projects it looks like the first one is highlighted.
Things took longer than anticipated due to a particular bad case of the common cold.
However, I uploaded version 0.3 with two small changes:
ControllerHUDManagerComponent now stores a reference ‘ActiveUserWidget’ to the UserWidget instance the currently focused button belongs to. If the currently focused button is not a child of a nested UserWidget, the reference is None.
This is useful if some logic is implemented in the UserWidget wrapping a button or a checkbox. For example if you store important data in the UserWidget instance representing an item in an inventory, you can now access that data from the inventory UserWidget whenever a button is clicked etc.
Buttons are only focusable when visible.
I am still thinking about a better way to handle nested layouts.
PS: My marketplace submission was rejected because there are too many assets in that category already. I am not sure how to interpret that. That means that ControllerHUD will remain free for now. Of course donations are welcome.