using System; using System.Collections.Generic; using UnityEngine.EventSystems; namespace UnityEngine.UI { [AddComponentMenu("UI/Rect Mask 2D", 13)] [ExecuteInEditMode] [DisallowMultipleComponent] [RequireComponent(typeof(RectTransform))] public class RectMask2D : UIBehaviour, IClipper, ICanvasRaycastFilter { [NonSerialized] private readonly RectangularVertexClipper m_VertexClipper = new RectangularVertexClipper(); [NonSerialized] private RectTransform m_RectTransform; // 可裁剪的Graphics [NonSerialized] private HashSet m_ClipTargets = new HashSet(); [NonSerialized] private bool m_ShouldRecalculateClipRects; // 父节点的所有RectMask2D,用来算交集 [NonSerialized] private List m_Clippers = new List(); [NonSerialized] private Rect m_LastClipRectCanvasSpace; [NonSerialized] private bool m_LastValidClipRect; [NonSerialized] private bool m_ForceClip; public Rect canvasRect { get { Canvas canvas = null; var list = ListPool.Get(); gameObject.GetComponentsInParent(false, list); if (list.Count > 0) canvas = list[list.Count - 1]; ListPool.Release(list); return m_VertexClipper.GetCanvasRect(rectTransform, canvas); } } public RectTransform rectTransform { get { return m_RectTransform ?? (m_RectTransform = GetComponent()); } } protected RectMask2D() {} protected override void OnEnable() { base.OnEnable(); m_ShouldRecalculateClipRects = true; ClipperRegistry.Register(this); MaskUtilities.Notify2DMaskStateChanged(this); } protected override void OnDisable() { // we call base OnDisable first here // as we need to have the IsActive return the // correct value when we notify the children // that the mask state has changed. base.OnDisable(); m_ClipTargets.Clear(); m_Clippers.Clear(); ClipperRegistry.Unregister(this); MaskUtilities.Notify2DMaskStateChanged(this); } #if UNITY_EDITOR protected override void OnValidate() { base.OnValidate(); m_ShouldRecalculateClipRects = true; if (!IsActive()) return; MaskUtilities.Notify2DMaskStateChanged(this); } #endif public virtual bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera) { if (!isActiveAndEnabled) return true; return RectTransformUtility.RectangleContainsScreenPoint(rectTransform, sp, eventCamera); } public virtual void PerformClipping() { //TODO See if an IsActive() test would work well here or whether it might cause unexpected side effects (re case 776771) // if the parents are changed // or something similar we // do a recalculate here if (m_ShouldRecalculateClipRects) { MaskUtilities.GetRectMasksForClip(this, m_Clippers); m_ShouldRecalculateClipRects = false; } // 裁剪 // get the compound rects from // the clippers that are valid bool validRect = true; Rect clipRect = Clipping.FindCullAndClipWorldRect(m_Clippers, out validRect); bool clipRectChanged = clipRect != m_LastClipRectCanvasSpace; if (clipRectChanged || m_ForceClip) { foreach (IClippable clipTarget in m_ClipTargets) clipTarget.SetClipRect(clipRect, validRect); m_LastClipRectCanvasSpace = clipRect; m_LastValidClipRect = validRect; } // 剔除 foreach (IClippable clipTarget in m_ClipTargets) { var maskable = clipTarget as MaskableGraphic; if (maskable != null && !maskable.canvasRenderer.hasMoved && !clipRectChanged) continue; clipTarget.Cull(m_LastClipRectCanvasSpace, m_LastValidClipRect); } } public void AddClippable(IClippable clippable) { if (clippable == null) return; m_ShouldRecalculateClipRects = true; if (!m_ClipTargets.Contains(clippable)) m_ClipTargets.Add(clippable); m_ForceClip = true; } public void RemoveClippable(IClippable clippable) { if (clippable == null) return; m_ShouldRecalculateClipRects = true; clippable.SetClipRect(new Rect(), false); m_ClipTargets.Remove(clippable); m_ForceClip = true; } protected override void OnTransformParentChanged() { base.OnTransformParentChanged(); m_ShouldRecalculateClipRects = true; } protected override void OnCanvasHierarchyChanged() { base.OnCanvasHierarchyChanged(); m_ShouldRecalculateClipRects = true; } #region UGUI_DEBUG Vector3[] cornors = new Vector3[4]; void OnDrawGizmos() { Rect rect = rectTransform.rect; Gizmos.color = Color.red; rectTransform.GetWorldCorners(cornors); for (int i = 0; i < cornors.Length; ++i) { Gizmos.DrawLine(cornors[i], cornors[(i + 1) % cornors.Length]); } } #endregion } }