using UnityEngine; using UnityEngine.UI; using UnityEngine.Events; using System.Collections.Generic; using UnityEngine.EventSystems; /// /// 用于在Ui中切割Renderer的组件,类似RectMask2d; /// 暂时未处理Mask嵌套的情况 /// [DisallowMultipleComponent] [RequireComponent(typeof(RectTransform))] public class UICameraMask : UIBehaviour, IClipper { private RectTransform rectTransform; private bool m_ShouldRecalculateClipRects; private List maskableList; private Vector4 clipArea; protected override void Awake() { base.Awake(); rectTransform = (RectTransform)transform; maskableList = new List(); } protected override void OnEnable() { base.OnEnable(); m_ShouldRecalculateClipRects = true; ClipperRegistry.Register(this); } protected override void OnDisable() { base.OnDisable(); maskableList.Clear(); ClipperRegistry.Unregister(this); } // 暂时使用低端的transform.hasChanged Api来追踪Transform状态变化。 // 用ClipperRegistry获得Clipping流程时间点。 public virtual void PerformClipping() { if (IsTransformDirty(rectTransform)) m_ShouldRecalculateClipRects = true; if (m_ShouldRecalculateClipRects) { m_ShouldRecalculateClipRects = false; ResetClipArea(); } } public void AddClippable(UICameraMaskable clippable) { if (clippable == null) return; if (!maskableList.Contains(clippable)) maskableList.Add(clippable); SetClipForOne(clippable); } public void RemoveClippable(UICameraMaskable clippable) { if (clippable == null) return; var index = maskableList.IndexOf(clippable); if (index >= 0) { maskableList.RemoveAt(index); RemoveClipForOne(clippable); } } private void ResetClipArea() { var corners = new Vector3[4]; rectTransform.GetWorldCorners(corners); for (int i = 0; i < corners.Length; i++) { corners[i] = RectTransformUtility.WorldToScreenPoint(GetWorldCamera(), corners[i]); corners[i].x = corners[i].x / Screen.width * 2f - 1f; corners[i].y = corners[i].y / Screen.height * 2f - 1f; } clipArea = new Vector4(corners[0].x, corners[0].y, corners[2].x, corners[2].y); for (int i = 0; i < maskableList.Count; i++) SetClipForOne(maskableList[i]); } private void SetClipForOne(UICameraMaskable clippable) { clippable.SetClipRect(clipArea); } private Camera GetWorldCamera() { var uiManager = UIManager.Instance(); if (uiManager) return uiManager.UICamera; else return Camera.main; } // 暂时使用transform.hasChanged api来检测Mask是否有变化 Rect lastRect; private bool IsTransformDirty(RectTransform rectTransform) { bool result = false; if (rectTransform.hasChanged) { rectTransform.hasChanged = false; result = true; } else if (rectTransform.rect != lastRect) result = true; if (result) lastRect = rectTransform.rect; return result; } public static void RemoveClipForOne(UICameraMaskable clippable) { clippable.SetClipRect(new Vector4(-1f, -1f, 1f, 1f)); } }