1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
| [ExecuteAlways, DisallowMultipleComponent, RequireComponent(typeof(RectTransform))] public class CutRectSetter : UIBehaviour, IClipper { [SerializeField] private Canvas _canvas; [SerializeField] private MaskableGraphic _clipTarget;
private bool m_shouldRecalculateClipRects = false; private Rect m_prevClipRect = default; private bool m_hasClipped = false;
public void PerformClipping() { if (ReferenceEquals(_canvas, null) || null == _clipTarget) { return; }
RectTransform selfRectTransform = transform as RectTransform; RectTransform targetTectTransform = _clipTarget.transform as RectTransform; if (!IsInsideRect(selfRectTransform, targetTectTransform)) { CancelClip(); m_prevClipRect = default; m_shouldRecalculateClipRects = false; return; }
Rect currentRect = selfRectTransform.rect; Vector2 selfPivot = selfRectTransform.pivot; currentRect.position = SwitchToRectTransform(selfRectTransform, targetTectTransform); Vector2 delta = new Vector2(-currentRect.width * selfPivot.x, -currentRect.height * selfPivot.y); currentRect.position += delta;
m_shouldRecalculateClipRects = m_shouldRecalculateClipRects || m_prevClipRect != currentRect; if (m_shouldRecalculateClipRects) { _clipTarget.SetClipRect(default, true); _clipTarget.SetClipRect(currentRect, true); _clipTarget.Cull(currentRect, true); m_prevClipRect = currentRect; m_shouldRecalculateClipRects = false; } }
[ContextMenu(nameof(CancelClip))] private void CancelClip() { _clipTarget.SetClipRect(default, true); m_hasClipped = false; }
protected override void OnEnable() { base.OnEnable(); m_shouldRecalculateClipRects = true; ClipperRegistry.Register(this); MaskUtilities.Notify2DMaskStateChanged(this); }
protected override void OnDisable() { base.OnDisable(); ClipperRegistry.Unregister(this); MaskUtilities.Notify2DMaskStateChanged(this); CancelClip(); }
protected override void OnTransformParentChanged() { m_shouldRecalculateClipRects = true; }
protected override void OnCanvasHierarchyChanged() { m_shouldRecalculateClipRects = true; }
#region some utils
private readonly Vector2 RECT_TOP = new Vector2(0.5f, 1f); private readonly Vector2 RECT_BOTTOM = new Vector2(0.5f, 0f); private readonly Vector2 RECT_LEFT = new Vector2(0f, 0.5f); private readonly Vector2 RECT_RIGHT = new Vector2(1f, 0.5f); private readonly Vector2 RECT_CENTER = new Vector2(0.5f, 0.5f);
private readonly Vector2 RECT_BOTTOM_LEFT = new Vector2(0f, 0f); private readonly Vector2 RECT_BOTTOM_RIGHT = new Vector2(1f, 0f); private readonly Vector2 RECT_TOP_LEFT = new Vector2(0f, 1f); private readonly Vector2 RECT_TOP_RIGHT = new Vector2(1f, 1f);
private Vector2 SwitchToRectTransform(RectTransform from, RectTransform to) { Vector2 screenP = RectTransformUtility.WorldToScreenPoint(null, from.position); RectTransformUtility.ScreenPointToLocalPointInRectangle(to, screenP, null, out Vector2 localPoint); return localPoint; }
private bool IsInsideRect(RectTransform rectTransform, RectTransform outterRect) { Vector3 checkWorldPoint = RectOffsetToWorld(rectTransform, RECT_BOTTOM_LEFT); bool bottomLeftOut = !RectTransformUtility.RectangleContainsScreenPoint(outterRect, checkWorldPoint); checkWorldPoint = RectOffsetToWorld(rectTransform, RECT_BOTTOM_RIGHT); bool bottomRightOut = !RectTransformUtility.RectangleContainsScreenPoint(outterRect, checkWorldPoint); checkWorldPoint = RectOffsetToWorld(rectTransform, RECT_TOP_LEFT); bool topLeftOut = !RectTransformUtility.RectangleContainsScreenPoint(outterRect, checkWorldPoint); checkWorldPoint = RectOffsetToWorld(rectTransform, RECT_TOP_RIGHT); bool topRightOut = !RectTransformUtility.RectangleContainsScreenPoint(outterRect, checkWorldPoint);
return !(bottomLeftOut || bottomRightOut || topLeftOut || topRightOut); }
private Vector3 RectOffsetToWorld(RectTransform rectTransform, Vector2 normalizedRectPosition) { Rect selfRect = rectTransform.rect; Vector2 localposition = Vector2.zero; if (RECT_CENTER == normalizedRectPosition) { localposition = selfRect.center; } else if (RECT_TOP == normalizedRectPosition) { localposition = new Vector2(selfRect.center.x, selfRect.yMax); } else if (RECT_BOTTOM == normalizedRectPosition) { localposition = new Vector2(selfRect.center.x, selfRect.yMin); } else if (RECT_LEFT == normalizedRectPosition) { localposition = new Vector2(selfRect.xMin, selfRect.center.y); } else if (RECT_RIGHT == normalizedRectPosition) { localposition = new Vector2(selfRect.xMax, selfRect.center.y); } else if (RECT_TOP_LEFT == normalizedRectPosition) { localposition = new Vector2(selfRect.xMin, selfRect.yMax); } else if (RECT_TOP_RIGHT == normalizedRectPosition) { localposition = selfRect.max; } else if (RECT_BOTTOM_LEFT == normalizedRectPosition) { localposition = selfRect.min; } else if (RECT_BOTTOM_RIGHT == normalizedRectPosition) { localposition = new Vector2(selfRect.xMax, selfRect.yMin); } else { Debug.LogError($"{normalizedRectPosition} is not a normalized rect position"); }
return rectTransform.TransformPoint(localposition); }
#endregion
}
|