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 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
| namespace UnityEngine.UI { [RequireComponent(typeof(CanvasRenderer))] public partial class RaycastOnlyPolygonGraphic : MaskableGraphic, ICanvasRaycastFilter { [SerializeField] private List<Vector2> m_innerPoints;
public IReadOnlyList<Vector2> PointList => m_innerPoints; public int PointCount => null == m_innerPoints ? 0 : m_innerPoints.Count;
public override void SetMaterialDirty() { return; } public override void SetVerticesDirty() { return; }
public bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera) { if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out Vector2 local)) { Vector2 bottomLeft = GetLocalRectPosition(rectTransform, Vector2.zero); Vector2 localDelta = new(local.x - bottomLeft.x, local.y - bottomLeft.y);
#if UNITY_EDITOR Debug.DrawLine(rectTransform.TransformPoint(bottomLeft), rectTransform.TransformPoint(local), Color.red); #endif Rect rect = rectTransform.rect; Vector2 normalizedLocalPoint = new(localDelta.x / rect.width, localDelta.y / rect.height); bool result = RaycastCheckPointInPolygon(normalizedLocalPoint); return result; }
return false; }
[ContextMenu(nameof(CreateNewPoint))] public void CreateNewPoint() { m_innerPoints ??= new List<Vector2>(); if (2 > m_innerPoints.Count) { m_innerPoints.Add(Vector2.zero); return; }
int pointCount = m_innerPoints.Count; Vector2 newPoint = m_innerPoints[pointCount - 1]; Vector2 tempDir = m_innerPoints[0] - m_innerPoints[pointCount - 1]; newPoint += 0.5f * tempDir; m_innerPoints.Add(newPoint); }
public Vector2 GetPoint(int index) { if (null == m_innerPoints) { return Vector2.zero; } return m_innerPoints[index]; }
public void SetPoint(int index, Vector2 point) { if (0 > index || m_innerPoints.Count <= index) { Debug.LogError($"Index {index} is out of range for points list."); return; } m_innerPoints[index] = point; }
protected override void OnPopulateMesh(VertexHelper vh) { vh.Clear(); }
private Vector2 GetLocalRectPosition(RectTransform target, Vector2 normalizedRectPosition) { Vector2 result = Vector2.zero; Vector2 targetSize = target.rect.size; Vector2 targetPivot = target.pivot;
Vector2 pivotOffset = Vector2.one * 0.5f - targetPivot; pivotOffset.x *= targetSize.x; pivotOffset.y *= targetSize.y;
result += pivotOffset;
result.x -= targetSize.x * 0.5f; result.y -= targetSize.y * 0.5f;
result.x += normalizedRectPosition.x * targetSize.x; result.y += normalizedRectPosition.y * targetSize.y;
return result; }
private bool RaycastCheckPointInPolygon(Vector2 normalizedPoint) { int pointCount = m_innerPoints.Count; if (3 > pointCount) { return false; }
if (3 == pointCount) { return PointInTriangle ( normalizedPoint, m_innerPoints[0], m_innerPoints[1], m_innerPoints[2] ); }
int intersectionCount = 0; for (int i = 0, length = pointCount; i < length; i++) { Vector2 upPoint = m_innerPoints[i]; Vector2 downPoint = m_innerPoints[(i + 1) % length]; if (upPoint.y < downPoint.y) { (upPoint, downPoint) = (downPoint, upPoint); }
Vector2 edge = new(1f, normalizedPoint.y);
if (Mathf.Max(upPoint.x, downPoint.x) < normalizedPoint.x || upPoint.y < normalizedPoint.y || downPoint.y > normalizedPoint.y) { continue; }
Vector2 p2up = upPoint - normalizedPoint; Vector2 down2up = upPoint - downPoint;
if ((Mathf.Approximately(upPoint.x, normalizedPoint.x) && Mathf.Approximately(upPoint.y, normalizedPoint.y)) || (Mathf.Approximately(downPoint.x, normalizedPoint.x) && Mathf.Approximately(downPoint.y, normalizedPoint.y)) || 0 == Vector3.Cross(p2up, down2up).z) { return true; }
Vector2 p2edge = edge - normalizedPoint; Vector2 p2down = downPoint - normalizedPoint;
Vector3 cross1 = Vector3.Cross(p2up, p2edge); Vector3 cross2 = Vector3.Cross(p2down, p2edge);
Vector2 edge2p = normalizedPoint - edge; Vector2 edge2up = upPoint - edge; Vector2 edge2down = downPoint - edge;
Vector3 cross3 = Vector3.Cross(edge2up, edge2p); Vector3 cross4 = Vector3.Cross(edge2down, edge2p);
if (0 >= cross1.z * cross2.z && 0 >= cross3.z * cross4.z && 0 < Vector3.Cross(p2down, p2up).z) { intersectionCount++; } }
return intersectionCount % 2 == 1; }
private bool PointInTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c) { Vector3 p2a = a - p; Vector3 p2b = b - p; Vector3 p2c = c - p;
Vector3 cross1 = Vector3.Cross(p2a, p2b); Vector3 cross2 = Vector3.Cross(p2b, p2c); Vector3 cross3 = Vector3.Cross(p2c, p2a);
float dot1 = Vector3.Dot(cross1, cross2); float dot2 = Vector3.Dot(cross2, cross3); float dot3 = Vector3.Dot(cross3, cross1);
return dot1 >= 0 && dot2 >= 0 && dot3 >= 0; }
} }
|