303 lines
8.8 KiB
C#
303 lines
8.8 KiB
C#
/// Credit zero3growlithe
|
|
/// sourced from: http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-2#post-2011648
|
|
|
|
/*USAGE:
|
|
Simply place the script on the ScrollRect that contains the selectable children you will be scrolling
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using UnityEngine.EventSystems;
|
|
|
|
namespace UnityEngine.UI.Extensions
|
|
{
|
|
[RequireComponent(typeof(ScrollRect))]
|
|
[AddComponentMenu("UI/Extensions/UIScrollToSelection")]
|
|
public class UIScrollToSelection : MonoBehaviour
|
|
{
|
|
#region MEMBERS
|
|
|
|
[Header("[ References ]")]
|
|
[SerializeField, Tooltip("View (boundaries/mask) rect transform. Used to check if automatic scroll to selection is required.")]
|
|
private RectTransform viewportRectTransform;
|
|
[SerializeField, Tooltip("Scroll rect used to reach selected element.")]
|
|
private ScrollRect targetScrollRect;
|
|
|
|
[Header("[ Scrolling ]")]
|
|
[SerializeField, Tooltip("Allow automatic scrolling only on these axes.")]
|
|
private Axis scrollAxes = Axis.ANY;
|
|
[SerializeField, Tooltip("MOVE_TOWARDS: stiff movement, LERP: smoothed out movement")]
|
|
private ScrollMethod usedScrollMethod = ScrollMethod.MOVE_TOWARDS;
|
|
[SerializeField]
|
|
private float scrollSpeed = 50;
|
|
|
|
[Space(5)]
|
|
[SerializeField, Tooltip("Scroll speed used when element to select is out of \"JumpOffsetThreshold\" range")]
|
|
private float endOfListJumpScrollSpeed = 150;
|
|
[SerializeField, Range(0, 1), Tooltip("If next element to scroll to is located over this screen percentage, use \"EndOfListJumpScrollSpeed\" to reach this element faster.")]
|
|
private float jumpOffsetThreshold = 1;
|
|
|
|
[Header("[ Input ]")]
|
|
[SerializeField]
|
|
private MouseButton cancelScrollMouseButtons = MouseButton.ANY;
|
|
[SerializeField]
|
|
private KeyCode[] cancelScrollKeys = new KeyCode[0];
|
|
|
|
// INTERNAL - MEMBERS ONLY
|
|
private Vector3[] viewRectCorners = new Vector3[4];
|
|
private Vector3[] selectedElementCorners = new Vector3[4];
|
|
|
|
#endregion
|
|
|
|
#region PROPERTIES
|
|
|
|
// REFERENCES
|
|
public RectTransform ViewRectTransform
|
|
{
|
|
get { return viewportRectTransform; }
|
|
set { viewportRectTransform = value; }
|
|
}
|
|
public ScrollRect TargetScrollRect
|
|
{
|
|
get { return targetScrollRect; }
|
|
set { targetScrollRect = value; }
|
|
}
|
|
|
|
// SCROLLING
|
|
public Axis ScrollAxes => scrollAxes;
|
|
public ScrollMethod UsedScrollMethod => usedScrollMethod;
|
|
public float ScrollSpeed => scrollSpeed;
|
|
public float EndOfListJumpScrollSpeed => endOfListJumpScrollSpeed;
|
|
public float JumpOffsetThreshold => jumpOffsetThreshold;
|
|
|
|
// INPUT
|
|
public MouseButton CancelScrollMouseButtons => cancelScrollMouseButtons;
|
|
public KeyCode[] CancelScrollKeys => cancelScrollKeys;
|
|
|
|
// VARIABLES
|
|
private RectTransform scrollRectContentTransform;
|
|
private GameObject lastCheckedSelection;
|
|
|
|
// COROUTINES
|
|
private Coroutine animationCoroutine;
|
|
|
|
#endregion
|
|
|
|
#region FUNCTIONS
|
|
|
|
protected void Awake()
|
|
{
|
|
ValidateReferences();
|
|
}
|
|
|
|
protected void LateUpdate()
|
|
{
|
|
TryToScrollToSelection();
|
|
}
|
|
|
|
protected void Reset()
|
|
{
|
|
TargetScrollRect = gameObject.GetComponentInParent<ScrollRect>() ?? gameObject.GetComponentInChildren<ScrollRect>();
|
|
ViewRectTransform = gameObject.GetComponent<RectTransform>();
|
|
}
|
|
|
|
private void ValidateReferences()
|
|
{
|
|
if (!targetScrollRect)
|
|
{
|
|
targetScrollRect = GetComponent<ScrollRect>();
|
|
}
|
|
|
|
if (!targetScrollRect)
|
|
{
|
|
Debug.LogError("[UIScrollToSelection] No ScrollRect found. Either attach this script to a ScrollRect or assign on in the 'Target Scroll Rect' property");
|
|
gameObject.SetActive(false);
|
|
return;
|
|
}
|
|
|
|
if (ViewRectTransform == null)
|
|
{
|
|
ViewRectTransform = TargetScrollRect.GetComponent<RectTransform>();
|
|
}
|
|
|
|
if (TargetScrollRect != null)
|
|
{
|
|
scrollRectContentTransform = TargetScrollRect.content;
|
|
}
|
|
|
|
if (EventSystem.current == null)
|
|
{
|
|
Debug.LogError("[UIScrollToSelection] Unity UI EventSystem not found. It is required to check current selected object.");
|
|
gameObject.SetActive(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void TryToScrollToSelection()
|
|
{
|
|
// update references if selection changed
|
|
GameObject selection = EventSystem.current.currentSelectedGameObject;
|
|
|
|
if (selection == null || selection.activeInHierarchy == false || selection == lastCheckedSelection ||
|
|
selection.transform.IsChildOf(transform) == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RectTransform selectionRect = selection.GetComponent<RectTransform>();
|
|
|
|
ViewRectTransform.GetWorldCorners(viewRectCorners);
|
|
selectionRect.GetWorldCorners(selectedElementCorners);
|
|
|
|
ScrollToSelection(selection);
|
|
|
|
lastCheckedSelection = selection;
|
|
}
|
|
|
|
private void ScrollToSelection(GameObject selection)
|
|
{
|
|
// initial check if we can scroll at all
|
|
if (selection == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// this is just to make names shorter a bit
|
|
Vector3[] corners = viewRectCorners;
|
|
Vector3[] selectionCorners = selectedElementCorners;
|
|
|
|
// calculate scroll offset
|
|
Vector2 offsetToSelection = Vector2.zero;
|
|
|
|
offsetToSelection.x =
|
|
(selectionCorners[0].x < corners[0].x ? selectionCorners[0].x - corners[0].x : 0) +
|
|
(selectionCorners[2].x > corners[2].x ? selectionCorners[2].x - corners[2].x : 0);
|
|
offsetToSelection.y =
|
|
(selectionCorners[0].y < corners[0].y ? selectionCorners[0].y - corners[0].y : 0) +
|
|
(selectionCorners[1].y > corners[1].y ? selectionCorners[1].y - corners[1].y : 0);
|
|
|
|
// calculate final scroll speed
|
|
float finalScrollSpeed = ScrollSpeed;
|
|
|
|
if (Math.Abs(offsetToSelection.x) / Screen.width >= JumpOffsetThreshold || Math.Abs(offsetToSelection.y) / Screen.height >= JumpOffsetThreshold)
|
|
{
|
|
finalScrollSpeed = EndOfListJumpScrollSpeed;
|
|
}
|
|
|
|
// initiate animation coroutine
|
|
Vector2 targetPosition = (Vector2)scrollRectContentTransform.localPosition - offsetToSelection;
|
|
|
|
if (animationCoroutine != null)
|
|
{
|
|
StopCoroutine(animationCoroutine);
|
|
}
|
|
|
|
animationCoroutine = StartCoroutine(ScrollToPosition(targetPosition, finalScrollSpeed));
|
|
}
|
|
|
|
private IEnumerator ScrollToPosition(Vector2 targetPosition, float speed)
|
|
{
|
|
Vector3 startPosition = scrollRectContentTransform.localPosition;
|
|
|
|
// cancel movement on axes not specified in ScrollAxes mask
|
|
targetPosition.x = ((ScrollAxes | Axis.HORIZONTAL) == ScrollAxes) ? targetPosition.x : startPosition.x;
|
|
targetPosition.y = ((ScrollAxes | Axis.VERTICAL) == ScrollAxes) ? targetPosition.y : startPosition.y;
|
|
|
|
// move to target position
|
|
Vector2 currentPosition2D = startPosition;
|
|
float horizontalSpeed = (Screen.width / Screen.dpi) * speed;
|
|
float verticalSpeed = (Screen.height / Screen.dpi) * speed;
|
|
|
|
while (currentPosition2D != targetPosition && CheckIfScrollInterrupted() == false)
|
|
{
|
|
currentPosition2D.x = MoveTowardsValue(currentPosition2D.x, targetPosition.x, horizontalSpeed, UsedScrollMethod);
|
|
currentPosition2D.y = MoveTowardsValue(currentPosition2D.y, targetPosition.y, verticalSpeed, UsedScrollMethod);
|
|
|
|
scrollRectContentTransform.localPosition = currentPosition2D;
|
|
|
|
yield return null;
|
|
}
|
|
|
|
scrollRectContentTransform.localPosition = currentPosition2D;
|
|
}
|
|
|
|
private bool CheckIfScrollInterrupted()
|
|
{
|
|
bool mouseButtonClicked = false;
|
|
|
|
// check mouse buttons
|
|
switch (CancelScrollMouseButtons)
|
|
{
|
|
case MouseButton.LEFT:
|
|
mouseButtonClicked |= Input.GetMouseButtonDown(0);
|
|
break;
|
|
case MouseButton.RIGHT:
|
|
mouseButtonClicked |= Input.GetMouseButtonDown(1);
|
|
break;
|
|
case MouseButton.MIDDLE:
|
|
mouseButtonClicked |= Input.GetMouseButtonDown(2);
|
|
break;
|
|
}
|
|
|
|
if (mouseButtonClicked == true)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// check keyboard buttons
|
|
for (int i = 0; i < CancelScrollKeys.Length; i++)
|
|
{
|
|
if (Input.GetKeyDown(CancelScrollKeys[i]) == true)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private float MoveTowardsValue(float from, float to, float delta, ScrollMethod method)
|
|
{
|
|
switch (method)
|
|
{
|
|
case ScrollMethod.MOVE_TOWARDS:
|
|
return Mathf.MoveTowards(from, to, delta * Time.unscaledDeltaTime);
|
|
case ScrollMethod.LERP:
|
|
return Mathf.Lerp(from, to, delta * Time.unscaledDeltaTime);
|
|
default:
|
|
return from;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CLASS_ENUMS
|
|
|
|
[Flags]
|
|
public enum Axis
|
|
{
|
|
NONE = 0x00000000,
|
|
HORIZONTAL = 0x00000001,
|
|
VERTICAL = 0x00000010,
|
|
ANY = 0x00000011
|
|
}
|
|
|
|
[Flags]
|
|
public enum MouseButton
|
|
{
|
|
NONE = 0x00000000,
|
|
LEFT = 0x00000001,
|
|
RIGHT = 0x00000010,
|
|
MIDDLE = 0x00000100,
|
|
ANY = 0x00000111
|
|
}
|
|
|
|
public enum ScrollMethod
|
|
{
|
|
MOVE_TOWARDS,
|
|
LERP
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |