How to scroll a UScrollBox with the gamepad
Quick disclaimer before we start. I spent very little time researching the best way to do this. I couldn't find something quickly and I have a game to ship, so I did what any respectable gamedev does in this situation: hack something real quick that works well enough.
TL;DR: Here's how we do it – detect input, store the scrolling flag based on gamepad stick input, modify scroll box offset.
Now let's get to it.
If you have a scroll box with focusable widgets as children, you can use ScrollWidgetIntoView as a helper. Unfortunately, my situation is different and I don't have focusable widgets. Instead, I needed an approach where I could scroll the content with a gamepad like you would with the mouse wheel.
The below approach, tested and implemented in UE 5.3.2, works for me and it may or may not work for you. If you have complex UI systems it may conflict with those, but in that case you probably won't have this problem. If you have a better suggestion for how to do this, please reach out, I'd love to hear it.
We start by adding three variables to our widget blueprint with the scroll box. The boolean bScrolling stores our state and gets set to true when there’s gamepad stick input. ScrollSpeed holds the target speed at which we'd like the contents to scroll, and InputValue will hold the analog value of the gamepad stick. This latter one is optional, but I wanted to take advantage of the analog behavior of the input.
We will need to override three different input functions: OnKeyDown, OnKeyUp, and OnAnalogValueChanged. It should be obvious what they do. They trigger when there's input and we'll use them to set the scroll flag and the analog value.
When any key is pressed, we check to see if the key is the gamepad right stick up or down, and set the bScrolling value to true if that’s the case.
On key up we do the inverse of the above and set the boolean to false when the gamepad stick key is released.
The analog value changed function gets called when any analog value changes, so we need to first check that our right stick Y-axis is what got touched in this event. Once we know that, we store the analog value in the InputValue variable. This is a variable in the range [-1.0, 1.0] where 0 is no input, and the two extremes are all the way up/down (though I’m too lazy to check which is which).
Finally, in the tick function, if the widget is visible and the bScrolling flag is set, we multiply our desired ScrollSpeed by the InputValue and also the frame delta time, we subtract this from the current scroll box offset, we clamp the resulting value to [0, “end of content offset’], and set this as the new scroll offset. The clamp will make sure we don't go beyond the end of the content inside the scroll box.
That’s it. Happy scrolling.
PS. Check out Terralysia on Steam, I use the above code/blueprints for the unlocks and leaderboards screens.