-
-
Save mandarinx/eae10c9e8d1a5534b7b19b74aeb2a665 to your computer and use it in GitHub Desktop.
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.EventSystems; | |
using UnityEngine.UI; | |
[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() | |
{ | |
// Scroll via input. | |
InputScroll(); | |
if (!mouseOver) | |
{ | |
// Lerp scrolling code. | |
m_ScrollRect.normalizedPosition = Vector2.Lerp(m_ScrollRect.normalizedPosition, m_NextScrollPosition, scrollSpeed * Time.deltaTime); | |
} | |
else | |
{ | |
m_NextScrollPosition = m_ScrollRect.normalizedPosition; | |
} | |
} | |
void InputScroll() | |
{ | |
if (m_Selectables.Count > 0) | |
{ | |
if (Input.GetButtonDown("Horizontal") || Input.GetButtonDown("Vertical") || Input.GetButton("Horizontal") || Input.GetButton("Vertical")) | |
{ | |
ScrollToSelected(false); | |
} | |
} | |
} | |
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); | |
} | |
} |
Life saver script! Thanks a lot.
Remarks:
I like the behavior better without the line 87: ScrollToSelected(false);
This makes the mouse scroll works better.
I know this is a bit necro af but you saved my life. Had to rewrite just a bit for the new Input System (line 52) but other than that it works like a charm. For real, thanks a bunch.
If the technique still applies, it's no necro in my opinion. Glad it's been of use to all of you. :-)
If the technique still applies, it's no necro in my opinion. Glad it's been of use to all of you. :-)
Yeah, works great. Though I just spent like 30 mins trying to understand why it was working on my main menu but not on my pause menu, then I saw the Lerp function, and well, can't use Time.deltaTime if Time.timeScale is 0, so i just changed it to Time.unscaledDeltaTime, which I think is much better for UI in general, just in case.
Anyways, thanks a bunch again, i've spent hours and all I found were methods involving the dropdown component, 300 lines code, or over-complicated stuff. This is much more elegant, easy to read and understan, and really easy to implement.
Again, thanks.
Ah! Good catch. It totally agree on letting UI run on a separate timer from game time.
@Nebuchaddy Can you share your rewrite with the new Input System?
@Brouilles A bit late, but I created a fork for this in case anyone else needs it. I've tested it on PC with a keyboard, an Xbox One gamepad and on an Android phone, it worked fine on all of them.
https://gist.github.com/emredesu/af597de14a4377e1ecf96b6f7b6cc506
Amazing how it still works after 5 years <3
@PostleService That's because Unity hasn't touched their own UI system in 5 years. :-)
@mandarinx I got back to the code because it confused me why it wouldn't actually center on the selected option on option change.
Imho, You may be missing:
m_ScrollRect.normalizedPosition = m_NextScrollPosition;
after line 76
since You set the new m_NextScrollPosition but never tell the RectTransform to use the new position after the input
I created an account just to thank you for this script. I tried lots of things before finding this and none of them worked.
Simple & effective, thanks a bunch!