Files
Fishing2/Assets/Plugins/Ilumisoft/Graphics Control/Scripts/UI/AutoScrollHandler.cs
2025-05-29 18:03:24 +08:00

118 lines
3.5 KiB
C#

using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Ilumisoft.GraphicsControl.UI
{
/// <summary>
/// <see cref="UnityEngine.UI.ScrollRect"/> does not automatically scroll up or down when using the keyboard or gamepad.
/// This class automatically handles scrolling, by scrolling towards the selected element of a <see cref="UnityEngine.UI.ScrollRect"/> when selection changes.
/// </summary>
[RequireComponent(typeof(ScrollRect))]
[DefaultExecutionOrder(1)]
public class AutoScrollHandler : MonoBehaviour
{
/// <summary>
/// The currently selected game object
/// </summary>
GameObject selected = null;
/// <summary>
/// The previously selected game object
/// </summary>
GameObject previousSelected = null;
[SerializeField]
[Tooltip("Scroll speed when changing selection")]
float scrollSpeed = 5.0f;
/// <summary>
/// Reference to the controlled <see cref="UnityEngine.UI.ScrollRect"/> component
/// </summary>
ScrollRect ScrollRect { get; set; }
private void Awake()
{
ScrollRect = GetComponent<ScrollRect>();
}
private void Start()
{
selected = EventSystem.current.currentSelectedGameObject;
previousSelected = selected;
// Auto focus the first element
ScrollRect.verticalScrollbar.value = 1;
}
void Update()
{
selected = EventSystem.current.currentSelectedGameObject;
// Check whether the selection has changed
if (selected != previousSelected)
{
previousSelected = selected;
if (selected != null)
{
OnSelectionChanged();
}
}
}
/// <summary>
/// Scroll towards the selected element when the selection has changed
/// </summary>
void OnSelectionChanged()
{
if (TryGetIndex(selected, out var index))
{
StopAllCoroutines();
StartCoroutine(ScrollTowards(index));
}
}
/// <summary>
/// Tries to get the index of the given element in the content
/// </summary>
/// <param name="element"></param>
/// <param name="index"></param>
/// <returns></returns>
bool TryGetIndex(GameObject element, out int index)
{
index = 0;
foreach (Transform child in ScrollRect.content)
{
if (element == child.gameObject)
{
return true;
}
index++;
}
return false;
}
/// <summary>
/// Scrolls towards the content element with the given index
/// </summary>
/// <param name="targetIndex"></param>
/// <returns></returns>
IEnumerator ScrollTowards(int targetIndex)
{
var scrollbar = ScrollRect.verticalScrollbar;
float target = 1 - targetIndex / (float)(ScrollRect.content.childCount - 1);
while (Mathf.Abs(scrollbar.value - target) > 0.01f)
{
scrollbar.value = Mathf.MoveTowards(scrollbar.value, target, scrollSpeed * Time.deltaTime);
yield return null;
}
}
}
}