UIControlArea.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.EventSystems;
  5. using UnityEngine.UI;
  6. [RequireComponent(typeof(RectTransform))]
  7. [DisallowMultipleComponent]
  8. public abstract class UIControlArea : UIBehaviour
  9. {
  10. protected Canvas m_Canvas;
  11. protected RectTransform m_RectTransform;
  12. protected RectTransform m_ParentRectTransform;
  13. protected UIControlArea[] m_ParentUICAs;
  14. private int m_InitFrameCount = -1;
  15. private bool m_Inited = false;
  16. private Vector3 m_InitWorldPos = Vector3.zero;
  17. private Vector2 m_InitAnchoredPos = Vector2.zero;
  18. public Canvas canvas
  19. {
  20. get
  21. {
  22. if (this.m_Canvas == null)
  23. {
  24. this.CacheCanvas();
  25. }
  26. return this.m_Canvas;
  27. }
  28. }
  29. public RectTransform rectTransform
  30. {
  31. get
  32. {
  33. if (object.ReferenceEquals(this.m_RectTransform, null))
  34. {
  35. this.m_RectTransform = base.GetComponent<RectTransform>();
  36. }
  37. return this.m_RectTransform;
  38. }
  39. }
  40. public RectTransform parentRectTransform
  41. {
  42. get
  43. {
  44. if (object.ReferenceEquals(this.m_ParentRectTransform, null))
  45. {
  46. var parent = rectTransform.parent;
  47. if (parent)
  48. {
  49. this.m_ParentRectTransform = parent as RectTransform;
  50. }
  51. }
  52. return this.m_ParentRectTransform;
  53. }
  54. }
  55. public UIControlArea[] parentUICAs
  56. {
  57. get
  58. {
  59. if (object.ReferenceEquals(m_ParentUICAs, null))
  60. {
  61. // 这种做法逻辑上效率有点低,后续有空的时候在看看怎么优化
  62. m_ParentUICAs = GetComponentsInParent<UIControlArea>();
  63. }
  64. return m_ParentUICAs;
  65. }
  66. }
  67. protected override void Awake()
  68. {
  69. base.Awake();
  70. m_Inited = false;
  71. }
  72. protected override void OnEnable()
  73. {
  74. base.OnEnable();
  75. RefreshInitWorldPos();
  76. StartCoroutine(DelayOneFrameRefreshAdapt());
  77. SafeRectCheck safeRectCheck = SafeRectCheck.Instance;
  78. if (safeRectCheck)
  79. {
  80. safeRectCheck.onChanged.AddListener(OnSafeRectChanged);
  81. }
  82. }
  83. protected override void OnDisable()
  84. {
  85. SafeRectCheck safeRectCheck = SafeRectCheck.Instance;
  86. if (safeRectCheck)
  87. {
  88. safeRectCheck.onChanged.RemoveListener(OnSafeRectChanged);
  89. }
  90. StopCoroutine(DelayOneFrameRefreshAdapt());
  91. LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
  92. base.OnDisable();
  93. }
  94. protected override void OnBeforeTransformParentChanged()
  95. {
  96. base.OnBeforeTransformParentChanged();
  97. LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
  98. }
  99. protected override void OnTransformParentChanged()
  100. {
  101. m_Canvas = null;
  102. m_ParentRectTransform = null;
  103. m_ParentUICAs = null;
  104. // if (!IsActive()) return;
  105. RefreshInitWorldPos();
  106. }
  107. protected override void OnRectTransformDimensionsChange()
  108. {
  109. // if (!IsActive()) return;
  110. RefreshInitWorldPos();
  111. }
  112. protected override void OnCanvasHierarchyChanged()
  113. {
  114. m_Canvas = null;
  115. // if (!IsActive()) return;
  116. RefreshInitWorldPos();
  117. }
  118. private void CacheCanvas()
  119. {
  120. List<Canvas> list = new List<Canvas>();
  121. base.gameObject.GetComponentsInParent(false, list);
  122. if (list.Count > 0)
  123. {
  124. int num = 0;
  125. while (num < list.Count)
  126. {
  127. if (!list[num].isActiveAndEnabled)
  128. {
  129. num++;
  130. continue;
  131. }
  132. m_Canvas = list[num];
  133. break;
  134. }
  135. }
  136. else
  137. {
  138. m_Canvas = null;
  139. }
  140. }
  141. protected virtual void OnSafeRectChanged()
  142. {
  143. RefreshAdapt();
  144. }
  145. public virtual void RefreshAdapt()
  146. {
  147. SafeRectCheck safeRectCheck = SafeRectCheck.Instance;
  148. if (!safeRectCheck) return;
  149. CalcAdapt(safeRectCheck.safeArea, safeRectCheck.orientation, safeRectCheck.screenWidth, safeRectCheck.screenHeight);
  150. }
  151. protected abstract void CalcRect(Rect safeArea, ScreenOrientation orientation, int width, int height, out Vector2 min, out Vector2 max);
  152. private void CalcAdapt(Rect safeArea, ScreenOrientation orientation, int width, int height)
  153. {
  154. if (!canvas || canvas.renderMode == RenderMode.WorldSpace) return;
  155. RectTransform parent = parentRectTransform;
  156. if (!parent) return;
  157. #if !UNITY_2018_4_OR_NEWER
  158. // safeArea 为左上角坐标
  159. safeArea.y = height - safeArea.y - safeArea.height;
  160. #endif
  161. Vector2 min, max;
  162. CalcRect(safeArea, orientation, width, height, out min, out max);
  163. SetArea(parent, min, max, width, height);
  164. }
  165. private void SetArea(RectTransform parent, Vector2 min, Vector2 max, int width, int height)
  166. {
  167. Vector2 minLocal, maxLocal;
  168. Camera camera = (canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera);
  169. if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parent, min, camera, out minLocal))
  170. {
  171. return;
  172. }
  173. if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parent, max, camera, out maxLocal))
  174. {
  175. return;
  176. }
  177. m_Inited = true;
  178. Rect rect = parent.rect;
  179. Vector2 size = rect.size;
  180. Vector2 position = rect.position;
  181. Vector2 anchorMin = rectTransform.anchorMin;
  182. Vector2 anchorMax = rectTransform.anchorMax;
  183. Vector2 pivot = rectTransform.pivot;
  184. Vector2 offsetMin = minLocal - Vector2.Scale(size, anchorMin);
  185. Vector2 offsetMax = maxLocal - Vector2.Scale(size, anchorMax);
  186. Vector2 sizeDelta = offsetMax - offsetMin;
  187. offsetMin = offsetMin - position;
  188. Vector2 anchoredPosition = offsetMin + Vector2.Scale(sizeDelta, pivot);
  189. Vector2 offset = parent.InverseTransformVector(m_InitWorldPos - rectTransform.position);
  190. anchoredPosition = anchoredPosition - offset + (m_InitAnchoredPos - rectTransform.anchoredPosition);
  191. foreach(var parentUICA in parentUICAs)
  192. {
  193. if (parentUICA == this) continue;
  194. // 需要过滤掉父对象中的适配偏移
  195. anchoredPosition += parentUICA.GetAnchoredPositionOffset();
  196. }
  197. rectTransform.anchoredPosition = anchoredPosition;
  198. rectTransform.sizeDelta = sizeDelta;
  199. }
  200. private void RefreshInitWorldPos()
  201. {
  202. if (m_Inited) return;
  203. int curFrameCount = Time.frameCount;
  204. if (m_InitFrameCount == -1)
  205. {
  206. m_InitFrameCount = curFrameCount;
  207. }
  208. else if (m_InitFrameCount != curFrameCount)
  209. {
  210. return;
  211. }
  212. m_InitWorldPos = rectTransform.position;
  213. m_InitAnchoredPos = rectTransform.anchoredPosition;
  214. }
  215. private IEnumerator DelayOneFrameRefreshAdapt()
  216. {
  217. yield return null;
  218. // 在初始界面打开时,需要保持缩放一致,否则计算目标坐标时无法确定准确
  219. while (!canvas || canvas.transform.lossyScale != rectTransform.lossyScale)
  220. {
  221. yield return null;
  222. }
  223. RefreshAdapt();
  224. }
  225. private Vector2 GetAnchoredPositionOffset()
  226. {
  227. return (m_InitAnchoredPos - rectTransform.anchoredPosition);
  228. }
  229. }