Skip to content

Instantly share code, notes, and snippets.

@emredesu
Forked from mandarinx/ScrollRectAutoScroll.cs
Last active June 18, 2024 07:35
Show Gist options
  • Save emredesu/af597de14a4377e1ecf96b6f7b6cc506 to your computer and use it in GitHub Desktop.
Save emredesu/af597de14a4377e1ecf96b6f7b6cc506 to your computer and use it in GitHub Desktop.
Re-written for the new input system. Also uses unscaledDeltaTime for use in pause menus, and doesn't do anything if the platform is a mobile platform with no gamepads connected.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.InputSystem;
[RequireComponent(typeof(ScrollRect))]
public class ScrollRectAutoScroll : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {
public float scrollSpeed = 10f;
private bool mouseOver = false;
private List<Selectable> m_Selectables = new List<Selectable>();
private ScrollRect m_ScrollRect;
private Vector2 m_NextScrollPosition = Vector2.up;
void OnEnable() {
if (m_ScrollRect) {
m_ScrollRect.content.GetComponentsInChildren(m_Selectables);
}
}
void Awake() {
m_ScrollRect = GetComponent<ScrollRect>();
}
void Start() {
if (m_ScrollRect) {
m_ScrollRect.content.GetComponentsInChildren(m_Selectables);
}
ScrollToSelected(true);
}
void Update() {
// If we are on mobile and we do not have a gamepad connected, do not do anything.
if (SystemInfo.deviceType == DeviceType.Handheld && Gamepad.all.Count <= 1) {
return;
}
// Scroll via input.
InputScroll();
if (!mouseOver) {
// Lerp scrolling code.
m_ScrollRect.normalizedPosition = Vector2.Lerp(m_ScrollRect.normalizedPosition, m_NextScrollPosition, scrollSpeed * Time.unscaledDeltaTime);
}
else {
m_NextScrollPosition = m_ScrollRect.normalizedPosition;
}
}
#nullable enable
void InputScroll() {
if (m_Selectables.Count > 0) {
Keyboard? currentKeyboard = Keyboard.current;
Gamepad? currentGamepad = Gamepad.current;
if (currentKeyboard != null) {
if (Keyboard.current.upArrowKey.isPressed || Keyboard.current.downArrowKey.isPressed) {
ScrollToSelected(false);
}
}
if (currentGamepad != null) {
if (Gamepad.current.dpad.up.isPressed || Gamepad.current.dpad.down.isPressed) {
ScrollToSelected(false);
}
}
}
}
#nullable disable
void ScrollToSelected(bool quickScroll) {
int selectedIndex = -1;
Selectable selectedElement = EventSystem.current.currentSelectedGameObject ? EventSystem.current.currentSelectedGameObject.GetComponent<Selectable>() : null;
if (selectedElement) {
selectedIndex = m_Selectables.IndexOf(selectedElement);
}
if (selectedIndex > -1) {
if (quickScroll) {
m_ScrollRect.normalizedPosition = new Vector2(0, 1 - (selectedIndex / ((float)m_Selectables.Count - 1)));
m_NextScrollPosition = m_ScrollRect.normalizedPosition;
}
else {
m_NextScrollPosition = new Vector2(0, 1 - (selectedIndex / ((float)m_Selectables.Count - 1)));
}
}
}
public void OnPointerEnter(PointerEventData eventData) {
mouseOver = true;
}
public void OnPointerExit(PointerEventData eventData) {
mouseOver = false;
ScrollToSelected(false);
}
}
@caschafly
Copy link

Hi there
Thank you so much for the script.
Just notice that the input can hold, for example the arrow button, and the scroll will not move even when the select button is switched to the next one. is there any way to fix it? sorry for my poor English, and I hope I have explained it clearly.
Looking forward to your help, thank you so much!

@emredesu
Copy link
Author

Hey @caschafly, thank you for the warning!
I have fixed this issue by switching key.wasPressedThisFrame with key.isPressed. I have committed the same solution in the script, so feel free to copy-paste if you'd like.

@caschafly
Copy link

caschafly commented Jul 16, 2022 via email

@Petschko
Copy link

Neat!

@san0suke
Copy link

It worked for me, thanks a lot!

@BenightedAlizar
Copy link

Rimec commented that they used scroll.verticalNormalizedPosition = (1 - ((selectedIndex / 7) / ((((float)selectables.Count) / 7) - 1)));
With 7 being the width, to make an another branch of this work with grids of 7 width.

...But I really like how your version uses input to check for vertical vs horizontal instead!

My game uses new input system and rebindable keys, so I decided to use:

if (Mathf.Abs(currContext.ReadValue().y) > Mathf.Abs(currContext.ReadValue().x))
With currContext being InputAction.CallbackContext.

That way it's not hardcoded to specific keys.
Thanks, this was the easiest one to get working in my game!

@AshrafMartin
Copy link

BenightedAlizar

could you explain to me how you made it work with the new input system please?

@BenightedAlizar
Copy link

@AshrafMartin

I used a C# event workflow for the new input system.
In it input methods look like this: void SomeMethod (InputAction.CallbackContext context)

I just check the context to get direction.
if (context.ReadValue<Vector2>().y > 0.1f)
That's up input, since y is above 0!
(I just put it at 0.1 for a bit of extra deadzone for controllers.)

And of course for left/right you just check context.ReadValue<Vector2>().x

The reason it was "currContext" above is because my input system is on a separate script, I just stored context in that variable.

@AshrafMartin
Copy link

AshrafMartin commented Oct 3, 2023

@AshrafMartin

I used a C# event workflow for the new input system. In it input methods look like this: void SomeMethod (InputAction.CallbackContext context)

I just check the context to get direction. if (context.ReadValue<Vector2>().y > 0.1f) That's up input, since y is above 0! (I just put it at 0.1 for a bit of extra deadzone for controllers.)

And of course for left/right you just check context.ReadValue<Vector2>().x

The reason it was "currContext" above is because my input system is on a separate script, I just stored context in that variable.

thanks for the quick reply, I tried something similar but got weird results, gonna see what I can do in the morning ,its almost midnight

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment