com.wallstop-studios.unity-helpers 2.0.0-rc50 → 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.
- package/Runtime/Core/Extension/UnityExtensions.cs +8 -0
- package/Runtime/Core/Helper/SceneHelper.cs +216 -0
- package/Runtime/Core/Helper/SceneHelper.cs.meta +3 -0
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +32 -0
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs.meta +3 -0
- package/Runtime/Core/Random/AbstractRandom.cs +27 -4
- package/Runtime/Utils/DeferredDisposalResult.cs +19 -0
- package/Runtime/Utils/DeferredDisposalResult.cs.meta +3 -0
- package/Tests/Runtime/Helper/SceneHelperTests.cs +92 -0
- package/Tests/Runtime/Helper/SceneHelperTests.cs.meta +3 -0
- package/Tests/Runtime/Scenes/Test1.unity +723 -0
- package/Tests/Runtime/Scenes/Test1.unity.meta +7 -0
- package/Tests/Runtime/Scenes/Test2.unity +723 -0
- package/Tests/Runtime/Scenes/Test2.unity.meta +7 -0
- package/Tests/Runtime/Scenes.meta +3 -0
- 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,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
|
+
}
|
|
@@ -67,7 +67,13 @@
|
|
|
67
67
|
throw new ArgumentException("Max cannot be zero");
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,19 @@
|
|
|
1
|
+
namespace UnityHelpers.Utils
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Threading.Tasks;
|
|
5
|
+
|
|
6
|
+
public sealed class DeferredDisposalResult<T>
|
|
7
|
+
{
|
|
8
|
+
public T Result { get; }
|
|
9
|
+
private readonly Func<Task> _disposeAsync;
|
|
10
|
+
|
|
11
|
+
public DeferredDisposalResult(T result, Func<Task> disposeAsync)
|
|
12
|
+
{
|
|
13
|
+
Result = result;
|
|
14
|
+
_disposeAsync = disposeAsync;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public Task DisposeAsync() => _disposeAsync();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
namespace UnityHelpers.Tests.Helper
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Collections;
|
|
5
|
+
using System.Collections.Generic;
|
|
6
|
+
using System.Linq;
|
|
7
|
+
using System.Threading.Tasks;
|
|
8
|
+
using Core.Helper;
|
|
9
|
+
using NUnit.Framework;
|
|
10
|
+
using UnityEngine;
|
|
11
|
+
using UnityEngine.TestTools;
|
|
12
|
+
using UnityHelpers.Utils;
|
|
13
|
+
|
|
14
|
+
public sealed class SceneHelperTests
|
|
15
|
+
{
|
|
16
|
+
private readonly List<Func<Task>> _disposalTasks = new();
|
|
17
|
+
|
|
18
|
+
[UnityTearDown]
|
|
19
|
+
public IEnumerator TearDown()
|
|
20
|
+
{
|
|
21
|
+
foreach (Task disposal in _disposalTasks.Select(disposalProducer => disposalProducer()))
|
|
22
|
+
{
|
|
23
|
+
while (!disposal.IsCompleted)
|
|
24
|
+
{
|
|
25
|
+
yield return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_disposalTasks.Clear();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
[Test]
|
|
33
|
+
public void GetScenesInBuild()
|
|
34
|
+
{
|
|
35
|
+
// This will only pass if you have scenes in your build path
|
|
36
|
+
string[] scenes = SceneHelper.GetScenesInBuild();
|
|
37
|
+
Assert.That(scenes, Is.Not.Empty);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
[Test]
|
|
41
|
+
public void GetAllScenePaths()
|
|
42
|
+
{
|
|
43
|
+
string[] scenePaths = SceneHelper.GetAllScenePaths();
|
|
44
|
+
Assert.That(scenePaths, Is.Not.Empty);
|
|
45
|
+
Assert.IsTrue(
|
|
46
|
+
scenePaths.Any(path => path.Contains("Test1")),
|
|
47
|
+
string.Join(",", scenePaths)
|
|
48
|
+
);
|
|
49
|
+
Assert.IsTrue(
|
|
50
|
+
scenePaths.Any(path => path.Contains("Test2")),
|
|
51
|
+
string.Join(",", scenePaths)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[UnityTest]
|
|
56
|
+
public IEnumerator GetObjectOfTypeInScene()
|
|
57
|
+
{
|
|
58
|
+
Task<DeferredDisposalResult<SpriteRenderer>> task =
|
|
59
|
+
SceneHelper.GetObjectOfTypeInScene<SpriteRenderer>(
|
|
60
|
+
@"Packages\com.wallstop-studios.unity-helpers\Tests\Runtime\Scenes\Test1.unity"
|
|
61
|
+
);
|
|
62
|
+
while (!task.IsCompleted)
|
|
63
|
+
{
|
|
64
|
+
yield return null;
|
|
65
|
+
}
|
|
66
|
+
Assert.IsTrue(task.IsCompletedSuccessfully, task.Exception?.ToString() ?? string.Empty);
|
|
67
|
+
|
|
68
|
+
_disposalTasks.Add(task.Result.DisposeAsync);
|
|
69
|
+
SpriteRenderer found = task.Result.Result;
|
|
70
|
+
Assert.IsTrue(found != null);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
[UnityTest]
|
|
74
|
+
public IEnumerator GetAllObjectOfTypeInScene()
|
|
75
|
+
{
|
|
76
|
+
Task<DeferredDisposalResult<SpriteRenderer[]>> task =
|
|
77
|
+
SceneHelper.GetAllObjectsOfTypeInScene<SpriteRenderer>(
|
|
78
|
+
@"Packages\com.wallstop-studios.unity-helpers\Tests\Runtime\Scenes\Test1.unity"
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
while (!task.IsCompleted)
|
|
82
|
+
{
|
|
83
|
+
yield return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Assert.IsTrue(task.IsCompletedSuccessfully, task.Exception?.ToString() ?? string.Empty);
|
|
87
|
+
_disposalTasks.Add(task.Result.DisposeAsync);
|
|
88
|
+
SpriteRenderer[] found = task.Result.Result;
|
|
89
|
+
Assert.That(found, Has.Length.EqualTo(7));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|