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);
}
}
@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