UIVerticalScroller.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /// Credit Mrs. YakaYocha
  2. /// Sourced from - https://www.youtube.com/channel/UCHp8LZ_0-iCvl-5pjHATsgw
  3. /// Please donate: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RJ8D9FRFQF9VS
  4. using UnityEngine.Events;
  5. namespace UnityEngine.UI.Extensions
  6. {
  7. [RequireComponent(typeof(ScrollRect))]
  8. [AddComponentMenu("Layout/Extensions/Vertical Scroller")]
  9. public class UIVerticalScroller : MonoBehaviour
  10. {
  11. [Tooltip("desired ScrollRect")]
  12. public ScrollRect scrollRect;
  13. [Tooltip("Center display area (position of zoomed content)")]
  14. public RectTransform center;
  15. [Tooltip("Size / spacing of elements")]
  16. public RectTransform elementSize;
  17. [Tooltip("Scale = 1/ (1+distance from center * shrinkage)")]
  18. public Vector2 elementShrinkage = new Vector2(1f / 200, 1f / 200);
  19. [Tooltip("Minimum element scale (furthest from center)")]
  20. public Vector2 minScale = new Vector2(0.7f, 0.7f);
  21. [Tooltip("Select the item to be in center on start.")]
  22. public int startingIndex = -1;
  23. [Tooltip("Stop scrolling past last element from inertia.")]
  24. public bool stopMomentumOnEnd = true;
  25. [Tooltip("Set Items out of center to not interactible.")]
  26. public bool disableUnfocused = true;
  27. [Tooltip("Button to go to the next page. (optional)")]
  28. public GameObject scrollUpButton;
  29. [Tooltip("Button to go to the previous page. (optional)")]
  30. public GameObject scrollDownButton;
  31. [Tooltip("Event fired when a specific item is clicked, exposes index number of item. (optional)")]
  32. public IntEvent OnButtonClicked;
  33. [Tooltip("Event fired when the focused item is Changed. (optional)")]
  34. public IntEvent OnFocusChanged;
  35. [HideInInspector]
  36. public GameObject[] _arrayOfElements;
  37. public int focusedElementIndex { get; private set; }
  38. public string result { get; private set; }
  39. private float[] distReposition;
  40. private float[] distance;
  41. //private int elementsDistance;
  42. //Scrollable area (content of desired ScrollRect)
  43. [HideInInspector]
  44. public RectTransform scrollingPanel{ get { return scrollRect.content; } }
  45. /// <summary>
  46. /// Constructor when not used as component but called from other script, don't forget to set the non-optional properties.
  47. /// </summary>
  48. public UIVerticalScroller()
  49. {
  50. }
  51. /// <summary>
  52. /// Constructor when not used as component but called from other script
  53. /// </summary>
  54. public UIVerticalScroller(RectTransform center, RectTransform elementSize, ScrollRect scrollRect, GameObject[] arrayOfElements)
  55. {
  56. this.center = center;
  57. this.elementSize = elementSize;
  58. this.scrollRect = scrollRect;
  59. _arrayOfElements = arrayOfElements;
  60. }
  61. /// <summary>
  62. /// Awake this instance.
  63. /// </summary>
  64. public void Awake()
  65. {
  66. if (!scrollRect)
  67. {
  68. scrollRect = GetComponent<ScrollRect>();
  69. }
  70. if (!center)
  71. {
  72. Debug.LogError("Please define the RectTransform for the Center viewport of the scrollable area");
  73. }
  74. if (!elementSize)
  75. {
  76. elementSize = center;
  77. }
  78. if (_arrayOfElements == null || _arrayOfElements.Length == 0)
  79. {
  80. _arrayOfElements = new GameObject[scrollingPanel.childCount];
  81. for (int i = 0; i < scrollingPanel.childCount; i++)
  82. {
  83. _arrayOfElements[i] = scrollingPanel.GetChild(i).gameObject;
  84. }
  85. }
  86. }
  87. /// <summary>
  88. /// Recognises and resizes the children.
  89. /// </summary>
  90. /// <param name="startingIndex">Starting index.</param>
  91. /// <param name="arrayOfElements">Array of elements.</param>
  92. public void updateChildren(int startingIndex = -1, GameObject[] arrayOfElements = null)
  93. {
  94. // Set _arrayOfElements to arrayOfElements if given, otherwise to child objects of the scrolling panel.
  95. if (arrayOfElements != null)
  96. {
  97. _arrayOfElements = arrayOfElements;
  98. }
  99. else
  100. {
  101. _arrayOfElements = new GameObject[scrollingPanel.childCount];
  102. for (int i = 0; i < scrollingPanel.childCount; i++)
  103. {
  104. _arrayOfElements[i] = scrollingPanel.GetChild(i).gameObject;
  105. }
  106. }
  107. // resize the elements to match elementSize rect
  108. for (var i = 0; i < _arrayOfElements.Length; i++)
  109. {
  110. int j = i;
  111. _arrayOfElements[i].GetComponent<Button>().onClick.RemoveAllListeners();
  112. if (OnButtonClicked != null)
  113. {
  114. _arrayOfElements[i].GetComponent<Button>().onClick.AddListener(() => OnButtonClicked.Invoke(j));
  115. }
  116. RectTransform r = _arrayOfElements[i].GetComponent<RectTransform>();
  117. r.anchorMax = r.anchorMin = r.pivot = new Vector2(0.5f, 0.5f);
  118. r.localPosition = new Vector2(0, i * elementSize.rect.size.y);
  119. r.sizeDelta = elementSize.rect.size;
  120. }
  121. // prepare for scrolling
  122. distance = new float[_arrayOfElements.Length];
  123. distReposition = new float[_arrayOfElements.Length];
  124. focusedElementIndex = -1;
  125. //scrollRect.scrollSensitivity = elementSize.rect.height / 5;
  126. // if starting index is given, snap to respective element
  127. if (startingIndex > -1)
  128. {
  129. startingIndex = startingIndex > _arrayOfElements.Length ? _arrayOfElements.Length - 1 : startingIndex;
  130. SnapToElement(startingIndex);
  131. }
  132. }
  133. public void Start()
  134. {
  135. if (scrollUpButton)
  136. scrollUpButton.GetComponent<Button>().onClick.AddListener(() =>
  137. {
  138. ScrollUp();
  139. });
  140. if (scrollDownButton)
  141. scrollDownButton.GetComponent<Button>().onClick.AddListener(() =>
  142. {
  143. ScrollDown();
  144. });
  145. updateChildren(startingIndex, _arrayOfElements);
  146. }
  147. public void Update()
  148. {
  149. if (_arrayOfElements.Length < 1)
  150. {
  151. return;
  152. }
  153. for (var i = 0; i < _arrayOfElements.Length; i++)
  154. {
  155. distReposition[i] = center.GetComponent<RectTransform>().position.y - _arrayOfElements[i].GetComponent<RectTransform>().position.y;
  156. distance[i] = Mathf.Abs(distReposition[i]);
  157. //Magnifying effect
  158. Vector2 scale = Vector2.Max(minScale, new Vector2(1 / (1 + distance[i] * elementShrinkage.x), (1 / (1 + distance[i] * elementShrinkage.y))));
  159. _arrayOfElements[i].GetComponent<RectTransform>().transform.localScale = new Vector3(scale.x, scale.y, 1f);
  160. }
  161. // detect focused element
  162. float minDistance = Mathf.Min(distance);
  163. int oldFocusedElement = focusedElementIndex;
  164. for (var i = 0; i < _arrayOfElements.Length; i++)
  165. {
  166. _arrayOfElements[i].GetComponent<CanvasGroup>().interactable = !disableUnfocused || minDistance == distance[i];
  167. if (minDistance == distance[i])
  168. {
  169. focusedElementIndex = i;
  170. result = _arrayOfElements[i].GetComponentInChildren<Text>().text;
  171. }
  172. }
  173. if (focusedElementIndex != oldFocusedElement && OnFocusChanged != null)
  174. {
  175. OnFocusChanged.Invoke(focusedElementIndex);
  176. }
  177. if (!UIExtensionsInputManager.GetMouseButton(0))
  178. {
  179. // scroll slowly to nearest element when not dragged
  180. ScrollingElements();
  181. }
  182. // stop scrolling past last element from inertia
  183. if (stopMomentumOnEnd
  184. && (_arrayOfElements[0].GetComponent<RectTransform>().position.y > center.position.y
  185. || _arrayOfElements[_arrayOfElements.Length - 1].GetComponent<RectTransform>().position.y < center.position.y))
  186. {
  187. scrollRect.velocity = Vector2.zero;
  188. }
  189. }
  190. private void ScrollingElements()
  191. {
  192. float newY = Mathf.Lerp(scrollingPanel.anchoredPosition.y, scrollingPanel.anchoredPosition.y + distReposition[focusedElementIndex], Time.deltaTime * 2f);
  193. Vector2 newPosition = new Vector2(scrollingPanel.anchoredPosition.x, newY);
  194. scrollingPanel.anchoredPosition = newPosition;
  195. }
  196. public void SnapToElement(int element)
  197. {
  198. float deltaElementPositionY = elementSize.rect.height * element;
  199. Vector2 newPosition = new Vector2(scrollingPanel.anchoredPosition.x, -deltaElementPositionY);
  200. scrollingPanel.anchoredPosition = newPosition;
  201. }
  202. public void ScrollUp()
  203. {
  204. float deltaUp = elementSize.rect.height / 1.2f;
  205. Vector2 newPositionUp = new Vector2(scrollingPanel.anchoredPosition.x, scrollingPanel.anchoredPosition.y - deltaUp);
  206. scrollingPanel.anchoredPosition = Vector2.Lerp(scrollingPanel.anchoredPosition, newPositionUp, 1);
  207. }
  208. public void ScrollDown()
  209. {
  210. float deltaDown = elementSize.rect.height / 1.2f;
  211. Vector2 newPositionDown = new Vector2(scrollingPanel.anchoredPosition.x, scrollingPanel.anchoredPosition.y + deltaDown);
  212. scrollingPanel.anchoredPosition = newPositionDown;
  213. }
  214. [System.Serializable]
  215. public class IntEvent:UnityEvent<int>
  216. {
  217. }
  218. }
  219. }