com.wallstop-studios.unity-helpers 2.0.0-rc76.2 → 2.0.0-rc76.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/Editor/CustomDrawers/WShowIfPropertyDrawer.cs +1 -0
  2. package/Editor/CustomEditors/PolygonCollider2DOptimizerEditor.cs +40 -0
  3. package/Editor/CustomEditors/PolygonCollider2DOptimizerEditor.cs.meta +3 -0
  4. package/{Runtime/Core/Extension → Editor/Extensions}/SerializedPropertyExtensions.cs +1 -1
  5. package/Runtime/Core/DataStructure/CyclicBuffer.cs +29 -19
  6. package/Runtime/Core/Extension/DictionaryExtensions.cs +30 -10
  7. package/Runtime/Core/Extension/EnumExtensions.cs +4 -4
  8. package/Runtime/Core/Extension/IEnumerableExtensions.cs +12 -10
  9. package/Runtime/Core/Extension/UnityExtensions.cs +68 -0
  10. package/Runtime/Core/Helper/Helpers.cs +12 -0
  11. package/Runtime/Core/Helper/LineHelper.cs +194 -0
  12. package/Runtime/Core/Helper/LineHelper.cs.meta +3 -0
  13. package/Runtime/Tags/CollisionSenses.cs +91 -0
  14. package/Runtime/Tags/CollisionSenses.cs.meta +3 -0
  15. package/Runtime/Utils/ChildSpawner.cs +100 -0
  16. package/Runtime/Utils/ChildSpawner.cs.meta +3 -0
  17. package/Runtime/Utils/CollisionProxy.cs +48 -0
  18. package/Runtime/Utils/CollisionProxy.cs.meta +3 -0
  19. package/Runtime/Utils/PolygonCollider2DOptimizer.cs +83 -0
  20. package/Runtime/Utils/PolygonCollider2DOptimizer.cs.meta +3 -0
  21. package/Runtime/Utils/SerializedStringComparer.cs +107 -0
  22. package/Runtime/Utils/SerializedStringComparer.cs.meta +3 -0
  23. package/Tests/Runtime/DataStructures/CyclicBufferTests.cs +2 -2
  24. package/package.json +4 -1
  25. package/Editor/UI.meta +0 -3
  26. /package/{Runtime/Core/Extension → Editor/Extensions}/SerializedPropertyExtensions.cs.meta +0 -0
@@ -2,6 +2,7 @@
2
2
  {
3
3
  #if UNITY_EDITOR
4
4
  using System.Reflection;
5
+ using Extensions;
5
6
  using UnityEditor;
6
7
  using UnityEngine;
7
8
  using WallstopStudios.UnityHelpers.Core.Attributes;
@@ -0,0 +1,40 @@
1
+ namespace WallstopStudios.UnityHelpers.Editor.CustomEditors
2
+ {
3
+ #if UNITY_EDITOR
4
+ using UnityEditor;
5
+ using UnityEngine;
6
+ using UnityHelpers.Utils;
7
+
8
+ [CustomEditor(typeof(PolygonCollider2DOptimizer))]
9
+ public sealed class PolygonCollider2DOptimizerEditor : Editor
10
+ {
11
+ private SerializedProperty _tolerance;
12
+
13
+ private void OnEnable()
14
+ {
15
+ _tolerance = serializedObject.FindProperty(
16
+ nameof(PolygonCollider2DOptimizer.tolerance)
17
+ );
18
+ }
19
+
20
+ public override void OnInspectorGUI()
21
+ {
22
+ serializedObject.Update();
23
+ if (EditorGUILayout.PropertyField(_tolerance))
24
+ {
25
+ serializedObject.ApplyModifiedProperties();
26
+ }
27
+
28
+ if (GUILayout.Button("Optimize"))
29
+ {
30
+ PolygonCollider2DOptimizer optimizer = target as PolygonCollider2DOptimizer;
31
+ if (optimizer != null)
32
+ {
33
+ optimizer.Refresh();
34
+ }
35
+ serializedObject.ApplyModifiedProperties();
36
+ }
37
+ }
38
+ }
39
+ #endif
40
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: df2a6833fa1643b1abb304382b3ab03b
3
+ timeCreated: 1748568224
@@ -1,4 +1,4 @@
1
- namespace WallstopStudios.UnityHelpers.Core.Extension
1
+ namespace WallstopStudios.UnityHelpers.Editor.Extensions
2
2
  {
3
3
  #if UNITY_EDITOR
4
4
  using System;
@@ -3,7 +3,6 @@
3
3
  using System;
4
4
  using System.Collections;
5
5
  using System.Collections.Generic;
6
- using System.Linq;
7
6
  using Extension;
8
7
  using Helper;
9
8
 
@@ -53,7 +52,6 @@
53
52
  public int Count { get; private set; }
54
53
 
55
54
  private readonly List<T> _buffer;
56
- private readonly List<T> _cache;
57
55
  private int _position;
58
56
 
59
57
  public T this[int index]
@@ -81,7 +79,6 @@
81
79
  _position = 0;
82
80
  Count = 0;
83
81
  _buffer = new List<T>();
84
- _cache = new List<T>();
85
82
  if (initialContents != null)
86
83
  {
87
84
  foreach (T item in initialContents)
@@ -131,17 +128,26 @@
131
128
 
132
129
  public bool Remove(T element, IEqualityComparer<T> comparer = null)
133
130
  {
131
+ if (Count == 0)
132
+ {
133
+ return false;
134
+ }
135
+
136
+ int write = 0;
134
137
  bool removed = false;
135
- _cache.Clear();
136
138
  comparer ??= EqualityComparer<T>.Default;
137
- foreach (T item in this)
139
+ for (int i = 0; i < Count; ++i)
138
140
  {
141
+ int readIdx = AdjustedIndexFor(i);
142
+ T item = _buffer[readIdx];
143
+
139
144
  if (!removed && comparer.Equals(item, element))
140
145
  {
141
146
  removed = true;
142
147
  continue;
143
148
  }
144
- _cache.Add(item);
149
+
150
+ _buffer[write++] = item;
145
151
  }
146
152
 
147
153
  if (!removed)
@@ -149,27 +155,34 @@
149
155
  return false;
150
156
  }
151
157
 
152
- Clear();
153
- foreach (T item in _cache)
154
- {
155
- Add(item);
156
- }
158
+ _buffer.RemoveRange(write, _buffer.Count - write);
157
159
 
160
+ Count--;
161
+ _position = Count < Capacity ? Count : 0;
158
162
  return true;
159
163
  }
160
164
 
161
165
  public int RemoveAll(Func<T, bool> predicate)
162
166
  {
167
+ if (Count == 0)
168
+ {
169
+ return 0;
170
+ }
171
+
172
+ int write = 0;
163
173
  int removedCount = 0;
164
- foreach (T item in this)
174
+
175
+ for (int i = 0; i < Count; ++i)
165
176
  {
177
+ int readIdx = AdjustedIndexFor(i);
178
+ T item = _buffer[readIdx];
166
179
  if (predicate(item))
167
180
  {
168
181
  removedCount++;
169
182
  }
170
183
  else
171
184
  {
172
- _cache.Add(item);
185
+ _buffer[write++] = item;
173
186
  }
174
187
  }
175
188
 
@@ -178,12 +191,9 @@
178
191
  return 0;
179
192
  }
180
193
 
181
- Clear();
182
- foreach (T item in _cache)
183
- {
184
- Add(item);
185
- }
186
-
194
+ _buffer.RemoveRange(write, _buffer.Count - write);
195
+ Count -= removedCount;
196
+ _position = Count < Capacity ? Count : 0;
187
197
  return removedCount;
188
198
  }
189
199
 
@@ -144,10 +144,11 @@
144
144
 
145
145
  public static Dictionary<K, V> Merge<K, V>(
146
146
  this IReadOnlyDictionary<K, V> lhs,
147
- IReadOnlyDictionary<K, V> rhs
147
+ IReadOnlyDictionary<K, V> rhs,
148
+ Func<Dictionary<K, V>> creator = null
148
149
  )
149
150
  {
150
- Dictionary<K, V> result = new();
151
+ Dictionary<K, V> result = creator?.Invoke() ?? new Dictionary<K, V>();
151
152
  if (0 < lhs.Count)
152
153
  {
153
154
  foreach (KeyValuePair<K, V> kvp in lhs)
@@ -176,10 +177,11 @@
176
177
  /// <returns>All elements of rhs that either don't exist in or are different from lhs</returns>
177
178
  public static Dictionary<K, V> Difference<K, V>(
178
179
  this IReadOnlyDictionary<K, V> lhs,
179
- IReadOnlyDictionary<K, V> rhs
180
+ IReadOnlyDictionary<K, V> rhs,
181
+ Func<Dictionary<K, V>> creator = null
180
182
  )
181
183
  {
182
- Dictionary<K, V> result = new(rhs.Count);
184
+ Dictionary<K, V> result = creator?.Invoke() ?? new Dictionary<K, V>(rhs.Count);
183
185
  foreach (KeyValuePair<K, V> kvp in rhs)
184
186
  {
185
187
  K key = kvp.Key;
@@ -194,9 +196,12 @@
194
196
  return result;
195
197
  }
196
198
 
197
- public static Dictionary<V, K> Reverse<K, V>(this IReadOnlyDictionary<K, V> dictionary)
199
+ public static Dictionary<V, K> Reverse<K, V>(
200
+ this IReadOnlyDictionary<K, V> dictionary,
201
+ Func<Dictionary<V, K>> creator = null
202
+ )
198
203
  {
199
- Dictionary<V, K> output = new(dictionary.Count);
204
+ Dictionary<V, K> output = creator?.Invoke() ?? new Dictionary<V, K>(dictionary.Count);
200
205
  foreach (KeyValuePair<K, V> entry in dictionary)
201
206
  {
202
207
  output[entry.Value] = entry.Key;
@@ -264,10 +269,25 @@
264
269
  return false;
265
270
  }
266
271
 
267
- return dictionary.Count == other.Count
268
- && dictionary.All(kvp =>
269
- other.TryGetValue(kvp.Key, out V value) && kvp.Value.Equals(value)
270
- );
272
+ if (dictionary.Count != other.Count)
273
+ {
274
+ return false;
275
+ }
276
+
277
+ if (dictionary.Count == 0)
278
+ {
279
+ return true;
280
+ }
281
+
282
+ foreach (KeyValuePair<K, V> entry in dictionary)
283
+ {
284
+ if (!other.TryGetValue(entry.Key, out V value) || !entry.Value.Equals(value))
285
+ {
286
+ return false;
287
+ }
288
+ }
289
+
290
+ return true;
271
291
  }
272
292
 
273
293
  public static void Deconstruct<K, V>(this KeyValuePair<K, V> kvp, out K key, out V value)
@@ -16,8 +16,8 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
16
16
 
17
17
  private static readonly bool UseDensePacking;
18
18
  private static readonly int Min;
19
- private static readonly string[]? DenseNames;
20
- private static readonly Dictionary<int, string>? SparseNames;
19
+ private static readonly string[] DenseNames;
20
+ private static readonly Dictionary<int, string> SparseNames;
21
21
 
22
22
  static EnumNameCache()
23
23
  {
@@ -85,8 +85,8 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
85
85
 
86
86
  private static readonly bool UseDensePacking;
87
87
  private static readonly int Min;
88
- private static readonly string[]? DenseNames;
89
- private static readonly Dictionary<int, string>? SparseNames;
88
+ private static readonly string[] DenseNames;
89
+ private static readonly Dictionary<int, string> SparseNames;
90
90
 
91
91
  static EnumDisplayNameCache()
92
92
  {
@@ -33,13 +33,13 @@
33
33
  FuncBasedComparer<T> comparerObject =
34
34
  (FuncBasedComparer<T>)
35
35
  ComparerCache.GetOrAdd(comparer, () => new FuncBasedComparer<T>(comparer));
36
- return enumeration.OrderBy(_ => _, comparerObject);
36
+ return enumeration.OrderBy(x => x, comparerObject);
37
37
  }
38
38
 
39
39
  public static IEnumerable<T> Ordered<T>(this IEnumerable<T> enumerable)
40
40
  where T : IComparable
41
41
  {
42
- return enumerable.OrderBy(_ => _);
42
+ return enumerable.OrderBy(x => x);
43
43
  }
44
44
 
45
45
  public static IEnumerable<T> Shuffled<T>(
@@ -47,7 +47,7 @@
47
47
  IRandom random = null
48
48
  )
49
49
  {
50
- random = random ?? ThreadLocalRandom<PcgRandom>.Instance;
50
+ random ??= ThreadLocalRandom<PcgRandom>.Instance;
51
51
  return enumerable.OrderBy(_ => random.Next());
52
52
  }
53
53
 
@@ -81,6 +81,13 @@
81
81
  using IEnumerator<T> enumerator = items.GetEnumerator();
82
82
  bool hasNext = enumerator.MoveNext();
83
83
 
84
+ while (hasNext)
85
+ {
86
+ yield return NextPartitionOf().ToList();
87
+ }
88
+
89
+ yield break;
90
+
84
91
  IEnumerable<T> NextPartitionOf()
85
92
  {
86
93
  int remainingCountForPartition = size;
@@ -90,11 +97,6 @@
90
97
  hasNext = enumerator.MoveNext();
91
98
  }
92
99
  }
93
-
94
- while (hasNext)
95
- {
96
- yield return NextPartitionOf().ToList();
97
- }
98
100
  }
99
101
 
100
102
  public static List<T> ToList<T>(this IEnumerable<T> enumerable, int count)
@@ -113,9 +115,9 @@
113
115
  _comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
114
116
  }
115
117
 
116
- public int Compare(T lhs, T rhs)
118
+ public int Compare(T x, T y)
117
119
  {
118
- return _comparer(lhs, rhs);
120
+ return _comparer(x, y);
119
121
  }
120
122
  }
121
123
  }
@@ -1603,5 +1603,73 @@
1603
1603
  }
1604
1604
  }
1605
1605
  #endif
1606
+
1607
+ public static bool IsDontDestroyOnLoad(this GameObject gameObjectToCheck)
1608
+ {
1609
+ if (gameObjectToCheck == null)
1610
+ {
1611
+ return false;
1612
+ }
1613
+
1614
+ return string.Equals(
1615
+ gameObjectToCheck.scene.name,
1616
+ "DontDestroyOnLoad",
1617
+ StringComparison.Ordinal
1618
+ );
1619
+ }
1620
+
1621
+ public static bool IsCircleFullyContained(
1622
+ this Collider2D targetCollider,
1623
+ Vector2 center,
1624
+ float radius,
1625
+ int sampleCount = 16
1626
+ )
1627
+ {
1628
+ for (int i = 0; i < sampleCount; ++i)
1629
+ {
1630
+ float angle = 2 * Mathf.PI / sampleCount * i;
1631
+ Vector2 pointOnCircle =
1632
+ center + new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * radius;
1633
+
1634
+ if (!targetCollider.OverlapPoint(pointOnCircle))
1635
+ {
1636
+ return false;
1637
+ }
1638
+ }
1639
+ return true;
1640
+ }
1641
+
1642
+ public static void Invert(this PolygonCollider2D col, Rect outerRect)
1643
+ {
1644
+ int originalCount = col.pathCount;
1645
+ if (originalCount == 0)
1646
+ {
1647
+ return;
1648
+ }
1649
+
1650
+ Vector2[][] originals = new Vector2[originalCount][];
1651
+ for (int i = 0; i < originalCount; i++)
1652
+ {
1653
+ originals[i] = col.GetPath(i).ToArray();
1654
+ }
1655
+
1656
+ Vector2[] outerPath =
1657
+ {
1658
+ new Vector2(outerRect.xMin, outerRect.yMin),
1659
+ new Vector2(outerRect.xMin, outerRect.yMax),
1660
+ new Vector2(outerRect.xMax, outerRect.yMax),
1661
+ new Vector2(outerRect.xMax, outerRect.yMin),
1662
+ };
1663
+
1664
+ col.pathCount = originalCount + 1;
1665
+ col.SetPath(0, outerPath);
1666
+
1667
+ for (int i = 0; i < originalCount; ++i)
1668
+ {
1669
+ Vector2[] hole = originals[i];
1670
+ Array.Reverse(hole);
1671
+ col.SetPath(i + 1, hole);
1672
+ }
1673
+ }
1606
1674
  }
1607
1675
  }
@@ -13,6 +13,9 @@
13
13
  using Object = UnityEngine.Object;
14
14
  #if UNITY_EDITOR
15
15
  using UnityEditor;
16
+ using UnityEditorInternal;
17
+ #else
18
+ using System;
16
19
  #endif
17
20
 
18
21
  public static partial class Helpers
@@ -24,6 +27,15 @@
24
27
  StringComparer.Ordinal
25
28
  );
26
29
 
30
+ public static string[] GetAllLayerNames()
31
+ {
32
+ #if UNITY_EDITOR
33
+ return InternalEditorUtility.layers;
34
+ #else
35
+ return Array.Empty<string>();
36
+ #endif
37
+ }
38
+
27
39
  // https://gamedevelopment.tutsplus.com/tutorials/unity-solution-for-hitting-moving-targets--cms-29633
28
40
  public static Vector2 PredictCurrentTarget(
29
41
  this GameObject currentTarget,
@@ -0,0 +1,194 @@
1
+ namespace WallstopStudios.UnityHelpers.Core.Helper
2
+ {
3
+ using System.Collections.Generic;
4
+ using System.Linq;
5
+ using UnityEngine;
6
+
7
+ public static class LineHelper
8
+ {
9
+ private static float PerpendicularDistance(
10
+ Vector2 point,
11
+ Vector2 lineStart,
12
+ Vector2 lineEnd
13
+ )
14
+ {
15
+ float xDistance = lineEnd.x - lineStart.x;
16
+ float yDistance = lineEnd.y - lineStart.y;
17
+
18
+ if (Mathf.Approximately(xDistance, 0) && Mathf.Approximately(yDistance, 0))
19
+ {
20
+ return Vector2.Distance(point, lineStart);
21
+ }
22
+
23
+ float t =
24
+ ((point.x - lineStart.x) * xDistance + (point.y - lineStart.y) * yDistance)
25
+ / (xDistance * xDistance + yDistance * yDistance);
26
+
27
+ Vector2 closestPoint = t switch
28
+ {
29
+ < 0 => lineStart,
30
+ > 1 => lineEnd,
31
+ _ => new Vector2(lineStart.x + t * xDistance, lineStart.y + t * yDistance),
32
+ };
33
+
34
+ return Vector2.Distance(point, closestPoint);
35
+ }
36
+
37
+ // c# implementation of the Ramer-Douglas-Peucker-Algorithm by Craig Selbert slightly adapted for Unity Vector Types
38
+ //http://www.codeproject.com/Articles/18936/A-Csharp-Implementation-of-Douglas-Peucker-Line-Ap
39
+ public static List<Vector2> SimplifyPrecise(List<Vector2> points, double tolerance)
40
+ {
41
+ if (points == null || points.Count < 3)
42
+ {
43
+ return points;
44
+ }
45
+
46
+ int firstPoint = 0;
47
+ int lastPoint = points.Count - 1;
48
+
49
+ //Add the first and last index to the keepers
50
+ List<int> pointIndexsToKeep = new() { firstPoint, lastPoint };
51
+
52
+ //The first and the last point cannot be the same
53
+ while (points[firstPoint] == points[lastPoint])
54
+ {
55
+ lastPoint--;
56
+ }
57
+
58
+ DouglasPeuckerReductionRecursive(
59
+ points,
60
+ firstPoint,
61
+ lastPoint,
62
+ tolerance,
63
+ ref pointIndexsToKeep
64
+ );
65
+
66
+ List<Vector2> returnPoints = new();
67
+ pointIndexsToKeep.Sort();
68
+ foreach (int index in pointIndexsToKeep)
69
+ {
70
+ returnPoints.Add(points[index]);
71
+ }
72
+
73
+ return returnPoints;
74
+ }
75
+
76
+ private static void DouglasPeuckerReductionRecursive(
77
+ List<Vector2> points,
78
+ int firstPoint,
79
+ int lastPoint,
80
+ double tolerance,
81
+ ref List<int> pointIndexesToKeep
82
+ )
83
+ {
84
+ while (true)
85
+ {
86
+ double maxDistance = 0;
87
+ int indexFarthest = 0;
88
+
89
+ for (int index = firstPoint; index < lastPoint; index++)
90
+ {
91
+ double distance = InternalPerpendicularDistance(
92
+ points[firstPoint],
93
+ points[lastPoint],
94
+ points[index]
95
+ );
96
+ if (distance > maxDistance)
97
+ {
98
+ maxDistance = distance;
99
+ indexFarthest = index;
100
+ }
101
+ }
102
+
103
+ if (maxDistance > tolerance && indexFarthest != 0)
104
+ {
105
+ //Add the largest point that exceeds the tolerance
106
+ pointIndexesToKeep.Add(indexFarthest);
107
+
108
+ DouglasPeuckerReductionRecursive(
109
+ points,
110
+ firstPoint,
111
+ indexFarthest,
112
+ tolerance,
113
+ ref pointIndexesToKeep
114
+ );
115
+
116
+ firstPoint = indexFarthest;
117
+ continue;
118
+ }
119
+
120
+ break;
121
+ }
122
+
123
+ return;
124
+
125
+ static double InternalPerpendicularDistance(
126
+ Vector2 point1,
127
+ Vector2 point2,
128
+ Vector2 point
129
+ )
130
+ {
131
+ double area = System.Math.Abs(
132
+ .5f
133
+ * (
134
+ point1.x * point2.y
135
+ + point2.x * point.y
136
+ + point.x * point1.y
137
+ - point2.x * point1.y
138
+ - point.x * point2.y
139
+ - point1.x * point.y
140
+ )
141
+ );
142
+ double bottom = System.Math.Sqrt(
143
+ System.Math.Pow(point1.x - point2.x, 2.0)
144
+ + System.Math.Pow(point1.y - point2.y, 2.0)
145
+ );
146
+ double height = area / bottom * 2.0;
147
+ return height;
148
+ }
149
+ }
150
+
151
+ public static List<Vector2> Simplify(List<Vector2> points, float epsilon)
152
+ {
153
+ if (points == null || points.Count < 3 || epsilon <= 0)
154
+ {
155
+ return new List<Vector2>(points ?? Enumerable.Empty<Vector2>());
156
+ }
157
+
158
+ float maxDistance = 0;
159
+ int index = 0;
160
+ int end = points.Count - 1;
161
+
162
+ for (int i = 1; i < end; ++i)
163
+ {
164
+ float distance = PerpendicularDistance(points[i], points[0], points[end]);
165
+ if (distance > maxDistance)
166
+ {
167
+ index = i;
168
+ maxDistance = distance;
169
+ }
170
+ }
171
+
172
+ List<Vector2> result = new();
173
+
174
+ if (maxDistance > epsilon)
175
+ {
176
+ List<Vector2> recResults1 = Simplify(points.GetRange(0, index + 1), epsilon);
177
+ List<Vector2> recResults2 = Simplify(
178
+ points.GetRange(index, points.Count - index),
179
+ epsilon
180
+ );
181
+
182
+ result.AddRange(recResults1.Take(recResults1.Count - 1));
183
+ result.AddRange(recResults2);
184
+ }
185
+ else
186
+ {
187
+ result.Add(points[0]);
188
+ result.Add(points[end]);
189
+ }
190
+
191
+ return result;
192
+ }
193
+ }
194
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 0b74d35d6b084ca2802d2955ff5a4365
3
+ timeCreated: 1748562665
@@ -0,0 +1,91 @@
1
+ namespace WallstopStudios.UnityHelpers.Tags
2
+ {
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using Core.Attributes;
6
+ using UnityEngine;
7
+
8
+ [DisallowMultipleComponent]
9
+ [RequireComponent(typeof(TagHandler))]
10
+ public sealed class CollisionSenses : MonoBehaviour
11
+ {
12
+ public const string CollisionDisabledTag = nameof(CollisionDisabledTag);
13
+
14
+ [SiblingComponent]
15
+ private TagHandler _tagHandler;
16
+
17
+ private readonly List<Collider2D> _managedColliders = new();
18
+
19
+ private void Awake()
20
+ {
21
+ this.AssignRelationalComponents();
22
+ }
23
+
24
+ private void OnEnable()
25
+ {
26
+ if (_tagHandler.HasTag(CollisionDisabledTag))
27
+ {
28
+ StartManagingColliders();
29
+ }
30
+
31
+ _tagHandler.OnTagAdded += CheckForTagAddition;
32
+ _tagHandler.OnTagRemoved += CheckForTagRemoval;
33
+ }
34
+
35
+ private void OnDisable()
36
+ {
37
+ _tagHandler.OnTagAdded -= CheckForTagAddition;
38
+ _tagHandler.OnTagRemoved -= CheckForTagRemoval;
39
+ StopManagingColliders();
40
+ }
41
+
42
+ private void CheckForTagAddition(string addedTag)
43
+ {
44
+ if (!isActiveAndEnabled)
45
+ {
46
+ return;
47
+ }
48
+
49
+ if (string.Equals(addedTag, CollisionDisabledTag, StringComparison.Ordinal))
50
+ {
51
+ StartManagingColliders();
52
+ }
53
+ }
54
+
55
+ private void CheckForTagRemoval(string removedTag)
56
+ {
57
+ if (!isActiveAndEnabled)
58
+ {
59
+ return;
60
+ }
61
+
62
+ if (string.Equals(removedTag, CollisionDisabledTag, StringComparison.Ordinal))
63
+ {
64
+ StopManagingColliders();
65
+ }
66
+ }
67
+
68
+ private void StopManagingColliders()
69
+ {
70
+ foreach (Collider2D managedCollider in _managedColliders)
71
+ {
72
+ if (managedCollider != null)
73
+ {
74
+ managedCollider.enabled = true;
75
+ }
76
+ }
77
+
78
+ _managedColliders.Clear();
79
+ }
80
+
81
+ private void StartManagingColliders()
82
+ {
83
+ GetComponentsInChildren(_managedColliders);
84
+ _managedColliders.RemoveAll(managed => !managed.enabled);
85
+ foreach (Collider2D managedCollider in _managedColliders)
86
+ {
87
+ managedCollider.enabled = false;
88
+ }
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 4157eb60e7a64fcda98a72acb17bf2c3
3
+ timeCreated: 1748569172
@@ -0,0 +1,100 @@
1
+ namespace WallstopStudios.UnityHelpers.Utils
2
+ {
3
+ using System.Collections.Generic;
4
+ using System.Linq;
5
+ using Core.Extension;
6
+ using UnityEngine;
7
+
8
+ [DisallowMultipleComponent]
9
+ public sealed class ChildSpawner : MonoBehaviour
10
+ {
11
+ private static readonly HashSet<GameObject> SpawnedPrefabs = new();
12
+
13
+ [SerializeField]
14
+ private GameObject[] _prefabs;
15
+
16
+ [SerializeField]
17
+ private GameObject[] _editorOnlyPrefabs;
18
+
19
+ [SerializeField]
20
+ private GameObject[] _developmentOnlyPrefabs;
21
+
22
+ private void Start()
23
+ {
24
+ if (
25
+ _prefabs
26
+ .Concat(_editorOnlyPrefabs)
27
+ .Concat(_developmentOnlyPrefabs)
28
+ .Distinct()
29
+ .Count()
30
+ != (_prefabs.Length + _editorOnlyPrefabs.Length + _developmentOnlyPrefabs.Length)
31
+ )
32
+ {
33
+ IEnumerable<string> duplicateChildNames = _prefabs
34
+ .Concat(_editorOnlyPrefabs)
35
+ .Concat(_developmentOnlyPrefabs)
36
+ .GroupBy(x => x)
37
+ .Where(group => group.Count() > 1)
38
+ .Select(group => group.Key != null ? group.Key.name : "null");
39
+ this.LogError(
40
+ $"Duplicate child prefab detected: {string.Join(",", duplicateChildNames)}"
41
+ );
42
+ }
43
+
44
+ int count = 0;
45
+ foreach (GameObject prefab in _prefabs)
46
+ {
47
+ GameObject child = Spawn(prefab);
48
+ if (child != null)
49
+ {
50
+ child.name = $"{child.name} ({count++:00})";
51
+ }
52
+ }
53
+
54
+ if (Application.isEditor)
55
+ {
56
+ foreach (GameObject prefab in _editorOnlyPrefabs)
57
+ {
58
+ GameObject child = Spawn(prefab);
59
+ if (child != null)
60
+ {
61
+ child.name = $"{child.name} (EDITOR-ONLY {count++:00})";
62
+ }
63
+ }
64
+ }
65
+
66
+ if (Application.isEditor || Debug.isDebugBuild)
67
+ {
68
+ foreach (GameObject prefab in _developmentOnlyPrefabs)
69
+ {
70
+ GameObject child = Spawn(prefab);
71
+ if (child != null)
72
+ {
73
+ child.name = $"{child.name} (DEVELOPMENT-ONLY {count++:00})";
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ private static void CleanName(GameObject child)
80
+ {
81
+ child.name = child.name.Replace("(Clone)", string.Empty);
82
+ }
83
+
84
+ private GameObject Spawn(GameObject prefab)
85
+ {
86
+ if (SpawnedPrefabs.Contains(prefab))
87
+ {
88
+ return null;
89
+ }
90
+
91
+ GameObject child = Instantiate(prefab, transform);
92
+ CleanName(child);
93
+ if (child.IsDontDestroyOnLoad() || gameObject.IsDontDestroyOnLoad())
94
+ {
95
+ SpawnedPrefabs.Add(prefab);
96
+ }
97
+ return child;
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 2b3c4b1f5ea74914be632464d47b345b
3
+ timeCreated: 1748569304
@@ -0,0 +1,48 @@
1
+ namespace WallstopStudios.UnityHelpers.Utils
2
+ {
3
+ using System;
4
+ using UnityEngine;
5
+
6
+ [DisallowMultipleComponent]
7
+ [RequireComponent(typeof(Collider2D))]
8
+ public sealed class CollisionProxy : MonoBehaviour
9
+ {
10
+ public event Action<Collision2D> OnCollisionEnter = _ => { };
11
+ public event Action<Collision2D> OnCollisionStay = _ => { };
12
+ public event Action<Collision2D> OnCollisionExit = _ => { };
13
+
14
+ public event Action<Collider2D> OnTriggerEnter = _ => { };
15
+ public event Action<Collider2D> OnTriggerStay = _ => { };
16
+ public event Action<Collider2D> OnTriggerExit = _ => { };
17
+
18
+ private void OnTriggerEnter2D(Collider2D other)
19
+ {
20
+ OnTriggerEnter?.Invoke(other);
21
+ }
22
+
23
+ private void OnTriggerStay2D(Collider2D other)
24
+ {
25
+ OnTriggerStay?.Invoke(other);
26
+ }
27
+
28
+ private void OnTriggerExit2D(Collider2D other)
29
+ {
30
+ OnTriggerExit?.Invoke(other);
31
+ }
32
+
33
+ private void OnCollisionEnter2D(Collision2D other)
34
+ {
35
+ OnCollisionEnter?.Invoke(other);
36
+ }
37
+
38
+ private void OnCollisionStay2D(Collision2D other)
39
+ {
40
+ OnCollisionStay?.Invoke(other);
41
+ }
42
+
43
+ private void OnCollisionExit2D(Collision2D other)
44
+ {
45
+ OnCollisionExit?.Invoke(other);
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 9d8886d28ef545d8967a436999bea13e
3
+ timeCreated: 1748569245
@@ -0,0 +1,83 @@
1
+ namespace WallstopStudios.UnityHelpers.Utils
2
+ {
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using Core.Attributes;
6
+ using Core.Helper;
7
+ using UnityEngine;
8
+
9
+ /// <summary>
10
+ /// Polygon collider optimizer. Removes points from the collider polygon with
11
+ /// the given reduction Tolerance
12
+ /// </summary>
13
+ [AddComponentMenu("2D Collider Optimization/ Polygon Collider Optimizer")]
14
+ [RequireComponent(typeof(PolygonCollider2D))]
15
+ public sealed class PolygonCollider2DOptimizer : MonoBehaviour
16
+ {
17
+ [Serializable]
18
+ private sealed class Path
19
+ {
20
+ public List<Vector2> points = new();
21
+
22
+ public Path() { }
23
+
24
+ public Path(IEnumerable<Vector2> points)
25
+ {
26
+ this.points.AddRange(points);
27
+ }
28
+ }
29
+
30
+ public double tolerance;
31
+
32
+ [SiblingComponent]
33
+ private PolygonCollider2D _collider;
34
+
35
+ [SerializeField]
36
+ private List<Path> _originalPaths = new();
37
+
38
+ public void Refresh()
39
+ {
40
+ OnValidate();
41
+ }
42
+
43
+ private void OnValidate()
44
+ {
45
+ if (_collider == null)
46
+ {
47
+ this.AssignRelationalComponents();
48
+ }
49
+
50
+ /*
51
+ When first getting a reference to the collider save the paths
52
+ so that the optimization is re-doable (by performing it on the original path
53
+ every time)
54
+ */
55
+ if (_originalPaths.Count == 0)
56
+ {
57
+ for (int i = 0; i < _collider.pathCount; ++i)
58
+ {
59
+ List<Vector2> points = new(_collider.GetPath(i));
60
+ Path path = new(points);
61
+ _originalPaths.Add(path);
62
+ }
63
+ }
64
+
65
+ //Reset the original paths
66
+ if (tolerance <= 0)
67
+ {
68
+ for (int i = 0; i < _originalPaths.Count; ++i)
69
+ {
70
+ _collider.SetPath(i, _originalPaths[i].points.ToArray());
71
+ }
72
+ return;
73
+ }
74
+
75
+ for (int i = 0; i < _originalPaths.Count; ++i)
76
+ {
77
+ List<Vector2> path = _originalPaths[i].points;
78
+ List<Vector2> updatedPath = LineHelper.SimplifyPrecise(path, tolerance);
79
+ _collider.SetPath(i, updatedPath.ToArray());
80
+ }
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 9363f4728d254b8bae1afb829777d9d3
3
+ timeCreated: 1748566207
@@ -0,0 +1,107 @@
1
+ namespace WallstopStudios.UnityHelpers.Utils
2
+ {
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using System.ComponentModel;
6
+
7
+ [Serializable]
8
+ public sealed class SerializedStringComparer : IEqualityComparer<string>
9
+ {
10
+ public enum StringCompareMode
11
+ {
12
+ Ordinal = 0,
13
+ OrdinalIgnoreCase = 1,
14
+ CurrentCulture = 2,
15
+ CurrentCultureIgnoreCase = 3,
16
+ InvariantCulture = 4,
17
+ InvariantCultureIgnoreCase = 5,
18
+ }
19
+
20
+ public StringCompareMode compareMode = StringCompareMode.Ordinal;
21
+
22
+ public SerializedStringComparer() { }
23
+
24
+ public SerializedStringComparer(StringCompareMode compareMode)
25
+ {
26
+ this.compareMode = compareMode;
27
+ }
28
+
29
+ public bool Equals(string x, string y)
30
+ {
31
+ switch (compareMode)
32
+ {
33
+ case StringCompareMode.Ordinal:
34
+ {
35
+ return StringComparer.Ordinal.Equals(x, y);
36
+ }
37
+ case StringCompareMode.OrdinalIgnoreCase:
38
+ {
39
+ return StringComparer.OrdinalIgnoreCase.Equals(x, y);
40
+ }
41
+ case StringCompareMode.CurrentCulture:
42
+ {
43
+ return StringComparer.CurrentCulture.Equals(x, y);
44
+ }
45
+ case StringCompareMode.CurrentCultureIgnoreCase:
46
+ {
47
+ return StringComparer.CurrentCultureIgnoreCase.Equals(x, y);
48
+ }
49
+ case StringCompareMode.InvariantCulture:
50
+ {
51
+ return StringComparer.InvariantCulture.Equals(x, y);
52
+ }
53
+ case StringCompareMode.InvariantCultureIgnoreCase:
54
+ {
55
+ return StringComparer.InvariantCultureIgnoreCase.Equals(x, y);
56
+ }
57
+ default:
58
+ {
59
+ throw new InvalidEnumArgumentException(
60
+ nameof(compareMode),
61
+ (int)compareMode,
62
+ typeof(StringCompareMode)
63
+ );
64
+ }
65
+ }
66
+ }
67
+
68
+ public int GetHashCode(string obj)
69
+ {
70
+ switch (compareMode)
71
+ {
72
+ case StringCompareMode.Ordinal:
73
+ {
74
+ return StringComparer.Ordinal.GetHashCode(obj);
75
+ }
76
+ case StringCompareMode.OrdinalIgnoreCase:
77
+ {
78
+ return StringComparer.OrdinalIgnoreCase.GetHashCode(obj);
79
+ }
80
+ case StringCompareMode.CurrentCulture:
81
+ {
82
+ return StringComparer.CurrentCulture.GetHashCode(obj);
83
+ }
84
+ case StringCompareMode.CurrentCultureIgnoreCase:
85
+ {
86
+ return StringComparer.CurrentCultureIgnoreCase.GetHashCode(obj);
87
+ }
88
+ case StringCompareMode.InvariantCulture:
89
+ {
90
+ return StringComparer.InvariantCulture.GetHashCode(obj);
91
+ }
92
+ case StringCompareMode.InvariantCultureIgnoreCase:
93
+ {
94
+ return StringComparer.InvariantCultureIgnoreCase.GetHashCode(obj);
95
+ }
96
+ default:
97
+ {
98
+ throw new InvalidEnumArgumentException(
99
+ nameof(compareMode),
100
+ (int)compareMode,
101
+ typeof(StringCompareMode)
102
+ );
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 2f66e3a62c374ef9a285d5e9e10f9fb2
3
+ timeCreated: 1748568941
@@ -19,8 +19,8 @@
19
19
  Assert.Throws<ArgumentException>(() => new CyclicBuffer<int>(int.MinValue));
20
20
  for (int i = 0; i < NumTries; i++)
21
21
  {
22
- Assert.Throws<ArgumentException>(
23
- () => new CyclicBuffer<int>(PRNG.Instance.Next(int.MinValue, -1))
22
+ Assert.Throws<ArgumentException>(() =>
23
+ new CyclicBuffer<int>(PRNG.Instance.Next(int.MinValue, -1))
24
24
  );
25
25
  }
26
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.wallstop-studios.unity-helpers",
3
- "version": "2.0.0-rc76.2",
3
+ "version": "2.0.0-rc76.5",
4
4
  "displayName": "Unity Helpers",
5
5
  "description": "Various Unity Helper Library",
6
6
  "dependencies": {},
@@ -74,6 +74,9 @@
74
74
 
75
75
 
76
76
 
77
+
78
+
79
+
77
80
 
78
81
 
79
82
 
package/Editor/UI.meta DELETED
@@ -1,3 +0,0 @@
1
- fileFormatVersion: 2
2
- guid: 6b8ee675c4ab4498bbf073237de1d78b
3
- timeCreated: 1748371749