GeometryCollider.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using Unity.Collections;
  4. using UnityEngine;
  5. using UnityEngine.Experimental.U2D;
  6. using UnityEngine.U2D;
  7. #if UNITY_EDITOR
  8. using UnityEditor;
  9. #endif
  10. [ExecuteAlways]
  11. public class GeometryCollider : MonoBehaviour
  12. {
  13. [SerializeField]
  14. bool m_UpdateCollider = false;
  15. int m_HashCode = 0;
  16. void Start()
  17. {
  18. }
  19. // Update is called once per frame
  20. void Update()
  21. {
  22. if (m_UpdateCollider)
  23. Bake(gameObject, false);
  24. }
  25. static public void Bake(GameObject go, bool forced)
  26. {
  27. var spriteShapeController = go.GetComponent<SpriteShapeController>();
  28. var spriteShapeRenderer = go.GetComponent<SpriteShapeRenderer>();
  29. var polyCollider = go.GetComponent<PolygonCollider2D>();
  30. var geometryCollider = go.GetComponent<GeometryCollider>();
  31. if (spriteShapeController != null && polyCollider != null)
  32. {
  33. var spline = spriteShapeController.spline;
  34. if (geometryCollider != null)
  35. {
  36. int splineHashCode = spline.GetHashCode();
  37. if (splineHashCode == geometryCollider.m_HashCode && !forced)
  38. return;
  39. geometryCollider.m_HashCode = splineHashCode;
  40. }
  41. NativeArray<ushort> indexArray;
  42. NativeSlice<Vector3> posArray;
  43. NativeSlice<Vector2> uv0Array;
  44. NativeArray<SpriteShapeSegment> geomArray;
  45. spriteShapeRenderer.GetChannels(65536, out indexArray, out posArray, out uv0Array);
  46. geomArray = spriteShapeRenderer.GetSegments(spline.GetPointCount() * 8);
  47. NativeArray<ushort> indexArrayLocal = new NativeArray<ushort>(indexArray.Length, Allocator.Temp);
  48. List<Vector2> points = new List<Vector2>();
  49. int indexCount = 0, vertexCount = 0, counter = 0;
  50. for (int u = 0; u < geomArray.Length; ++u)
  51. {
  52. if (geomArray[u].indexCount > 0)
  53. {
  54. for (int i = 0; i < geomArray[u].indexCount; ++i)
  55. {
  56. indexArrayLocal[counter] = (ushort)(indexArray[counter] + vertexCount);
  57. counter++;
  58. }
  59. vertexCount += geomArray[u].vertexCount;
  60. indexCount += geomArray[u].indexCount;
  61. }
  62. }
  63. Debug.Log(go.name + " : " + counter);
  64. OuterEdges(polyCollider, indexArrayLocal, posArray, indexCount);
  65. }
  66. }
  67. // Generate the outer edges from the Renderer mesh. Based on code from www.h3xed.com
  68. static void OuterEdges(PolygonCollider2D polygonCollider, NativeArray<ushort> triangles, NativeSlice<Vector3> vertices, int triangleCount)
  69. {
  70. // Get just the outer edges from the mesh's triangles (ignore or remove any shared edges)
  71. Dictionary<string, KeyValuePair<int, int>> edges = new Dictionary<string, KeyValuePair<int, int>>();
  72. for (int i = 0; i < triangleCount; i += 3)
  73. {
  74. for (int e = 0; e < 3; e++)
  75. {
  76. int vert1 = triangles[i + e];
  77. int vert2 = triangles[i + e + 1 > i + 2 ? i : i + e + 1];
  78. string edge = Mathf.Min(vert1, vert2) + ":" + Mathf.Max(vert1, vert2);
  79. if (edges.ContainsKey(edge))
  80. {
  81. edges.Remove(edge);
  82. }
  83. else
  84. {
  85. edges.Add(edge, new KeyValuePair<int, int>(vert1, vert2));
  86. }
  87. }
  88. }
  89. // Create edge lookup (Key is first vertex, Value is second vertex, of each edge)
  90. Dictionary<int, int> lookup = new Dictionary<int, int>();
  91. foreach (KeyValuePair<int, int> edge in edges.Values)
  92. {
  93. if (lookup.ContainsKey(edge.Key) == false)
  94. {
  95. lookup.Add(edge.Key, edge.Value);
  96. }
  97. }
  98. // Create empty polygon collider
  99. polygonCollider.pathCount = 0;
  100. // Loop through edge vertices in order
  101. int startVert = 0;
  102. int nextVert = startVert;
  103. int highestVert = startVert;
  104. List<Vector2> colliderPath = new List<Vector2>();
  105. while (true)
  106. {
  107. // Add vertex to collider path
  108. colliderPath.Add(vertices[nextVert]);
  109. // Get next vertex
  110. nextVert = lookup[nextVert];
  111. // Store highest vertex (to know what shape to move to next)
  112. if (nextVert > highestVert)
  113. {
  114. highestVert = nextVert;
  115. }
  116. // Shape complete
  117. if (nextVert == startVert)
  118. {
  119. // Add path to polygon collider
  120. polygonCollider.pathCount++;
  121. polygonCollider.SetPath(polygonCollider.pathCount - 1, colliderPath.ToArray());
  122. colliderPath.Clear();
  123. // Go to next shape if one exists
  124. if (lookup.ContainsKey(highestVert + 1))
  125. {
  126. // Set starting and next vertices
  127. startVert = highestVert + 1;
  128. nextVert = startVert;
  129. // Continue to next loop
  130. continue;
  131. }
  132. // No more verts
  133. break;
  134. }
  135. }
  136. }
  137. #if UNITY_EDITOR
  138. [MenuItem("SpriteShape/Generate Geometry Collider", false, 358)]
  139. public static void BakeGeometryCollider()
  140. {
  141. if (Selection.activeGameObject != null)
  142. GeometryCollider.Bake(Selection.activeGameObject, true);
  143. }
  144. #endif
  145. }