using System.Collections.Generic; using UnityEngine.Pool; namespace UnityEngine.UI { /// /// Mask related utility class. This class provides masking-specific utility functions. /// public class MaskUtilities { /// /// Notify all IClippables under the given component that they need to recalculate clipping. /// /// The object thats changed for whose children should be notified. public static void Notify2DMaskStateChanged(Component mask) { var components = ListPool.Get(); mask.GetComponentsInChildren(components); for (var i = 0; i < components.Count; i++) { if (components[i] == null || components[i].gameObject == mask.gameObject) continue; var toNotify = components[i] as IClippable; if (toNotify != null) toNotify.RecalculateClipping(); } ListPool.Release(components); } /// /// Notify all IMaskable under the given component that they need to recalculate masking. /// /// The object thats changed for whose children should be notified. public static void NotifyStencilStateChanged(Component mask) { var components = ListPool.Get(); mask.GetComponentsInChildren(components); for (var i = 0; i < components.Count; i++) { if (components[i] == null || components[i].gameObject == mask.gameObject) continue; var toNotify = components[i] as IMaskable; if (toNotify != null) toNotify.RecalculateMasking(); } ListPool.Release(components); } /// /// Find a root Canvas. /// /// Transform to start the search at going up the hierarchy. /// Finds either the most root canvas, or the first canvas that overrides sorting. public static Transform FindRootSortOverrideCanvas(Transform start) { var canvasList = ListPool.Get(); start.GetComponentsInParent(false, canvasList); Canvas canvas = null; for (int i = 0; i < canvasList.Count; ++i) { canvas = canvasList[i]; // We found the canvas we want to use break if (canvas.overrideSorting) break; } ListPool.Release(canvasList); return canvas != null ? canvas.transform : null; } /// /// Find the stencil depth for a given element. /// /// The starting transform to search. /// Where the search of parents should stop /// What the proper stencil buffer index should be. public static int GetStencilDepth(Transform transform, Transform stopAfter) { var depth = 0; if (transform == stopAfter) return depth; var t = transform.parent; var components = ListPool.Get(); while (t != null) { t.GetComponents(components); for (var i = 0; i < components.Count; ++i) { if (components[i] != null && components[i].MaskEnabled() && components[i].graphic.IsActive()) { ++depth; break; } } if (t == stopAfter) break; t = t.parent; } ListPool.Release(components); return depth; } /// /// Helper function to determine if the child is a descendant of father or is father. /// /// The transform to compare against. /// The starting transform to search up the hierarchy. /// Is child equal to father or is a descendant. public static bool IsDescendantOrSelf(Transform father, Transform child) { if (father == null || child == null) return false; if (father == child) return true; while (child.parent != null) { if (child.parent == father) return true; child = child.parent; } return false; } /// /// Find the correct RectMask2D for a given IClippable. /// /// Clippable to search from. /// The Correct RectMask2D public static RectMask2D GetRectMaskForClippable(IClippable clippable) { List rectMaskComponents = ListPool.Get(); List canvasComponents = ListPool.Get(); RectMask2D componentToReturn = null; clippable.gameObject.GetComponentsInParent(false, rectMaskComponents); if (rectMaskComponents.Count > 0) { for (int rmi = 0; rmi < rectMaskComponents.Count; rmi++) { componentToReturn = rectMaskComponents[rmi]; if (componentToReturn.gameObject == clippable.gameObject) { componentToReturn = null; continue; } if (!componentToReturn.isActiveAndEnabled) { componentToReturn = null; continue; } clippable.gameObject.GetComponentsInParent(false, canvasComponents); for (int i = canvasComponents.Count - 1; i >= 0; i--) { if (!IsDescendantOrSelf(canvasComponents[i].transform, componentToReturn.transform) && canvasComponents[i].overrideSorting) { componentToReturn = null; break; } } break; } } ListPool.Release(rectMaskComponents); ListPool.Release(canvasComponents); return componentToReturn; } /// /// Search for all RectMask2D that apply to the given RectMask2D (includes self). /// /// Starting clipping object. /// The list of Rect masks public static void GetRectMasksForClip(RectMask2D clipper, List masks) { masks.Clear(); List canvasComponents = ListPool.Get(); List rectMaskComponents = ListPool.Get(); clipper.transform.GetComponentsInParent(false, rectMaskComponents); if (rectMaskComponents.Count > 0) { clipper.transform.GetComponentsInParent(false, canvasComponents); for (int i = rectMaskComponents.Count - 1; i >= 0; i--) { if (!rectMaskComponents[i].IsActive()) continue; bool shouldAdd = true; for (int j = canvasComponents.Count - 1; j >= 0; j--) { if (!IsDescendantOrSelf(canvasComponents[j].transform, rectMaskComponents[i].transform) && canvasComponents[j].overrideSorting) { shouldAdd = false; break; } } if (shouldAdd) masks.Add(rectMaskComponents[i]); } } ListPool.Release(rectMaskComponents); ListPool.Release(canvasComponents); } } }