com.wallstop-studios.unity-helpers 1.0.0-rc7 → 1.0.0-rc9

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.
@@ -1,5 +1,6 @@
1
1
  namespace UnityHelpers.Editor
2
2
  {
3
+ #if UNITY_EDITOR
3
4
  using System;
4
5
  using System.Collections.Generic;
5
6
  using System.Linq;
@@ -14,14 +15,9 @@
14
15
  public const int DefaultFramesPerSecond = 12;
15
16
 
16
17
  public List<Texture2D> frames;
17
- public int framesPerSecond;
18
+ public int framesPerSecond = DefaultFramesPerSecond;
18
19
  public string animationName;
19
20
  public string assetPath;
20
-
21
- public AnimationData()
22
- {
23
- framesPerSecond = DefaultFramesPerSecond;
24
- }
25
21
  }
26
22
 
27
23
  public sealed class AnimationCreator : ScriptableWizard
@@ -215,4 +211,5 @@
215
211
  }
216
212
  }
217
213
  }
214
+ #endif
218
215
  }
@@ -1,5 +1,6 @@
1
1
  namespace UnityHelpers.Editor
2
2
  {
3
+ #if UNITY_EDITOR
3
4
  using System;
4
5
  using System.Linq;
5
6
  using System.Collections.Generic;
@@ -138,11 +139,14 @@
138
139
  }
139
140
 
140
141
  frameRate = _currentClip.frameRate = EditorGUILayout.FloatField("FrameRate", frameRate);
142
+ DrawGuiLine(height: 5, color: new Color(0f, 0.5f, 1f, 1f));
141
143
  _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
142
144
 
143
145
  // Need a copy because we might be mutating it
144
- foreach (AnimationEventItem item in _state.ToList())
146
+ List<AnimationEventItem> stateCopy = _state.ToList();
147
+ for (int i = 0; i < stateCopy.Count; ++i)
145
148
  {
149
+ AnimationEventItem item = stateCopy[i];
146
150
  AnimationEvent animEvent = item.animationEvent;
147
151
 
148
152
  int frame = Mathf.RoundToInt(animEvent.time * oldFrameRate);
@@ -151,11 +155,15 @@
151
155
  DrawSpritePreview(item);
152
156
 
153
157
  EditorGUI.indentLevel++;
154
-
155
158
  RenderAnimationEventItem(item, frame, frameRate);
156
-
159
+
160
+ if (i != stateCopy.Count - 1)
161
+ {
162
+ DrawGuiLine(height: 3, color: new Color(0f, 1f, 0.3f, 1f));
163
+ EditorGUILayout.Space();
164
+ }
165
+
157
166
  EditorGUI.indentLevel--;
158
- EditorGUILayout.Space();
159
167
  }
160
168
 
161
169
  EditorGUILayout.EndScrollView();
@@ -640,7 +648,7 @@
640
648
  private bool TryFindSpriteForEvent(AnimationEventItem item, out Sprite sprite)
641
649
  {
642
650
  sprite = null;
643
- foreach (ObjectReferenceKeyframe keyFrame in _referenceCurve)
651
+ foreach (ObjectReferenceKeyframe keyFrame in _referenceCurve ?? Enumerable.Empty<ObjectReferenceKeyframe>())
644
652
  {
645
653
  if (keyFrame.time <= item.animationEvent.time)
646
654
  {
@@ -738,5 +746,15 @@
738
746
  }
739
747
  }
740
748
  }
749
+
750
+ private void DrawGuiLine(int height = 1, Color? color = null)
751
+ {
752
+ Rect rect = EditorGUILayout.GetControlRect(false, height);
753
+ rect.height = height;
754
+ int minusWidth = EditorGUI.indentLevel * 16;
755
+ rect.xMin += minusWidth;
756
+ EditorGUI.DrawRect(rect, color ?? new Color(0.5f, 0.5f, 0.5f, 1f));
757
+ }
741
758
  }
759
+ #endif
742
760
  }
@@ -1,5 +1,6 @@
1
1
  namespace UnityHelpers.Editor
2
2
  {
3
+ #if UNITY_EDITOR
3
4
  using System;
4
5
  using System.Collections;
5
6
  using System.Collections.Generic;
@@ -137,4 +138,5 @@
137
138
  }
138
139
  }
139
140
  }
141
+ #endif
140
142
  }
@@ -18,18 +18,20 @@
18
18
 
19
19
  public static class ChildComponentExtensions
20
20
  {
21
- private static readonly Dictionary<Type, List<FieldInfo>> FieldsByType = new();
21
+ private static readonly Dictionary<Type, FieldInfo[]> FieldsByType = new();
22
22
 
23
23
  public static void AssignChildComponents(this Component component)
24
24
  {
25
25
  Type componentType = component.GetType();
26
- List<FieldInfo> fields = FieldsByType.GetOrAdd(componentType, type =>
27
- {
28
- FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
29
- return fields
30
- .Where(prop => Attribute.IsDefined(prop, typeof(ChildComponentAttribute)))
31
- .ToList();
32
- });
26
+ FieldInfo[] fields = FieldsByType.GetOrAdd(
27
+ componentType, type =>
28
+ {
29
+ FieldInfo[] fields = type.GetFields(
30
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
31
+ return fields
32
+ .Where(prop => Attribute.IsDefined(prop, typeof(ChildComponentAttribute)))
33
+ .ToArray();
34
+ });
33
35
 
34
36
  foreach (FieldInfo field in fields)
35
37
  {
@@ -42,7 +44,7 @@
42
44
  {
43
45
  Component[] childComponents = component.GetComponentsInChildren(childComponentType, true);
44
46
  foundChild = 0 < childComponents.Length;
45
-
47
+
46
48
  Array correctTypedArray = Array.CreateInstance(childComponentType, childComponents.Length);
47
49
  Array.Copy(childComponents, correctTypedArray, childComponents.Length);
48
50
  field.SetValue(component, correctTypedArray);
@@ -51,7 +53,7 @@
51
53
  {
52
54
  childComponentType = fieldType.GenericTypeArguments[0];
53
55
  Type constructedListType = typeof(List<>).MakeGenericType(childComponentType);
54
- IList instance = (IList) Activator.CreateInstance(constructedListType);
56
+ IList instance = (IList)Activator.CreateInstance(constructedListType);
55
57
 
56
58
  foundChild = false;
57
59
  foreach (Component childComponent in component.GetComponentsInChildren(childComponentType, true))
@@ -74,7 +76,10 @@
74
76
 
75
77
  if (!foundChild)
76
78
  {
77
- if (field.GetCustomAttributes(typeof(ChildComponentAttribute), false)[0] is ChildComponentAttribute { optional: false } _)
79
+ if (field.GetCustomAttributes(typeof(ChildComponentAttribute), false)[0] is ChildComponentAttribute
80
+ {
81
+ optional: false
82
+ } _)
78
83
  {
79
84
  component.LogError($"Unable to find child component of type {fieldType}");
80
85
  }
@@ -82,4 +87,4 @@
82
87
  }
83
88
  }
84
89
  }
85
- }
90
+ }
@@ -18,18 +18,20 @@
18
18
 
19
19
  public static class ParentComponentExtensions
20
20
  {
21
- private static readonly Dictionary<Type, List<FieldInfo>> FieldsByType = new();
21
+ private static readonly Dictionary<Type, FieldInfo[]> FieldsByType = new();
22
22
 
23
23
  public static void AssignParentComponents(this Component component)
24
24
  {
25
25
  Type componentType = component.GetType();
26
- List<FieldInfo> fields = FieldsByType.GetOrAdd(componentType, type =>
27
- {
28
- FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
29
- return fields
30
- .Where(prop => Attribute.IsDefined(prop, typeof(ParentComponentAttribute)))
31
- .ToList();
32
- });
26
+ FieldInfo[] fields = FieldsByType.GetOrAdd(
27
+ componentType, type =>
28
+ {
29
+ FieldInfo[] fields = type.GetFields(
30
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
31
+ return fields
32
+ .Where(prop => Attribute.IsDefined(prop, typeof(ParentComponentAttribute)))
33
+ .ToArray();
34
+ });
33
35
 
34
36
  foreach (FieldInfo field in fields)
35
37
  {
@@ -42,7 +44,7 @@
42
44
  {
43
45
  Component[] parentComponents = component.GetComponentsInParent(parentComponentType, true);
44
46
  foundParent = 0 < parentComponents.Length;
45
-
47
+
46
48
  Array correctTypedArray = Array.CreateInstance(parentComponentType, parentComponents.Length);
47
49
  Array.Copy(parentComponents, correctTypedArray, parentComponents.Length);
48
50
  field.SetValue(component, correctTypedArray);
@@ -51,7 +53,7 @@
51
53
  {
52
54
  parentComponentType = fieldType.GenericTypeArguments[0];
53
55
  Type constructedListType = typeof(List<>).MakeGenericType(parentComponentType);
54
- IList instance = (IList) Activator.CreateInstance(constructedListType);
56
+ IList instance = (IList)Activator.CreateInstance(constructedListType);
55
57
 
56
58
  foundParent = false;
57
59
  foreach (Component parentComponent in component.GetComponentsInParent(parentComponentType, true))
@@ -74,7 +76,8 @@
74
76
 
75
77
  if (!foundParent)
76
78
  {
77
- if (field.GetCustomAttributes(typeof(ParentComponentAttribute), false)[0] is ParentComponentAttribute { optional: false } _)
79
+ if (field.GetCustomAttributes(typeof(ParentComponentAttribute), false)[0] is
80
+ ParentComponentAttribute { optional: false } _)
78
81
  {
79
82
  component.LogError($"Unable to find parent component of type {fieldType}");
80
83
  }
@@ -82,4 +85,4 @@
82
85
  }
83
86
  }
84
87
  }
85
- }
88
+ }
@@ -18,18 +18,20 @@
18
18
 
19
19
  public static class SiblingComponentExtensions
20
20
  {
21
- private static readonly Dictionary<Type, List<FieldInfo>> FieldsByType = new();
21
+ private static readonly Dictionary<Type, FieldInfo[]> FieldsByType = new();
22
22
 
23
23
  public static void AssignSiblingComponents(this Component component)
24
24
  {
25
25
  Type componentType = component.GetType();
26
- List<FieldInfo> fields = FieldsByType.GetOrAdd(componentType,
26
+ FieldInfo[] fields = FieldsByType.GetOrAdd(
27
+ componentType,
27
28
  type =>
28
29
  {
29
- FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
30
+ FieldInfo[] fields = type.GetFields(
31
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
30
32
  return fields
31
33
  .Where(prop => Attribute.IsDefined(prop, typeof(SiblingComponentAttribute)))
32
- .ToList();
34
+ .ToArray();
33
35
  });
34
36
 
35
37
  foreach (FieldInfo field in fields)
@@ -52,7 +54,7 @@
52
54
  {
53
55
  siblingComponentType = fieldType.GenericTypeArguments[0];
54
56
  Type constructedListType = typeof(List<>).MakeGenericType(siblingComponentType);
55
- IList instance = (IList) Activator.CreateInstance(constructedListType);
57
+ IList instance = (IList)Activator.CreateInstance(constructedListType);
56
58
 
57
59
  foundSibling = false;
58
60
  foreach (Component siblingComponent in component.GetComponents(siblingComponentType))
@@ -78,7 +80,8 @@
78
80
 
79
81
  if (!foundSibling)
80
82
  {
81
- if (field.GetCustomAttributes(typeof(SiblingComponentAttribute), false)[0] is SiblingComponentAttribute {optional: false} _)
83
+ if (field.GetCustomAttributes(typeof(SiblingComponentAttribute), false)[0] is
84
+ SiblingComponentAttribute { optional: false } _)
82
85
  {
83
86
  component.LogError($"Unable to find sibling component of type {fieldType}");
84
87
  }
@@ -86,4 +89,4 @@
86
89
  }
87
90
  }
88
91
  }
89
- }
92
+ }
@@ -12,21 +12,26 @@
12
12
 
13
13
  [AttributeUsage(AttributeTargets.Field)]
14
14
  [MeansImplicitUse]
15
- public sealed class ValidateAssignmentAttribute : Attribute
16
- {
17
- }
15
+ public sealed class ValidateAssignmentAttribute : Attribute { }
18
16
 
19
17
  public static class ValidateAssignmentExtensions
20
18
  {
21
- private static readonly Dictionary<Type, List<FieldInfo>> FieldsByType = new();
19
+ private static readonly Dictionary<Type, FieldInfo[]> FieldsByType = new();
20
+
21
+ private static FieldInfo[] GetOrAdd(Type objectType)
22
+ {
23
+ return FieldsByType.GetOrAdd(
24
+ objectType, type => type
25
+ .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
26
+ .Where(prop => Attribute.IsDefined(prop, typeof(ValidateAssignmentAttribute)))
27
+ .ToArray());
28
+ }
22
29
 
23
30
  public static void ValidateAssignments(this Object o)
24
31
  {
25
32
  #if UNITY_EDITOR
26
33
  Type objectType = o.GetType();
27
- List<FieldInfo> fields = FieldsByType.GetOrAdd(objectType, type => type
28
- .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
29
- .Where(prop => Attribute.IsDefined(prop, typeof(ValidateAssignmentAttribute))).ToList());
34
+ FieldInfo[] fields = GetOrAdd(objectType);
30
35
 
31
36
  foreach (FieldInfo field in fields)
32
37
  {
@@ -43,24 +48,48 @@
43
48
  public static bool AreAnyAssignmentsInvalid(this Object o)
44
49
  {
45
50
  Type objectType = o.GetType();
46
- List<FieldInfo> fields = FieldsByType.GetOrAdd(objectType, type => type
47
- .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
48
- .Where(prop => Attribute.IsDefined(prop, typeof(ValidateAssignmentAttribute))).ToList());
51
+ FieldInfo[] fields = GetOrAdd(objectType);
49
52
 
50
- return fields.Any(field => IsFieldInvalid(field, o));
53
+ foreach (FieldInfo field in fields)
54
+ {
55
+ if (IsFieldInvalid(field, o))
56
+ {
57
+ return true;
58
+ }
59
+ }
60
+
61
+ return false;
62
+ }
63
+
64
+ private static bool IsInvalid(IEnumerable enumerable)
65
+ {
66
+ IEnumerator enumerator = enumerable.GetEnumerator();
67
+ try
68
+ {
69
+ return !enumerator.MoveNext();
70
+ }
71
+ finally
72
+ {
73
+ if (enumerator is IDisposable disposable)
74
+ {
75
+ disposable.Dispose();
76
+ }
77
+ }
51
78
  }
52
79
 
53
80
  private static bool IsFieldInvalid(FieldInfo field, Object o)
54
81
  {
55
82
  object fieldValue = field.GetValue(o);
83
+
56
84
  return fieldValue switch
57
85
  {
58
- IList list => list.Count <= 0,
59
- ICollection collection => collection.Count <= 0,
60
86
  Object unityObject => !unityObject,
61
87
  string stringValue => string.IsNullOrWhiteSpace(stringValue),
88
+ IList list => list.Count <= 0,
89
+ ICollection collection => collection.Count <= 0,
90
+ IEnumerable enumerable => IsInvalid(enumerable),
62
91
  _ => fieldValue == null
63
92
  };
64
93
  }
65
94
  }
66
- }
95
+ }
@@ -1,10 +1,14 @@
1
1
  namespace UnityHelpers.Core.Extension
2
2
  {
3
+ using System.Collections.Generic;
3
4
  using System.Text;
4
5
  using Serialization;
5
6
 
6
7
  public static class StringExtensions
7
8
  {
9
+ private static readonly HashSet<char> PascalCaseSeparators =
10
+ new() { '_', ' ', '\r', '\n', '\t', '.', '\'', '"' };
11
+
8
12
  public static string Center(this string input, int length)
9
13
  {
10
14
  if (input == null || length <= input.Length)
@@ -17,12 +21,12 @@
17
21
 
18
22
  public static byte[] GetBytes(this string input)
19
23
  {
20
- return System.Text.Encoding.Default.GetBytes(input);
24
+ return Encoding.Default.GetBytes(input);
21
25
  }
22
26
 
23
27
  public static string GetString(this byte[] bytes)
24
28
  {
25
- return System.Text.Encoding.Default.GetString(bytes);
29
+ return Encoding.Default.GetString(bytes);
26
30
  }
27
31
 
28
32
  public static string ToJson<T>(this T value)
@@ -34,29 +38,66 @@
34
38
  {
35
39
  int startIndex = 0;
36
40
  StringBuilder stringBuilder = new();
41
+ bool appendedAnySeparator = false;
37
42
  for (int i = 0; i < value.Length; ++i)
38
43
  {
39
- if (startIndex < i && char.IsLower(value[i - 1]) && char.IsUpper(value[i]))
44
+ while (startIndex < value.Length && PascalCaseSeparators.Contains(value[startIndex]))
45
+ {
46
+ ++startIndex;
47
+ }
48
+
49
+ if (startIndex < i && char.IsLower(value[i - 1]) &&
50
+ (char.IsUpper(value[i]) || PascalCaseSeparators.Contains(value[i])))
40
51
  {
41
52
  _ = stringBuilder.Append(char.ToUpper(value[startIndex]));
42
53
  if (1 < i - startIndex)
43
54
  {
44
- _ = stringBuilder.Append(value.Substring(startIndex + 1, i - 1 - startIndex).ToLower());
55
+ for (int j = startIndex + 1; j < i; ++j)
56
+ {
57
+ char current = value[j];
58
+ if (PascalCaseSeparators.Contains(current))
59
+ {
60
+ continue;
61
+ }
62
+
63
+ _ = stringBuilder.Append(char.ToLower(current));
64
+ }
65
+ }
66
+
67
+ if (!string.IsNullOrEmpty(separator))
68
+ {
69
+ appendedAnySeparator = true;
70
+ _ = stringBuilder.Append(separator);
45
71
  }
46
72
 
47
- _ = stringBuilder.Append(separator);
48
73
  startIndex = i;
49
74
  continue;
50
75
  }
51
76
 
52
- if (startIndex + 1 < i && char.IsLower(value[i]) && char.IsUpper(value[i - 1]))
77
+ if (startIndex + 1 < i && char.IsLower(value[i]) &&
78
+ (char.IsUpper(value[i - 1]) || PascalCaseSeparators.Contains(value[i - 1])))
53
79
  {
54
80
  _ = stringBuilder.Append(char.ToUpper(value[startIndex]));
55
81
  if (1 < i - 1 - startIndex)
56
82
  {
57
- _ = stringBuilder.Append(value.Substring(startIndex + 1, i - 1 - startIndex).ToLower());
83
+ for (int j = startIndex + 1; j < i; ++j)
84
+ {
85
+ char current = value[j];
86
+ if (PascalCaseSeparators.Contains(current))
87
+ {
88
+ continue;
89
+ }
90
+
91
+ _ = stringBuilder.Append(char.ToLower(current));
92
+ }
93
+ }
94
+
95
+ if (!string.IsNullOrEmpty(separator))
96
+ {
97
+ appendedAnySeparator = true;
98
+ _ = stringBuilder.Append(separator);
58
99
  }
59
- _ = stringBuilder.Append(separator);
100
+
60
101
  startIndex = i - 1;
61
102
  }
62
103
  }
@@ -66,11 +107,25 @@
66
107
  _ = stringBuilder.Append(char.ToUpper(value[startIndex]));
67
108
  if (startIndex + 1 < value.Length)
68
109
  {
69
- _ = stringBuilder.Append(value.Substring(startIndex + 1, value.Length - 1 - startIndex).ToLower());
110
+ for (int j = startIndex + 1; j < value.Length; ++j)
111
+ {
112
+ char current = value[j];
113
+ if (PascalCaseSeparators.Contains(current))
114
+ {
115
+ continue;
116
+ }
117
+
118
+ _ = stringBuilder.Append(char.ToLower(current));
119
+ }
70
120
  }
71
121
  }
122
+ else if (appendedAnySeparator && !string.IsNullOrEmpty(separator) &&
123
+ separator.Length <= stringBuilder.Length)
124
+ {
125
+ stringBuilder.Remove(stringBuilder.Length - separator.Length, separator.Length);
126
+ }
72
127
 
73
128
  return stringBuilder.ToString();
74
129
  }
75
130
  }
76
- }
131
+ }
@@ -55,7 +55,9 @@
55
55
 
56
56
  if (log)
57
57
  {
58
- component.LogWarn("Failed to find {0} on {1} (name: {2}), id [{3}].", typeof(T).Name, tag, gameObject.name, gameObject.GetInstanceID());
58
+ component.LogWarn(
59
+ "Failed to find {0} on {1} (name: {2}), id [{3}].", typeof(T).Name, tag, gameObject.name,
60
+ gameObject.GetInstanceID());
59
61
  }
60
62
 
61
63
  return default;
@@ -111,9 +113,9 @@
111
113
  }
112
114
  }
113
115
 
114
- public static bool HasComponent<T>(this Object unityObject)
116
+ public static bool HasComponent<T>(this Object unityObject) where T : Object
115
117
  {
116
- return (unityObject) switch
118
+ return unityObject switch
117
119
  {
118
120
  GameObject go => go.HasComponent<T>(),
119
121
  Component component => component.HasComponent<T>(),
@@ -121,12 +123,12 @@
121
123
  };
122
124
  }
123
125
 
124
- public static bool HasComponent<T>(this Component component)
126
+ public static bool HasComponent<T>(this Component component) where T : Object
125
127
  {
126
128
  return component.TryGetComponent<T>(out _);
127
129
  }
128
130
 
129
- public static bool HasComponent<T>(this GameObject gameObject)
131
+ public static bool HasComponent<T>(this GameObject gameObject) where T : Object
130
132
  {
131
133
  return gameObject.TryGetComponent<T>(out _);
132
134
  }
@@ -162,7 +164,8 @@
162
164
  }
163
165
  }
164
166
 
165
- public static IEnumerable<GameObject> IterateOverChildGameObjectsRecursivelyIncludingSelf(this GameObject gameObject)
167
+ public static IEnumerable<GameObject> IterateOverChildGameObjectsRecursivelyIncludingSelf(
168
+ this GameObject gameObject)
166
169
  {
167
170
  yield return gameObject;
168
171
 
@@ -186,7 +189,8 @@
186
189
  }
187
190
  }
188
191
 
189
- public static IEnumerable<GameObject> IterateOverParentGameObjectsRecursivelyIncludingSelf(this GameObject gameObject)
192
+ public static IEnumerable<GameObject> IterateOverParentGameObjectsRecursivelyIncludingSelf(
193
+ this GameObject gameObject)
190
194
  {
191
195
  yield return gameObject;
192
196
 
@@ -225,7 +229,8 @@
225
229
  }
226
230
  }
227
231
 
228
- public static void EnableRendererRecursively<T>(this Component component, bool enabled,
232
+ public static void EnableRendererRecursively<T>(
233
+ this Component component, bool enabled,
229
234
  Func<T, bool> exclude = null) where T : Renderer
230
235
  {
231
236
  if (component == null)
@@ -388,31 +393,36 @@
388
393
  }
389
394
  }
390
395
 
391
- public static void DestroyAllChildrenGameObjectsImmediatelyConditionally(this GameObject gameObject,
396
+ public static void DestroyAllChildrenGameObjectsImmediatelyConditionally(
397
+ this GameObject gameObject,
392
398
  Func<GameObject, bool> acceptancePredicate)
393
399
  {
394
- InternalDestroyAllChildrenGameObjects(gameObject, toDestroy =>
395
- {
396
- if (!acceptancePredicate(toDestroy))
400
+ InternalDestroyAllChildrenGameObjects(
401
+ gameObject, toDestroy =>
397
402
  {
398
- return;
399
- }
403
+ if (!acceptancePredicate(toDestroy))
404
+ {
405
+ return;
406
+ }
400
407
 
401
- Object.DestroyImmediate(toDestroy);
402
- });
408
+ Object.DestroyImmediate(toDestroy);
409
+ });
403
410
  }
404
411
 
405
- public static void DestroyAllChildGameObjectsConditionally(this GameObject gameObject,
412
+ public static void DestroyAllChildGameObjectsConditionally(
413
+ this GameObject gameObject,
406
414
  Func<GameObject, bool> acceptancePredicate)
407
415
  {
408
- InternalDestroyAllChildrenGameObjects(gameObject, toDestroy =>
409
- {
410
- if (!acceptancePredicate(toDestroy))
416
+ InternalDestroyAllChildrenGameObjects(
417
+ gameObject, toDestroy =>
411
418
  {
412
- return;
413
- }
414
- toDestroy.Destroy();
415
- });
419
+ if (!acceptancePredicate(toDestroy))
420
+ {
421
+ return;
422
+ }
423
+
424
+ toDestroy.Destroy();
425
+ });
416
426
  }
417
427
 
418
428
  public static void DestroyAllChildrenGameObjectsImmediately(this GameObject gameObject) =>
@@ -424,7 +434,8 @@
424
434
  public static void EditorDestroyAllChildrenGameObjects(this GameObject gameObject) =>
425
435
  InternalDestroyAllChildrenGameObjects(gameObject, go => go.Destroy());
426
436
 
427
- private static void InternalDestroyAllChildrenGameObjects(this GameObject gameObject,
437
+ private static void InternalDestroyAllChildrenGameObjects(
438
+ this GameObject gameObject,
428
439
  Action<GameObject> destroyFunction)
429
440
  {
430
441
  for (int i = gameObject.transform.childCount - 1; 0 <= i; --i)
@@ -441,8 +452,16 @@
441
452
  {
442
453
  return true;
443
454
  }
444
- #endif
455
+
456
+ return PrefabUtility.GetPrefabAssetType(gameObject) switch
457
+ {
458
+ PrefabAssetType.NotAPrefab => false,
459
+ PrefabAssetType.MissingAsset => scene.rootCount == 0,
460
+ _ => true,
461
+ };
462
+ #else
445
463
  return scene.rootCount == 0;
464
+ #endif
446
465
  }
447
466
 
448
467
  public static bool IsPrefab(this Component component)
@@ -516,7 +535,9 @@
516
535
  }
517
536
 
518
537
  // https://gamedevelopment.tutsplus.com/tutorials/unity-solution-for-hitting-moving-targets--cms-29633
519
- public static Vector2 PredictCurrentTarget(this GameObject currentTarget, Vector2 launchLocation, float projectileSpeed, bool predictiveFiring, Vector2 targetVelocity)
538
+ public static Vector2 PredictCurrentTarget(
539
+ this GameObject currentTarget, Vector2 launchLocation, float projectileSpeed, bool predictiveFiring,
540
+ Vector2 targetVelocity)
520
541
  {
521
542
  Vector2 target = currentTarget.transform.position;
522
543
 
@@ -530,8 +551,9 @@
530
551
  return target;
531
552
  }
532
553
 
533
- float a = (targetVelocity.x * targetVelocity.x) + (targetVelocity.y * targetVelocity.y) - (projectileSpeed * projectileSpeed);
534
-
554
+ float a = (targetVelocity.x * targetVelocity.x) + (targetVelocity.y * targetVelocity.y) -
555
+ (projectileSpeed * projectileSpeed);
556
+
535
557
  float b = 2 * (targetVelocity.x * (target.x - launchLocation.x) +
536
558
  targetVelocity.y * (target.y - launchLocation.y));
537
559
 
@@ -593,7 +615,7 @@
593
615
  {
594
616
  GameObject go => go,
595
617
  Component c => c.gameObject,
596
- _ => default
618
+ _ => null
597
619
  };
598
620
  }
599
621
 
@@ -610,10 +632,12 @@
610
632
 
611
633
  public static GameObject FindChildGameObjectWithTag(this GameObject gameObject, string tag)
612
634
  {
613
- return gameObject.IterateOverChildGameObjectsRecursivelyIncludingSelf().FirstOrDefault(child => child.CompareTag(tag));
635
+ return gameObject.IterateOverChildGameObjectsRecursivelyIncludingSelf()
636
+ .FirstOrDefault(child => child.CompareTag(tag));
614
637
  }
615
638
 
616
- public static bool HasLineOfSight(Vector2 initialLocation, Vector2 direction, Transform transform, float totalDistance, float delta)
639
+ public static bool HasLineOfSight(
640
+ Vector2 initialLocation, Vector2 direction, Transform transform, float totalDistance, float delta)
617
641
  {
618
642
  int hits = Physics2D.RaycastNonAlloc(initialLocation, direction, Buffers.RaycastHits);
619
643
  for (int i = 0; i < hits; ++i)
@@ -634,7 +658,9 @@
634
658
  return true;
635
659
  }
636
660
 
637
- public static Coroutine StartFunctionAsCoroutine(this MonoBehaviour monoBehaviour, Action action, float updateRate, bool useJitter = false, bool waitBefore = false)
661
+ public static Coroutine StartFunctionAsCoroutine(
662
+ this MonoBehaviour monoBehaviour, Action action, float updateRate, bool useJitter = false,
663
+ bool waitBefore = false)
638
664
  {
639
665
  return monoBehaviour.StartCoroutine(FunctionAsCoroutine(action, updateRate, useJitter, waitBefore));
640
666
  }
@@ -642,30 +668,51 @@
642
668
  private static IEnumerator FunctionAsCoroutine(Action action, float updateRate, bool useJitter, bool waitBefore)
643
669
  {
644
670
  bool usedJitter = false;
645
- WaitForSeconds wait = new(updateRate);
646
-
647
671
  while (true)
648
672
  {
673
+ float startTime;
649
674
  if (waitBefore)
650
675
  {
651
- // Copy-pasta the code, no way to unify in a performant way without generating garbage
652
- yield return wait;
653
676
  if (useJitter && !usedJitter)
654
677
  {
655
- yield return new WaitForSeconds(PcgRandom.Instance.NextFloat(updateRate));
678
+ float delay = PcgRandom.Instance.NextFloat(updateRate);
679
+ startTime = Time.time;
680
+ while (!HasEnoughTimePassed(startTime, delay))
681
+ {
682
+ yield return null;
683
+ }
684
+
656
685
  usedJitter = true;
657
686
  }
687
+
688
+ startTime = Time.time;
689
+ while (!HasEnoughTimePassed(startTime, updateRate))
690
+ {
691
+ yield return null;
692
+ }
658
693
  }
659
694
 
660
695
  action();
696
+
661
697
  if (!waitBefore)
662
698
  {
663
- yield return wait;
664
699
  if (useJitter && !usedJitter)
665
700
  {
666
- yield return new WaitForSeconds(PcgRandom.Instance.NextFloat(updateRate));
701
+ float delay = PcgRandom.Instance.NextFloat(updateRate);
702
+ startTime = Time.time;
703
+ while (!HasEnoughTimePassed(startTime, delay))
704
+ {
705
+ yield return null;
706
+ }
707
+
667
708
  usedJitter = true;
668
709
  }
710
+
711
+ startTime = Time.time;
712
+ while (!HasEnoughTimePassed(startTime, updateRate))
713
+ {
714
+ yield return null;
715
+ }
669
716
  }
670
717
  }
671
718
  }
@@ -727,6 +774,7 @@
727
774
  {
728
775
  yield return null;
729
776
  }
777
+
730
778
  action();
731
779
  }
732
780
 
@@ -736,7 +784,8 @@
736
784
  action();
737
785
  }
738
786
 
739
- public static bool HasEnoughTimePassed(float timestamp, float desiredDuration) => timestamp + desiredDuration < Time.time;
787
+ public static bool HasEnoughTimePassed(float timestamp, float desiredDuration) =>
788
+ timestamp + desiredDuration < Time.time;
740
789
 
741
790
  public static Vector2 Opposite(this Vector2 vector)
742
791
  {
@@ -774,12 +823,12 @@
774
823
 
775
824
  public static Vector3Int AsVector3Int(this (uint x, uint y, uint z) vector)
776
825
  {
777
- return new Vector3Int((int) vector.x, (int) vector.y, (int) vector.z);
826
+ return new Vector3Int((int)vector.x, (int)vector.y, (int)vector.z);
778
827
  }
779
828
 
780
829
  public static Vector3Int AsVector3Int(this Vector3 vector)
781
830
  {
782
- return new Vector3Int((int) Math.Round(vector.x), (int) Math.Round(vector.y), (int) Math.Round(vector.z));
831
+ return new Vector3Int((int)Math.Round(vector.x), (int)Math.Round(vector.y), (int)Math.Round(vector.z));
783
832
  }
784
833
 
785
834
  public static Vector3 AsVector3(this (uint x, uint y, uint z) vector)
@@ -829,7 +878,7 @@
829
878
  }
830
879
 
831
880
  foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
832
- .Where(field => Attribute.IsDefined(field, typeof(SerializeField))))
881
+ .Where(field => Attribute.IsDefined(field, typeof(SerializeField))))
833
882
  {
834
883
  try
835
884
  {
@@ -881,13 +930,15 @@
881
930
 
882
931
  public static GameObject GetTagObjectInChildHierarchy(this GameObject gameObject, string tag)
883
932
  {
884
- return gameObject.IterateOverChildGameObjectsRecursivelyIncludingSelf().FirstOrDefault(go => go.CompareTag(tag));
933
+ return gameObject.IterateOverChildGameObjectsRecursivelyIncludingSelf()
934
+ .FirstOrDefault(go => go.CompareTag(tag));
885
935
  }
886
936
 
887
937
  //https://answers.unity.com/questions/722748/refreshing-the-polygon-collider-2d-upon-sprite-cha.html
888
938
  public static void UpdateShapeToSprite(this Component component)
889
939
  {
890
- if (!component.TryGetComponent(out SpriteRenderer spriteRenderer) || component.TryGetComponent(out PolygonCollider2D collider))
940
+ if (!component.TryGetComponent(out SpriteRenderer spriteRenderer) ||
941
+ component.TryGetComponent(out PolygonCollider2D collider))
891
942
  {
892
943
  return;
893
944
  }
@@ -922,7 +973,8 @@
922
973
  return new Vector3Int(x, y, z);
923
974
  }
924
975
 
925
- public static GameObject TryGetClosestParentWithComponentIncludingSelf<T>(this GameObject current) where T : Component
976
+ public static GameObject TryGetClosestParentWithComponentIncludingSelf<T>(this GameObject current)
977
+ where T : Component
926
978
  {
927
979
  while (current != null)
928
980
  {
@@ -941,7 +993,7 @@
941
993
  #if UNITY_EDITOR
942
994
  public static IEnumerable<GameObject> EnumeratePrefabs(IEnumerable<string> assetPaths = null)
943
995
  {
944
- assetPaths ??= new[] {"Assets/Prefabs", "Assets/Resources"};
996
+ assetPaths ??= new[] { "Assets/Prefabs", "Assets/Resources" };
945
997
 
946
998
  foreach (string assetGuid in AssetDatabase.FindAssets("t:prefab", assetPaths.ToArray()))
947
999
  {
@@ -954,7 +1006,8 @@
954
1006
  }
955
1007
  }
956
1008
 
957
- public static IEnumerable<T> EnumerateScriptableObjects<T>(string[] assetPaths = null) where T : ScriptableObject
1009
+ public static IEnumerable<T> EnumerateScriptableObjects<T>(string[] assetPaths = null)
1010
+ where T : ScriptableObject
958
1011
  {
959
1012
  assetPaths ??= new[] { "Assets/Prefabs", "Assets/Resources", "Assets/TileMaps" };
960
1013
 
@@ -1038,7 +1091,10 @@
1038
1091
  {
1039
1092
  foreach (MonoBehaviour script in gameObject.GetComponentsInChildren<MonoBehaviour>())
1040
1093
  {
1041
- MethodInfo awakeInfo = AwakeMethodsByType.GetOrAdd(script.GetType(), type => type.GetMethod("Awake", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
1094
+ MethodInfo awakeInfo = AwakeMethodsByType.GetOrAdd(
1095
+ script.GetType(),
1096
+ type => type.GetMethod(
1097
+ "Awake", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
1042
1098
  if (awakeInfo != null)
1043
1099
  {
1044
1100
  _ = awakeInfo.Invoke(script, null);
@@ -1093,4 +1149,4 @@
1093
1149
  }
1094
1150
  }
1095
1151
  }
1096
- }
1152
+ }
@@ -0,0 +1,31 @@
1
+ namespace UnityHelpers.Tests.Extensions
2
+ {
3
+ using Core.Extension;
4
+ using NUnit.Framework;
5
+
6
+ public class StringExtensionTests
7
+ {
8
+ [Test]
9
+ public void ToPascalCaseNominal()
10
+ {
11
+ Assert.AreEqual("PascalCase", "PascalCase".ToPascalCase());
12
+ Assert.AreEqual("PascalCase", "pascalCase".ToPascalCase());
13
+ Assert.AreEqual("PascalCase", "_pascalCase".ToPascalCase());
14
+ Assert.AreEqual("PascalCase", "_PascalCase".ToPascalCase());
15
+ Assert.AreEqual("PascalCase", "pascal case".ToPascalCase());
16
+ Assert.AreEqual("PascalCase", " __Pascal____ ___Case__ ".ToPascalCase());
17
+ }
18
+
19
+ [Test]
20
+ public void ToPascalCaseCustomSeparator()
21
+ {
22
+ const string separator = ".";
23
+ Assert.AreEqual("Pascal.Case", "PascalCase".ToPascalCase(separator));
24
+ Assert.AreEqual("Pascal.Case", "pascalCase".ToPascalCase(separator));
25
+ Assert.AreEqual("Pascal.Case", "_pascalCase".ToPascalCase(separator));
26
+ Assert.AreEqual("Pascal.Case", "_PascalCase".ToPascalCase(separator));
27
+ Assert.AreEqual("Pascal.Case", "pascal case".ToPascalCase(separator));
28
+ Assert.AreEqual("Pascal.Case", " __Pascal____ ___Case__ ".ToPascalCase(separator));
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 522329788bc543aab266b1d91a8e2b47
3
+ timeCreated: 1720391633
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 3b57270da98c4ce8bb8714dfb1603ee0
3
+ timeCreated: 1720391613
@@ -2,7 +2,6 @@
2
2
  {
3
3
  using System;
4
4
  using System.Collections.Generic;
5
- using System.Diagnostics;
6
5
  using System.Linq;
7
6
  using NUnit.Framework;
8
7
  using UnityHelpers.Core.Random;
@@ -11,7 +10,7 @@
11
10
  {
12
11
  protected const int SampleCount = 10_000_000;
13
12
 
14
- protected int[] _samples = new int[1_000];
13
+ protected readonly int[] _samples = new int[1_000];
15
14
 
16
15
  protected abstract IRandom NewRandom();
17
16
 
@@ -48,19 +47,23 @@
48
47
  [Test]
49
48
  public void Byte()
50
49
  {
51
- TestAndVerify(random => random.NextByte(0, (byte)(_samples.Length < byte.MaxValue ? _samples.Length : byte.MaxValue)), byte.MaxValue);
50
+ TestAndVerify(
51
+ random => random.NextByte(0, (byte)(_samples.Length < byte.MaxValue ? _samples.Length : byte.MaxValue)),
52
+ byte.MaxValue);
52
53
  }
53
54
 
54
55
  [Test]
55
56
  public void Float()
56
57
  {
57
- TestAndVerify(random => (int)Math.Floor(random.NextFloat(0, _samples.Length)));
58
+ TestAndVerify(
59
+ random => Math.Clamp((int)Math.Floor(random.NextFloat(0, _samples.Length)), 0, _samples.Length - 1));
58
60
  }
59
61
 
60
62
  [Test]
61
63
  public void Double()
62
64
  {
63
- TestAndVerify(random => (int)Math.Floor(random.NextDouble(0, _samples.Length)));
65
+ TestAndVerify(
66
+ random => Math.Clamp((int)Math.Floor(random.NextDouble(0, _samples.Length)), 0, _samples.Length - 1));
64
67
  }
65
68
 
66
69
  [Test]
@@ -110,8 +113,13 @@
110
113
  }
111
114
  }
112
115
 
113
- Assert.AreEqual(0, zeroCountIndexes.Count, "No samples at {0} indices: [{1}]", zeroCountIndexes.Count, string.Join(",", zeroCountIndexes));
114
- Assert.AreEqual(0, outsideRange.Count, "{0} indexes outside of dev {1:0.00}. Expected: {2:0.00}. Found: [{3}]", outsideRange.Count, deviationAllowed, average, string.Join(",", outsideRange.Select(index => _samples[index])));
116
+ Assert.AreEqual(
117
+ 0, zeroCountIndexes.Count, "No samples at {0} indices: [{1}]", zeroCountIndexes.Count,
118
+ string.Join(",", zeroCountIndexes));
119
+ Assert.AreEqual(
120
+ 0, outsideRange.Count, "{0} indexes outside of dev {1:0.00}. Expected: {2:0.00}. Found: [{3}]",
121
+ outsideRange.Count, deviationAllowed, average,
122
+ string.Join(",", outsideRange.Select(index => _samples[index])));
115
123
  }
116
124
  }
117
- }
125
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.wallstop-studios.unity-helpers",
3
- "version": "1.0.0-rc7",
3
+ "version": "1.0.0-rc9",
4
4
  "displayName": "Unity Helpers",
5
5
  "description": "Various Unity Helper Library",
6
6
  "dependencies": {
@@ -1,43 +0,0 @@
1
- namespace UnityHelpers.Core.Attributes
2
- {
3
- using System;
4
- using System.Linq;
5
- using System.Reflection;
6
- using JetBrains.Annotations;
7
- using UnityEngine;
8
-
9
- [AttributeUsage(AttributeTargets.Field)]
10
- [MeansImplicitUse]
11
- public sealed class AutomaticallyFindAttribute : Attribute
12
- {
13
- public readonly string Tag = string.Empty;
14
- }
15
-
16
- public static class AutomaticallyFindExtensions
17
- {
18
- public static void AutomaticallyFind(this Component component)
19
- {
20
- var properties = component
21
- .GetType()
22
- .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
23
- .Where(prop => Attribute.IsDefined(prop, typeof(AutomaticallyFindAttribute)));
24
-
25
- foreach (FieldInfo field in properties)
26
- {
27
- Type fieldType = field.FieldType;
28
- Type type = fieldType.IsArray ? fieldType.GetElementType() : fieldType;
29
-
30
- if (type == typeof(GameObject))
31
- {
32
- var attribute = field.GetCustomAttributes(typeof(AutomaticallyFindAttribute), false)[0] as AutomaticallyFindAttribute;
33
- GameObject gameObject = GameObject.FindWithTag(attribute.Tag);
34
- field.SetValue(component, gameObject);
35
- return;
36
- }
37
-
38
- object foundObject = type.GetMethod("Find", BindingFlags.Public | BindingFlags.Static).Invoke(null, null);
39
- field.SetValue(component, foundObject);
40
- }
41
- }
42
- }
43
- }
@@ -1,11 +0,0 @@
1
- fileFormatVersion: 2
2
- guid: 26082413782e4f3e800873e5495f494b
3
- MonoImporter:
4
- externalObjects: {}
5
- serializedVersion: 2
6
- defaultReferences: []
7
- executionOrder: 0
8
- icon: {instanceID: 0}
9
- userData:
10
- assetBundleName:
11
- assetBundleVariant: