zero3growlithe

Unity UI - optimized UI grid

Apr 6th, 2017
132
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.22 KB | None | 0 0
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5.  
  6. // EXTENDS BaseMenuGrid class, it basically only enables elements that fit
  7. // on the screen and disables everything else to save on UI rendering times.
  8.  
  9. // USAGE
  10. // simply inherit after this class rather than BaseMenuGrid and it will take effect
  11.  
  12. // HIERARCHY LAYOUT
  13. // video showing compatible UI hierarchy layout: https://www.youtube.com/watch?v=lUun2xW6FJ4
  14.  
  15. public class OptimizedMenuGrid : BaseMenuGrid {
  16.  
  17. //*** ATTRIBUTES ***//
  18.     [Header("[ Settings ]")]
  19.     [SerializeField]
  20.     private Direction gridDirection; // is this a horizontal or vertical grid? Double direction not supported
  21.  
  22.     // INTERNAL
  23.     private List<RectTransform> cachedGridElements = new List<RectTransform>();
  24.  
  25.     // CONSTANTS
  26.     private const int ADDITIONAL_GRID_COUNT = 2;
  27.  
  28. //*** PROPERTIES ***//
  29.     // SETTINGS
  30.     private Direction GridDirection {
  31.         get {return gridDirection;}
  32.     }
  33.  
  34.     // CACHED REFERENCES
  35.     protected RectTransform LayoutListGroup {
  36.         get {return TargetScrollRect != null ? TargetScrollRect.content : null;}
  37.     }
  38.     protected List<RectTransform> CachedGridElements {
  39.         get {return cachedGridElements;}
  40.     }
  41.  
  42.     protected ScrollRect TargetScrollRect {get; set;}
  43.     protected RectTransform ScrollWindow {get; set;}
  44.  
  45.     protected ContentSizeFitter TargetSizeFitter {get; set;}
  46.     protected LayoutElement TargetLayoutElement {get; set;}
  47.     protected LayoutGroup TargetLayoutGroup {get; set;}
  48.  
  49.     // VARIABLES
  50.     private float GridSpacing {get; set;}
  51.     private float GridElementSize {get; set;}
  52.     private float ScrollWindowSize {get; set;}
  53.  
  54.     private int MaxGridDisplayCount {get; set;}
  55.     private int CurrentGridOffsetIndex {get; set;}
  56.  
  57.     // not a property due to ref
  58.     private Coroutine DisableComponentsCoroutine;
  59.  
  60. //*** METHODS - PUBLIC ***//
  61.     public override void SetUpGridElement (GameObject element)
  62.     {
  63.         element.transform.SetParent(TargetGrid);
  64.         element.transform.localScale = Vector3.one;
  65.  
  66.         CachedGridElements.Add(element.GetComponent<RectTransform>());
  67.  
  68.         // element.SetActive(CachedGridElements.Count <= MaxGridDisplayCount);
  69.         element.SetActive(true);
  70.     }
  71.  
  72.     public override void ClearGrid ()
  73.     {
  74.         base.ClearGrid();
  75.  
  76.         CachedGridElements.Clear();
  77.  
  78.         EnableLayoutComponents();
  79.         ExecuteRunMethodOnEndOfFrame(ref DisableComponentsCoroutine, DisableLayoutComponents);
  80.         ExecuteRunMethodOnEndOfFrame(UpdateVisibleGridElements);
  81.     }
  82.  
  83. //*** METHODS - PROTECTED ***//
  84.     protected override void Awake ()
  85.     {
  86.         base.Awake();
  87.  
  88.         CacheReferences();
  89.     }
  90.  
  91.     protected override void Start ()
  92.     {
  93.         base.Start();
  94.     }
  95.    
  96.     protected override void Update ()
  97.     {
  98.         base.Update();
  99.  
  100.         HandleReusableGridElements();
  101.     }
  102.    
  103.     protected override void OnDestroy ()
  104.     {
  105.         base.OnDestroy();
  106.     }
  107.  
  108. //*** METHODS - PRIVATE ***//
  109.     private void HandleReusableGridElements ()
  110.     {
  111.         // check main references
  112.         bool referencesAreIncorrect = (TargetScrollRect == null || LayoutListGroup == null || ScrollWindow == null);
  113.  
  114.         if (referencesAreIncorrect == true || CachedGridElements.Count < MaxGridDisplayCount)
  115.         {
  116.             return;
  117.         }
  118.  
  119.         // depending on selected scroll direction get current offset
  120.         int lastIndex = (int)Mathf.Clamp(CurrentGridOffsetIndex + MaxGridDisplayCount, 0, CachedGridElements.Count - 1);
  121.  
  122.         float firstOffset = GetElementOffset(CachedGridElements[CurrentGridOffsetIndex]);
  123.         float lastOffset = GetElementOffset(CachedGridElements[lastIndex]);
  124.  
  125.         firstOffset *= (GridDirection == Direction.HORIZONTAL) ? -1f : 1f;
  126.         lastOffset *= (GridDirection == Direction.HORIZONTAL) ? -1f : 1f;
  127.  
  128.         // swap elements when enough offset is given
  129.         if (lastOffset < -GridElementSize * 2f && CurrentGridOffsetIndex > 0)
  130.         {
  131.             MoveOffsetIndexInDirection(-1);
  132.  
  133.             UpdateVisibleGridElements();
  134.         }
  135.         else if (firstOffset > GridElementSize * 2f && CurrentGridOffsetIndex < CachedGridElements.Count - MaxGridDisplayCount)
  136.         {
  137.             MoveOffsetIndexInDirection(1);
  138.  
  139.             UpdateVisibleGridElements();
  140.         }
  141.     }
  142.  
  143.     private void MoveOffsetIndexInDirection (int direction)
  144.     {
  145.         CurrentGridOffsetIndex += direction;
  146.     }
  147.  
  148.     private void UpdateVisibleGridElements ()
  149.     {
  150.         for (int i = 0; i < CachedGridElements.Count; i++)
  151.         {
  152.             bool isActive = (i >= CurrentGridOffsetIndex) && (i < CurrentGridOffsetIndex + MaxGridDisplayCount);
  153.  
  154.             CachedGridElements[i].gameObject.SetActive(isActive);
  155.         }
  156.     }
  157.  
  158.     private void EnableLayoutComponents ()
  159.     {
  160.         TargetSizeFitter.enabled = true;
  161.         TargetLayoutGroup.enabled = true;
  162.     }
  163.  
  164.     private void DisableLayoutComponents ()
  165.     {
  166.         TargetSizeFitter.enabled = false;
  167.         TargetLayoutGroup.enabled = false;
  168.  
  169.         AdjustGridSize();
  170.     }
  171.  
  172.     private void CacheReferences ()
  173.     {
  174.         TargetScrollRect = TargetGrid.GetComponentInParent<ScrollRect>();
  175.         ScrollWindow = TargetScrollRect.GetComponent<RectTransform>();
  176.  
  177.         TargetSizeFitter = TargetGrid.GetComponent<ContentSizeFitter>();
  178.         TargetLayoutElement = GridElement.GetComponent<LayoutElement>();
  179.         TargetLayoutGroup = TargetGrid.GetComponent<LayoutGroup>();
  180.        
  181.         GridSpacing =
  182.             (GridDirection == Direction.HORIZONTAL) ?
  183.             ((HorizontalLayoutGroup)TargetLayoutGroup).spacing :
  184.             ((VerticalLayoutGroup)TargetLayoutGroup).spacing;
  185.         GridElementSize =
  186.             (GridDirection == Direction.HORIZONTAL) ?
  187.             TargetLayoutElement.preferredWidth :
  188.             TargetLayoutElement.preferredHeight;
  189.         ScrollWindowSize =
  190.             (GridDirection == Direction.HORIZONTAL) ?
  191.             ScrollWindow.rect.width :
  192.             ScrollWindow.rect.height;
  193.  
  194.         MaxGridDisplayCount = GetMaxGridDisplayCount();
  195.     }
  196.  
  197.     private void AdjustGridSize ()
  198.     {
  199.         // calculate grid length
  200.         float lenght =
  201.             CachedGridElements.Count * GridElementSize +
  202.             (CachedGridElements.Count - 1) * GridSpacing;
  203.  
  204.         lenght = Mathf.Clamp(lenght, 0, Mathf.Infinity);
  205.  
  206.         // set new size depending on grid direction
  207.         Vector2 newSize = TargetGrid.sizeDelta;
  208.  
  209.         if (GridDirection == Direction.HORIZONTAL)
  210.         {
  211.             newSize.x = lenght;
  212.         }
  213.         else
  214.         {
  215.             newSize.y = lenght;
  216.         }
  217.  
  218.         // apply final size
  219.         TargetGrid.sizeDelta = newSize;
  220.     }
  221.  
  222.     private int GetMaxGridDisplayCount ()
  223.     {
  224.         float overallLength = 0;
  225.  
  226.         int maxCount = 0;
  227.        
  228.         while (overallLength < ScrollWindowSize)
  229.         {
  230.             maxCount++;
  231.             overallLength = maxCount * (GridElementSize + GridSpacing);
  232.         }
  233.  
  234.         return maxCount + ADDITIONAL_GRID_COUNT;
  235.     }
  236.  
  237.     private float GetElementOffset (RectTransform selection)
  238.     {
  239.         switch (GridDirection)
  240.         {
  241.             case Direction.VERTICAL:
  242.                 return GetVerticalElementOffset(selection);
  243.             case Direction.HORIZONTAL:
  244.                 return GetHorizontalElementOffset(selection);
  245.             default:
  246.                 return 0.0f;
  247.         }
  248.     }
  249.  
  250.     private float GetVerticalElementOffset (RectTransform selection)
  251.     {
  252.         // move the current scroll rect to correct position
  253.         float selectionPosition = -selection.anchoredPosition.y;
  254.  
  255.         float elementHeight = selection.rect.height;
  256.         float maskHeight = ScrollWindow.rect.height;
  257.         float listAnchorPosition = LayoutListGroup.anchoredPosition.y;
  258.  
  259.         // get the element offset value depending on the cursor move direction
  260.         float offlimitsValue = GetScrollOffset(selectionPosition, listAnchorPosition, elementHeight, maskHeight);
  261.  
  262.         // return offlimitsValue / LayoutListGroup.rect.height;
  263.         return offlimitsValue;
  264.     }
  265.  
  266.     private float GetHorizontalElementOffset (RectTransform selection)
  267.     {
  268.         // move the current scroll rect to correct position
  269.         float selectionPosition = selection.anchoredPosition.x;
  270.  
  271.         float elementWidth = selection.rect.width;
  272.         float maskWidth = ScrollWindow.rect.width;
  273.         float listAnchorPosition = -LayoutListGroup.anchoredPosition.x;
  274.  
  275.         // get the element offset value depending on the cursor move direction
  276.         float offlimitsValue = -GetScrollOffset(selectionPosition, listAnchorPosition, elementWidth, maskWidth);
  277.  
  278.         // return offlimitsValue / LayoutListGroup.rect.width;
  279.         return offlimitsValue;
  280.     }
  281.  
  282.     private float GetScrollOffset (float position, float listAnchorPosition, float targetLength, float maskLength)
  283.     {
  284.         if (position < listAnchorPosition)
  285.         {
  286.             return listAnchorPosition - position;
  287.         }
  288.         else if (position + targetLength > listAnchorPosition + maskLength)
  289.         {
  290.             return (listAnchorPosition + maskLength) - (position + targetLength);
  291.         }
  292.  
  293.         return 0;
  294.     }
  295.  
  296. //*** ENUMS ***//
  297.     public enum Direction
  298.     {
  299.         VERTICAL,
  300.         HORIZONTAL
  301.     }
  302.  
  303. }
Advertisement
Add Comment
Please, Sign In to add comment