com.wallstop-studios.unity-helpers 2.0.0-rc49 → 2.0.0-rc51

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 (28) hide show
  1. package/Runtime/Core/Extension/UnityExtensions.cs +8 -0
  2. package/Runtime/Core/Helper/SceneHelper.cs +216 -0
  3. package/Runtime/Core/Helper/SceneHelper.cs.meta +3 -0
  4. package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +32 -0
  5. package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs.meta +3 -0
  6. package/Runtime/Core/Random/AbstractRandom.cs +27 -4
  7. package/Runtime/Core/Serialization/JsonConverters/ColorConverter.cs +88 -0
  8. package/Runtime/Core/Serialization/JsonConverters/ColorConverter.cs.meta +3 -0
  9. package/Runtime/Core/Serialization/JsonConverters/GameObjectConverter.cs +37 -0
  10. package/Runtime/Core/Serialization/JsonConverters/GameObjectConverter.cs.meta +3 -0
  11. package/Runtime/Core/Serialization/JsonConverters/Matrix4x4Converter.cs +218 -0
  12. package/Runtime/Core/Serialization/JsonConverters/Matrix4x4Converter.cs.meta +3 -0
  13. package/Runtime/Core/Serialization/JsonConverters/Vector2Converter.cs +2 -2
  14. package/Runtime/Core/Serialization/JsonConverters/Vector3Converter.cs +3 -3
  15. package/Runtime/Core/Serialization/JsonConverters/Vector4Converter.cs +88 -0
  16. package/Runtime/Core/Serialization/JsonConverters/Vector4Converter.cs.meta +3 -0
  17. package/Runtime/Core/Serialization/Serializer.cs +8 -0
  18. package/Runtime/Utils/DeferredDisposalResult.cs +19 -0
  19. package/Runtime/Utils/DeferredDisposalResult.cs.meta +3 -0
  20. package/Tests/Runtime/Helper/SceneHelperTests.cs +92 -0
  21. package/Tests/Runtime/Helper/SceneHelperTests.cs.meta +3 -0
  22. package/Tests/Runtime/Scenes/Test1.unity +723 -0
  23. package/Tests/Runtime/Scenes/Test1.unity.meta +7 -0
  24. package/Tests/Runtime/Scenes/Test2.unity +723 -0
  25. package/Tests/Runtime/Scenes/Test2.unity.meta +7 -0
  26. package/Tests/Runtime/Scenes.meta +3 -0
  27. package/Tests/Runtime/Serialization/JsonSerializationTest.cs +64 -1
  28. package/package.json +1 -1
@@ -3,6 +3,7 @@
3
3
  using System;
4
4
  using System.Collections.Generic;
5
5
  using System.Linq;
6
+ using System.Threading.Tasks;
6
7
  using DataStructure;
7
8
  using DataStructure.Adapters;
8
9
  using Helper;
@@ -1577,6 +1578,13 @@
1577
1578
  return false;
1578
1579
  }
1579
1580
 
1581
+ public static Task AsTask(this AsyncOperation asyncOp)
1582
+ {
1583
+ TaskCompletionSource<bool> taskCompletionSource = new();
1584
+ asyncOp.completed += _ => taskCompletionSource.SetResult(true);
1585
+ return taskCompletionSource.Task;
1586
+ }
1587
+
1580
1588
  #if UNITY_EDITOR
1581
1589
  public static IEnumerable<Sprite> GetSpritesFromClip(this AnimationClip clip)
1582
1590
  {
@@ -0,0 +1,216 @@
1
+ namespace UnityHelpers.Core.Helper
2
+ {
3
+ using System;
4
+ using System.Linq;
5
+ using System.Threading.Tasks;
6
+ using Extension;
7
+ using UnityEngine;
8
+ using UnityEngine.Events;
9
+ using UnityEngine.SceneManagement;
10
+ using Utils;
11
+ using Object = UnityEngine.Object;
12
+ #if UNITY_EDITOR
13
+ using UnityEditor;
14
+ using UnityEditor.SceneManagement;
15
+ #endif
16
+
17
+ public static class SceneHelper
18
+ {
19
+ public static string[] GetAllScenePaths(string[] searchFolders = null)
20
+ {
21
+ #if UNITY_EDITOR
22
+ searchFolders ??= Array.Empty<string>();
23
+ return AssetDatabase
24
+ .FindAssets("t:Scene", searchFolders)
25
+ .Select(AssetDatabase.GUIDToAssetPath)
26
+ .ToArray();
27
+ #else
28
+ return Array.Empty<string>();
29
+ #endif
30
+ }
31
+
32
+ public static string[] GetScenesInBuild()
33
+ {
34
+ #if UNITY_EDITOR
35
+ return EditorBuildSettings
36
+ .scenes.Where(scene => scene.enabled)
37
+ .Select(scene => scene.path)
38
+ .ToArray();
39
+ #else
40
+ return Array.Empty<string>();
41
+ #endif
42
+ }
43
+
44
+ public static async Task<DeferredDisposalResult<T>> GetObjectOfTypeInScene<T>(
45
+ string scenePath
46
+ )
47
+ where T : Object
48
+ {
49
+ DeferredDisposalResult<T[]> result = await GetAllObjectsOfTypeInScene<T>(scenePath);
50
+ return new DeferredDisposalResult<T>(
51
+ result.Result.FirstOrDefault(),
52
+ result.DisposeAsync
53
+ );
54
+ }
55
+
56
+ public static async Task<DeferredDisposalResult<T[]>> GetAllObjectsOfTypeInScene<T>(
57
+ string scenePath
58
+ )
59
+ where T : Object
60
+ {
61
+ // Ensure singleton is created
62
+ _ = UnityMainThreadDispatcher.Instance;
63
+ TaskCompletionSource<T[]> taskCompletionSource = new();
64
+
65
+ SceneLoadScope sceneScope = new(scenePath, OnSceneLoaded);
66
+ return await taskCompletionSource.Task.ContinueWith(result =>
67
+ {
68
+ return new DeferredDisposalResult<T[]>(
69
+ result.Result,
70
+ async () =>
71
+ {
72
+ TaskCompletionSource<bool> disposalComplete = new();
73
+ UnityMainThreadDispatcher.Instance.RunOnMainThread(
74
+ () =>
75
+ sceneScope
76
+ .DisposeAsync()
77
+ .ContinueWith(_ => disposalComplete.SetResult(true))
78
+ );
79
+
80
+ await disposalComplete.Task;
81
+ }
82
+ );
83
+ });
84
+
85
+ void OnSceneLoaded(Scene scene, LoadSceneMode mode)
86
+ {
87
+ if (!string.Equals(scene.path, scenePath, StringComparison.Ordinal))
88
+ {
89
+ return;
90
+ }
91
+
92
+ T[] foundObjects = Object
93
+ .FindObjectsByType<T>(FindObjectsInactive.Include, FindObjectsSortMode.None)
94
+ .Where(obj =>
95
+ {
96
+ GameObject go = obj.GetGameObject();
97
+ if (go == null)
98
+ {
99
+ return false;
100
+ }
101
+
102
+ return go.scene == scene;
103
+ })
104
+ .ToArray();
105
+ taskCompletionSource.TrySetResult(foundObjects);
106
+ }
107
+ }
108
+
109
+ public sealed class SceneLoadScope
110
+ {
111
+ private Scene? _openedScene;
112
+ private readonly UnityAction<Scene, LoadSceneMode> _onSceneLoaded;
113
+ private readonly bool _eventAdded;
114
+
115
+ public SceneLoadScope(string scenePath, UnityAction<Scene, LoadSceneMode> onSceneLoaded)
116
+ {
117
+ _onSceneLoaded = onSceneLoaded;
118
+ _eventAdded = false;
119
+ Scene activeScene = SceneManager.GetActiveScene();
120
+ if (
121
+ !activeScene.IsValid()
122
+ || !activeScene.isLoaded
123
+ || !string.Equals(activeScene.path, scenePath, StringComparison.Ordinal)
124
+ )
125
+ {
126
+ #if UNITY_EDITOR
127
+ if (Application.isPlaying)
128
+ {
129
+ SceneManager.sceneLoaded += onSceneLoaded;
130
+ _eventAdded = true;
131
+ _openedScene = EditorSceneManager.LoadSceneInPlayMode(
132
+ scenePath,
133
+ new LoadSceneParameters(LoadSceneMode.Additive, LocalPhysicsMode.None)
134
+ );
135
+ }
136
+ else
137
+ {
138
+ _openedScene = EditorSceneManager.OpenScene(
139
+ scenePath,
140
+ OpenSceneMode.Additive
141
+ );
142
+ onSceneLoaded?.Invoke(_openedScene.Value, LoadSceneMode.Additive);
143
+ }
144
+ #else
145
+ SceneManager.sceneLoaded += onSceneLoaded;
146
+ _eventAdded = true;
147
+ SceneManager.sceneLoaded += LocalSceneLoaded;
148
+ SceneManager.LoadScene(scenePath, LoadSceneMode.Additive);
149
+ _openedScene = SceneManager.GetSceneByPath(scenePath);
150
+
151
+ void LocalSceneLoaded(Scene scene, LoadSceneMode mode)
152
+ {
153
+ if (!string.Equals(scene.path, scenePath, StringComparison.Ordinal))
154
+ {
155
+ return;
156
+ }
157
+
158
+ _openedScene = scene;
159
+ SceneManager.sceneLoaded -= LocalSceneLoaded;
160
+ }
161
+ #endif
162
+ }
163
+ else
164
+ {
165
+ onSceneLoaded?.Invoke(activeScene, LoadSceneMode.Single);
166
+ _openedScene = null;
167
+ }
168
+ }
169
+
170
+ public async Task DisposeAsync()
171
+ {
172
+ if (_eventAdded)
173
+ {
174
+ SceneManager.sceneLoaded -= _onSceneLoaded;
175
+ }
176
+
177
+ if (_openedScene == null)
178
+ {
179
+ return;
180
+ }
181
+
182
+ Scene openedScene = _openedScene.Value;
183
+ if (!openedScene.IsValid())
184
+ {
185
+ return;
186
+ }
187
+
188
+ if (!openedScene.isLoaded)
189
+ {
190
+ return;
191
+ }
192
+
193
+ #if UNITY_EDITOR
194
+ if (Application.isPlaying)
195
+ {
196
+ AsyncOperation asyncOperation = SceneManager.UnloadSceneAsync(
197
+ openedScene,
198
+ UnloadSceneOptions.None
199
+ );
200
+ await asyncOperation.AsTask();
201
+ }
202
+ else
203
+ {
204
+ EditorSceneManager.CloseScene(openedScene, true);
205
+ }
206
+ #else
207
+ AsyncOperation asyncOperation = SceneManager.UnloadSceneAsync(
208
+ openedScene,
209
+ UnloadSceneOptions.None
210
+ );
211
+ await asyncOperation.AsTask();
212
+ #endif
213
+ }
214
+ }
215
+ }
216
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 366d25a4bde54caa9dbb17d039c63e6f
3
+ timeCreated: 1743955669
@@ -0,0 +1,32 @@
1
+ namespace UnityHelpers.Core.Helper
2
+ {
3
+ using System;
4
+ using System.Collections.Concurrent;
5
+ using UnityEngine;
6
+ using Utils;
7
+
8
+ public sealed class UnityMainThreadDispatcher : RuntimeSingleton<UnityMainThreadDispatcher>
9
+ {
10
+ private readonly ConcurrentQueue<Action> _actions = new();
11
+
12
+ public void RunOnMainThread(Action action)
13
+ {
14
+ _actions.Enqueue(action);
15
+ }
16
+
17
+ private void Update()
18
+ {
19
+ while (_actions.TryDequeue(out Action action))
20
+ {
21
+ try
22
+ {
23
+ action();
24
+ }
25
+ catch (Exception e)
26
+ {
27
+ Debug.LogException(e, this);
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 6d44672eb7364a0995fa1055d4187970
3
+ timeCreated: 1743968724
@@ -67,7 +67,13 @@
67
67
  throw new ArgumentException("Max cannot be zero");
68
68
  }
69
69
 
70
- return (uint)(NextDouble() * max);
70
+ uint result = (uint)(NextDouble() * max);
71
+ if (result == max)
72
+ {
73
+ return result - 1;
74
+ }
75
+
76
+ return result;
71
77
  }
72
78
 
73
79
  public uint NextUint(uint min, uint max)
@@ -129,7 +135,13 @@
129
135
  throw new ArgumentException($"Max {max} cannot be less-than or equal-to 0");
130
136
  }
131
137
 
132
- return (long)(NextDouble() * max);
138
+ long result = (long)(NextDouble() * max);
139
+ if (result == max)
140
+ {
141
+ return result - 1;
142
+ }
143
+
144
+ return result;
133
145
  }
134
146
 
135
147
  public long NextLong(long min, long max)
@@ -147,7 +159,13 @@
147
159
  return unchecked((long)NextUlong());
148
160
  }
149
161
 
150
- return unchecked((long)(NextDouble() * range + min));
162
+ long result = unchecked((long)(NextDouble() * range + min));
163
+ if (result == max)
164
+ {
165
+ return result - 1;
166
+ }
167
+
168
+ return result;
151
169
  }
152
170
 
153
171
  public ulong NextUlong()
@@ -159,7 +177,12 @@
159
177
 
160
178
  public ulong NextUlong(ulong max)
161
179
  {
162
- return (ulong)(NextDouble() * max);
180
+ ulong result = (ulong)(NextDouble() * max);
181
+ if (result == max)
182
+ {
183
+ return result - 1;
184
+ }
185
+ return result;
163
186
  }
164
187
 
165
188
  public ulong NextUlong(ulong min, ulong max)
@@ -0,0 +1,88 @@
1
+ namespace UnityHelpers.Core.Serialization.JsonConverters
2
+ {
3
+ using System;
4
+ using System.Text.Json;
5
+ using System.Text.Json.Serialization;
6
+ using UnityEngine;
7
+
8
+ public sealed class ColorConverter : JsonConverter<Color>
9
+ {
10
+ public static readonly ColorConverter Instance = new();
11
+
12
+ private ColorConverter() { }
13
+
14
+ public override Color Read(
15
+ ref Utf8JsonReader reader,
16
+ Type typeToConvert,
17
+ JsonSerializerOptions options
18
+ )
19
+ {
20
+ if (reader.TokenType != JsonTokenType.StartObject)
21
+ {
22
+ throw new JsonException($"Invalid token type {reader.TokenType}");
23
+ }
24
+
25
+ float r = 0;
26
+ float g = 0;
27
+ float b = 0;
28
+ float a = 1;
29
+
30
+ while (reader.Read())
31
+ {
32
+ if (reader.TokenType == JsonTokenType.EndObject)
33
+ {
34
+ return new Color(r, g, b, a);
35
+ }
36
+
37
+ if (reader.TokenType == JsonTokenType.PropertyName)
38
+ {
39
+ string propertyName = reader.GetString();
40
+ reader.Read();
41
+ switch (propertyName)
42
+ {
43
+ case "r":
44
+ {
45
+ r = reader.GetSingle();
46
+ break;
47
+ }
48
+ case "g":
49
+ {
50
+ g = reader.GetSingle();
51
+ break;
52
+ }
53
+ case "b":
54
+ {
55
+ b = reader.GetSingle();
56
+ break;
57
+ }
58
+ case "a":
59
+ {
60
+ a = reader.GetSingle();
61
+ break;
62
+ }
63
+ default:
64
+ {
65
+ throw new JsonException($"Unknown property: {propertyName}");
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ throw new JsonException("Incomplete JSON for Color");
72
+ }
73
+
74
+ public override void Write(
75
+ Utf8JsonWriter writer,
76
+ Color value,
77
+ JsonSerializerOptions options
78
+ )
79
+ {
80
+ writer.WriteStartObject();
81
+ writer.WriteNumber("r", value.r);
82
+ writer.WriteNumber("g", value.g);
83
+ writer.WriteNumber("b", value.b);
84
+ writer.WriteNumber("a", value.a);
85
+ writer.WriteEndObject();
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 637013e97e6d436a880092e239d12e4e
3
+ timeCreated: 1743567974
@@ -0,0 +1,37 @@
1
+ namespace UnityHelpers.Core.Serialization.JsonConverters
2
+ {
3
+ using System;
4
+ using System.Text.Json;
5
+ using System.Text.Json.Serialization;
6
+ using UnityEngine;
7
+ using Object = UnityEngine.Object;
8
+
9
+ public sealed class GameObjectConverter : JsonConverter<GameObject>
10
+ {
11
+ public static readonly GameObjectConverter Instance = new();
12
+
13
+ private GameObjectConverter() { }
14
+
15
+ public override GameObject Read(
16
+ ref Utf8JsonReader reader,
17
+ Type typeToConvert,
18
+ JsonSerializerOptions options
19
+ )
20
+ {
21
+ throw new NotImplementedException(nameof(Read));
22
+ }
23
+
24
+ public override void Write(
25
+ Utf8JsonWriter writer,
26
+ GameObject value,
27
+ JsonSerializerOptions options
28
+ )
29
+ {
30
+ writer.WriteStringValue(
31
+ value == null
32
+ ? "{}"
33
+ : new { name = value.name, type = value.GetType().FullName }.ToString()
34
+ );
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 6c12adc5c56c4be9bb536628749979b0
3
+ timeCreated: 1743567258
@@ -0,0 +1,218 @@
1
+ namespace UnityHelpers.Core.Serialization.JsonConverters
2
+ {
3
+ using System;
4
+ using System.Text.Json;
5
+ using System.Text.Json.Serialization;
6
+ using UnityEngine;
7
+
8
+ public sealed class Matrix4x4Converter : JsonConverter<Matrix4x4>
9
+ {
10
+ private static readonly string[] PropertyNames =
11
+ {
12
+ "m00",
13
+ "m01",
14
+ "m02",
15
+ "m03",
16
+ "m10",
17
+ "m11",
18
+ "m12",
19
+ "m13",
20
+ "m20",
21
+ "m21",
22
+ "m22",
23
+ "m23",
24
+ "m30",
25
+ "m31",
26
+ "m32",
27
+ "m33",
28
+ };
29
+
30
+ public static readonly Matrix4x4Converter Instance = new();
31
+
32
+ private Matrix4x4Converter() { }
33
+
34
+ public override Matrix4x4 Read(
35
+ ref Utf8JsonReader reader,
36
+ Type typeToConvert,
37
+ JsonSerializerOptions options
38
+ )
39
+ {
40
+ if (reader.TokenType != JsonTokenType.StartObject)
41
+ {
42
+ throw new JsonException("Expected StartObject token when parsing Matrix4x4.");
43
+ }
44
+
45
+ float m00 = 0;
46
+ float m01 = 0;
47
+ float m02 = 0;
48
+ float m03 = 0;
49
+ float m10 = 0;
50
+ float m11 = 0;
51
+ float m12 = 0;
52
+ float m13 = 0;
53
+ float m20 = 0;
54
+ float m21 = 0;
55
+ float m22 = 0;
56
+ float m23 = 0;
57
+ float m30 = 0;
58
+ float m31 = 0;
59
+ float m32 = 0;
60
+ float m33 = 0;
61
+
62
+ bool[] found = new bool[16];
63
+
64
+ while (reader.Read())
65
+ {
66
+ if (reader.TokenType == JsonTokenType.EndObject)
67
+ {
68
+ break;
69
+ }
70
+ if (reader.TokenType != JsonTokenType.PropertyName)
71
+ {
72
+ throw new JsonException("Expected property name in Matrix4x4 JSON.");
73
+ }
74
+ string propertyName = reader.GetString();
75
+ if (!reader.Read())
76
+ {
77
+ throw new JsonException($"Expected value for property '{propertyName}'.");
78
+ }
79
+ float value;
80
+ try
81
+ {
82
+ value = reader.GetSingle();
83
+ }
84
+ catch (Exception ex)
85
+ {
86
+ throw new JsonException($"Invalid value for property '{propertyName}'.", ex);
87
+ }
88
+ switch (propertyName)
89
+ {
90
+ case "m00":
91
+ m00 = value;
92
+ found[0] = true;
93
+ break;
94
+ case "m01":
95
+ m01 = value;
96
+ found[1] = true;
97
+ break;
98
+ case "m02":
99
+ m02 = value;
100
+ found[2] = true;
101
+ break;
102
+ case "m03":
103
+ m03 = value;
104
+ found[3] = true;
105
+ break;
106
+ case "m10":
107
+ m10 = value;
108
+ found[4] = true;
109
+ break;
110
+ case "m11":
111
+ m11 = value;
112
+ found[5] = true;
113
+ break;
114
+ case "m12":
115
+ m12 = value;
116
+ found[6] = true;
117
+ break;
118
+ case "m13":
119
+ m13 = value;
120
+ found[7] = true;
121
+ break;
122
+ case "m20":
123
+ m20 = value;
124
+ found[8] = true;
125
+ break;
126
+ case "m21":
127
+ m21 = value;
128
+ found[9] = true;
129
+ break;
130
+ case "m22":
131
+ m22 = value;
132
+ found[10] = true;
133
+ break;
134
+ case "m23":
135
+ m23 = value;
136
+ found[11] = true;
137
+ break;
138
+ case "m30":
139
+ m30 = value;
140
+ found[12] = true;
141
+ break;
142
+ case "m31":
143
+ m31 = value;
144
+ found[13] = true;
145
+ break;
146
+ case "m32":
147
+ m32 = value;
148
+ found[14] = true;
149
+ break;
150
+ case "m33":
151
+ m33 = value;
152
+ found[15] = true;
153
+ break;
154
+ }
155
+ }
156
+
157
+ for (int i = 0; i < found.Length; i++)
158
+ {
159
+ if (!found[i])
160
+ {
161
+ throw new JsonException(
162
+ $"Missing property '{PropertyNames[i]}' for Matrix4x4."
163
+ );
164
+ }
165
+ }
166
+
167
+ Matrix4x4 matrix = new()
168
+ {
169
+ m00 = m00,
170
+ m01 = m01,
171
+ m02 = m02,
172
+ m03 = m03,
173
+ m10 = m10,
174
+ m11 = m11,
175
+ m12 = m12,
176
+ m13 = m13,
177
+ m20 = m20,
178
+ m21 = m21,
179
+ m22 = m22,
180
+ m23 = m23,
181
+ m30 = m30,
182
+ m31 = m31,
183
+ m32 = m32,
184
+ m33 = m33,
185
+ };
186
+
187
+ return matrix;
188
+ }
189
+
190
+ public override void Write(
191
+ Utf8JsonWriter writer,
192
+ Matrix4x4 matrix,
193
+ JsonSerializerOptions options
194
+ )
195
+ {
196
+ writer.WriteStartObject();
197
+
198
+ writer.WriteNumber("m00", matrix.m00);
199
+ writer.WriteNumber("m01", matrix.m01);
200
+ writer.WriteNumber("m02", matrix.m02);
201
+ writer.WriteNumber("m03", matrix.m03);
202
+ writer.WriteNumber("m10", matrix.m10);
203
+ writer.WriteNumber("m11", matrix.m11);
204
+ writer.WriteNumber("m12", matrix.m12);
205
+ writer.WriteNumber("m13", matrix.m13);
206
+ writer.WriteNumber("m20", matrix.m20);
207
+ writer.WriteNumber("m21", matrix.m21);
208
+ writer.WriteNumber("m22", matrix.m22);
209
+ writer.WriteNumber("m23", matrix.m23);
210
+ writer.WriteNumber("m30", matrix.m30);
211
+ writer.WriteNumber("m31", matrix.m31);
212
+ writer.WriteNumber("m32", matrix.m32);
213
+ writer.WriteNumber("m33", matrix.m33);
214
+
215
+ writer.WriteEndObject();
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 456d0090c1d744de924e8f18a895b87b
3
+ timeCreated: 1743568330