InteractionArea.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEngine;
  6. using UnityEngine.UI;
  7. namespace TheraBytes.BetterUi
  8. {
  9. #if UNITY_2018_3_OR_NEWER
  10. [ExecuteAlways]
  11. #else
  12. [ExecuteInEditMode]
  13. #endif
  14. [HelpURL("https://documentation.therabytes.de/better-ui/InteractionArea.html")]
  15. [AddComponentMenu("Better UI/Controls/Interaction Area", 30)]
  16. public class InteractionArea : Graphic, ICanvasRaycastFilter, IResolutionDependency
  17. {
  18. public enum Shape
  19. {
  20. Rectangle,
  21. RoundedRectangle,
  22. Ellipse,
  23. }
  24. public Shape ClickableShape;
  25. [SerializeField]
  26. FloatSizeModifier cornerRadiusFallback = new FloatSizeModifier(5, 0, 1000);
  27. [SerializeField]
  28. FloatSizeConfigCollection cornerRadiusConfigs = new FloatSizeConfigCollection();
  29. public float CurrentCornerRadius { get { return cornerRadiusConfigs.GetCurrentItem(cornerRadiusFallback).LastCalculatedSize; } }
  30. public override void SetMaterialDirty() { /* Do nothing */ }
  31. public override void SetVerticesDirty() { /* Do nothing */ }
  32. protected override void OnPopulateMesh(VertexHelper vh) { vh.Clear(); }
  33. protected override void OnEnable()
  34. {
  35. base.OnEnable();
  36. UpdateCornerRadius();
  37. }
  38. public void OnResolutionChanged()
  39. {
  40. UpdateCornerRadius();
  41. }
  42. public void UpdateCornerRadius()
  43. {
  44. cornerRadiusConfigs.GetCurrentItem(cornerRadiusFallback).CalculateSize(this);
  45. }
  46. public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
  47. {
  48. Vector2 local;
  49. RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, sp, eventCamera, out local);
  50. Rect rect = rectTransform.rect;
  51. // Convert to have lower left corner as reference point.
  52. local.x += rectTransform.pivot.x * rect.width;
  53. local.y += rectTransform.pivot.y * rect.height;
  54. switch (ClickableShape)
  55. {
  56. case Shape.Rectangle:
  57. return true;
  58. case Shape.RoundedRectangle:
  59. {
  60. float r = Mathf.Min(CurrentCornerRadius, Mathf.Min(0.5f * rect.width, 0.5f * rect.height));
  61. // simple inside check
  62. if ((local.x >= r && local.x <= rect.width - r) ||
  63. (local.y >= r && local.y <= rect.height - r))
  64. return true;
  65. float a = 0;
  66. float b = 0;
  67. if (local.x < r) // left
  68. {
  69. a = r - local.x;
  70. }
  71. else if (local.x > rect.width - r) // right
  72. {
  73. a = local.x - (rect.width - r);
  74. }
  75. if (local.y < r) // lower
  76. {
  77. b = r - local.y;
  78. }
  79. else if (local.y > rect.height - r) // upper
  80. {
  81. b = local.y - (rect.height - r);
  82. }
  83. return a * a + b * b <= r * r;
  84. }
  85. case Shape.Ellipse:
  86. {
  87. float r = 0.5f * Mathf.Max(rect.width, rect.height);
  88. float aspect = rect.width / rect.height;
  89. float a = local.x - r;
  90. float b = local.y * aspect - r;
  91. return a * a + b * b <= r * r;
  92. }
  93. default: throw new NotImplementedException();
  94. }
  95. }
  96. void OnDrawGizmosSelected()
  97. {
  98. Gizmos.color = (raycastTarget && this.isActiveAndEnabled)
  99. ? Color.green
  100. : 0.5f * Color.green;
  101. DrawGizmos();
  102. }
  103. void OnDrawGizmos()
  104. {
  105. if (!raycastTarget || !this.isActiveAndEnabled)
  106. return;
  107. Gizmos.color = Color.gray;
  108. DrawGizmos();
  109. }
  110. void DrawGizmos()
  111. {
  112. Vector3[] corners = new Vector3[4];
  113. rectTransform.GetWorldCorners(corners);
  114. Vector3 zero = corners[0];
  115. Vector3 up = corners[1];
  116. Vector3 right = corners[3];
  117. switch (ClickableShape)
  118. {
  119. case Shape.Rectangle:
  120. {
  121. Vector3 a = Transpose(new Vector2(0, 0), zero, up, right);
  122. Vector3 b = Transpose(new Vector2(1, 0), zero, up, right);
  123. Vector3 c = Transpose(new Vector2(1, 1), zero, up, right);
  124. Vector3 d = Transpose(new Vector2(0, 1), zero, up, right);
  125. Gizmos.DrawLine(a, b);
  126. Gizmos.DrawLine(b, c);
  127. Gizmos.DrawLine(c, d);
  128. Gizmos.DrawLine(d, a);
  129. }
  130. break;
  131. case Shape.RoundedRectangle:
  132. {
  133. float w = rectTransform.rect.width;
  134. float h = rectTransform.rect.height;
  135. float radius = Mathf.Min(CurrentCornerRadius, Mathf.Min(0.5f * w, 0.5f * h));
  136. float rX = radius / w;
  137. float rY = radius / h;
  138. Vector3 t1 = Transpose(new Vector2(rX, 0), zero, up, right);
  139. Vector3 t2 = Transpose(new Vector2(1 - rX, 0), zero, up, right);
  140. Gizmos.DrawLine(t1, t2);
  141. Vector3 l1 = Transpose(new Vector2(0, rY), zero, up, right);
  142. Vector3 l2 = Transpose(new Vector2(0, 1 - rY), zero, up, right);
  143. Gizmos.DrawLine(l1, l2);
  144. Vector3 b1 = Transpose(new Vector2(rX, 1), zero, up, right);
  145. Vector3 b2 = Transpose(new Vector2(1 - rX, 1), zero, up, right);
  146. Gizmos.DrawLine(b1, b2);
  147. Vector3 r1 = Transpose(new Vector2(1, rY), zero, up, right);
  148. Vector3 r2 = Transpose(new Vector2(1, 1 - rY), zero, up, right);
  149. Gizmos.DrawLine(r1, r2);
  150. const int segments = 5;
  151. float step = (0.5f * Mathf.PI) / (segments );
  152. for (int i = 0; i < segments; i++)
  153. {
  154. float angleA = step * i;
  155. float angleB = step * (i + 1);
  156. Vector2 a = new Vector2(rX * Mathf.Cos(angleA), rY * Mathf.Sin(angleA));
  157. Vector2 b = new Vector2(rX * Mathf.Cos(angleB), rY * Mathf.Sin(angleB));
  158. // left bottom
  159. Vector2 center = new Vector2(rX, rY);
  160. Vector3 start = Transpose(center - a, zero, up, right);
  161. Vector3 end = Transpose(center - b, zero, up, right);
  162. Gizmos.DrawLine(start, end);
  163. // right top
  164. center = new Vector2(1 - rX, 1 - rY);
  165. start = Transpose(center + a, zero, up, right);
  166. end = Transpose(center + b, zero, up, right);
  167. Gizmos.DrawLine(start, end);
  168. // right bottom
  169. center = new Vector2(1 - rX, rY);
  170. start = Transpose(new Vector2(center.x + a.x, center.y - a.y), zero, up, right);
  171. end = Transpose(new Vector2(center.x + b.x, center.y - b.y), zero, up, right);
  172. Gizmos.DrawLine(start, end);
  173. // left top
  174. center = new Vector2(rX, 1 - rY);
  175. start = Transpose(new Vector2(center.x - a.x, center.y + a.y), zero, up, right);
  176. end = Transpose(new Vector2(center.x - b.x, center.y + b.y), zero, up, right);
  177. Gizmos.DrawLine(start, end);
  178. }
  179. }
  180. break;
  181. case Shape.Ellipse:
  182. {
  183. const int segments = 20;
  184. float step = (2 * Mathf.PI) / segments;
  185. for (int i = 0; i < segments; i++)
  186. {
  187. float angleA = step * i;
  188. float angleB = step * ((i + 1) % segments);
  189. Vector2 a = new Vector2(0.5f + 0.5f * Mathf.Cos(angleA), 0.5f + 0.5f * Mathf.Sin(angleA));
  190. Vector2 b = new Vector2(0.5f + 0.5f * Mathf.Cos(angleB), 0.5f + 0.5f * Mathf.Sin(angleB));
  191. Vector3 start = Transpose(a, zero, up, right);
  192. Vector3 end = Transpose(b, zero, up, right);
  193. Gizmos.DrawLine(start, end);
  194. }
  195. }
  196. break;
  197. default: throw new NotImplementedException();
  198. }
  199. }
  200. Vector3 Transpose(Vector2 relativePosition, Vector3 zeroPoint, Vector3 upperPoint, Vector3 rightPoint)
  201. {
  202. Vector3 x = relativePosition.x * (rightPoint - zeroPoint);
  203. Vector3 y = relativePosition.y * (upperPoint - zeroPoint);
  204. return zeroPoint + x + y;
  205. }
  206. }
  207. }