Skip to content

Commit 0dbb0c8

Browse files
committed
Added lifetime extensions and other improvements
1 parent 5bffd46 commit 0dbb0c8

File tree

8 files changed

+190
-21
lines changed

8 files changed

+190
-21
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## v0.1.1
4+
5+
### Added
6+
7+
- LifetimeManagement extensions for base Addressables
8+
- invoking action when operation with addressables is complete
9+
- support for adding auto release trigger by asset string key
10+
311
## v0.1.0
412

513
### Added

README.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ If you find this project useful, star it, I will be grateful!
2121
- [Async](#async)
2222
- [Examples](#examples-1)
2323
- [Lifetime managment](#lifetime-managment)
24-
- [Examples](#examples-2)
24+
- [Addressables Extensions](#addressables-extensions)
25+
- [Examples](#examples-2)
26+
- [Examples](#examples-3)
2527
- [Credits](#credits)
2628

2729
## Roadmap
@@ -123,18 +125,30 @@ ManageAddressables.InstantiateAsync(figureAssetRefGO, onCompletion: (go =>
123125

124126
When you load an addressable asset, you should release it as soon as you don't need it anymore, forgetting to do this can lead to many bad processes at runtime. Using the `Addressables Master` you can bind a release to the `GameoObject` that will do it for you automatically as soon as it is destroyed.
125127

126-
Below are methods for working with assets with lifetime management:
128+
#### Addressables Extensions
127129

128-
`Base methods`
130+
If you need to use standard operations for working with addressables assets, then you can use the extensions.
129131

130-
> Important! Use base methods below only for assets that instanced via `Addressables Master`.
132+
```c#
133+
public static async Task<AsyncOperationHandle<T>> AddAutoRelease<T>(this AsyncOperationHandle<T> operationHandle, GameObject targetGO)
134+
```
135+
136+
```c#
137+
public static async Task<AsyncOperationHandle<T>> AddAutoRelease<T>(this AsyncOperationHandle<T> operationHandle, GameObject targetGO, Action onCompletion)
138+
```
131139

132140
```c#
133-
public static void AddAutoReleaseAssetTrigger(AssetReference assetReference, GameObject targetGO)
141+
public static async Task<AsyncOperationHandle<GameObject>> AddReleaseOnDestroy(this AsyncOperationHandle<GameObject> operationHandle)
134142
```
135143

144+
##### Examples
145+
136146
```c#
137-
public static void AddAutoReleaseInstanceTrigger(AssetReference assetReference, GameObject targetGO)
147+
GameObject go = new GameObject("Temp");
148+
149+
assetReferenceMaterial.LoadAssetAsync().AddAutoRelease(go);
150+
151+
figureAssetRefGO.InstantiateAsync().AddReleaseOnDestroy();
138152
```
139153

140154
`Sync`

Runtime/AddresssablesExtensions.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using AddressablesMaster;
4+
using UnityEngine.ResourceManagement.AsyncOperations;
5+
6+
namespace UnityEngine.AddressableAssets
7+
{
8+
public static class LifetimeManagement
9+
{
10+
11+
/// <summary>
12+
/// Binds the release of the <see cref="UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle{T}"/>
13+
/// to the OnDestroy of the target GameObject.
14+
/// </summary>
15+
public static async Task<AsyncOperationHandle<T>> AddAutoRelease<T>(this AsyncOperationHandle<T> operationHandle, GameObject targetGO)
16+
{
17+
while (!operationHandle.IsDone) await Task.Yield();
18+
19+
if (operationHandle.Status == AsyncOperationStatus.Failed) return default;
20+
21+
AddAutoReleaseTrigger(operationHandle, targetGO);
22+
23+
return operationHandle;
24+
}
25+
26+
/// <summary>
27+
/// Binds the release of the <see cref="UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle{T}"/>
28+
/// to the OnDestroy of the target GameObject and invokes Action when destroyed.
29+
/// </summary>
30+
public static async Task<AsyncOperationHandle<T>> AddAutoRelease<T>(this AsyncOperationHandle<T> operationHandle, GameObject targetGO, Action onCompletion)
31+
{
32+
while (!operationHandle.IsDone) await Task.Yield();
33+
34+
if (operationHandle.Status == AsyncOperationStatus.Failed) return default;
35+
36+
var operation = AddAutoReleaseTrigger(operationHandle, targetGO);
37+
38+
operation.OnDestroyEvent += onCompletion;
39+
40+
return operationHandle;
41+
}
42+
43+
/// <summary>
44+
/// Binds the release of the instantiated GameObject to its OnDestroy.
45+
/// </summary>
46+
public static async Task<AsyncOperationHandle<GameObject>> AddReleaseOnDestroy(this AsyncOperationHandle<GameObject> operationHandle)
47+
{
48+
while (!operationHandle.IsDone) await Task.Yield();
49+
50+
if (operationHandle.Status == AsyncOperationStatus.Failed) return default;
51+
52+
AddAutoReleaseTrigger(operationHandle, operationHandle.Result);
53+
54+
return operationHandle;
55+
}
56+
57+
private static ReleaseHandleOnDestroy AddAutoReleaseTrigger(AsyncOperationHandle operationHandle,
58+
GameObject targetGO)
59+
{
60+
_ = targetGO ?? throw new ArgumentNullException(nameof(targetGO));
61+
62+
var trigger = targetGO.AddComponent<ReleaseHandleOnDestroy>();
63+
64+
trigger.OnDestroyEvent += () => Addressables.Release(operationHandle);
65+
66+
return trigger;
67+
}
68+
}
69+
}

Runtime/AddresssablesExtensions/LifetimeManagement.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/ManageAddressables.Async.cs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace AddressablesMaster
1515
{
1616
public static partial class ManageAddressables
1717
{
18-
public static async Task<OperationResult<IResourceLocator>> InitializeAsync()
18+
public static async Task<OperationResult<IResourceLocator>> InitializeAsync(Action<OperationResult<IResourceLocator>> onCompletion = null)
1919
{
2020
Clear();
2121

@@ -25,15 +25,20 @@ public static async Task<OperationResult<IResourceLocator>> InitializeAsync()
2525
await operation.Task;
2626

2727
OnInitializeCompleted(operation);
28-
return operation;
28+
29+
OperationResult<IResourceLocator> operationResult = operation;
30+
31+
onCompletion?.Invoke(operationResult);
32+
33+
return operationResult;
2934
}
3035
catch (Exception e)
3136
{
3237
throw e;
3338
}
3439
}
3540

36-
public static async Task<OperationResult<object>> LoadLocationsAsync(object key)
41+
public static async Task<OperationResult<object>> LoadLocationsAsync(object key, Action<OperationResult<object>> onCompletion = null)
3742
{
3843
_ = key ?? throw new InvalidKeyException(key);
3944

@@ -43,15 +48,21 @@ public static async Task<OperationResult<object>> LoadLocationsAsync(object key)
4348
await operation.Task;
4449

4550
OnLoadLocationsCompleted(operation, key);
46-
return new OperationResult<object>(operation.Status == AsyncOperationStatus.Succeeded, key);
51+
52+
var operationResult = new OperationResult<object>(operation.Status == AsyncOperationStatus.Succeeded, key);
53+
54+
onCompletion?.Invoke(operationResult);
55+
56+
return operationResult;
4757
}
4858
catch (Exception e)
4959
{
5060
throw e;
5161
}
5262
}
5363

54-
public static async Task<OperationResult<T>> LoadAssetAsync<T>(string key) where T : Object
64+
public static async Task<OperationResult<T>> LoadAssetAsync<T>(string key, Action<T> onCompletion = null)
65+
where T : Object
5566
{
5667
RuntimeKeyIsValid(key, true);
5768

@@ -72,6 +83,7 @@ public static async Task<OperationResult<T>> LoadAssetAsync<T>(string key) where
7283
await operation.Task;
7384

7485
OnLoadAssetCompleted(operation, key, false);
86+
onCompletion?.Invoke(operation.Result);
7587
return operation;
7688
}
7789
catch (Exception e)
@@ -80,7 +92,8 @@ public static async Task<OperationResult<T>> LoadAssetAsync<T>(string key) where
8092
}
8193
}
8294

83-
public static async Task<OperationResult<T>> LoadAssetAsync<T>(AssetReferenceT<T> reference, Action<T> onCompletion = null) where T : Object
95+
public static async Task<OperationResult<T>> LoadAssetAsync<T>(AssetReferenceT<T> reference,
96+
Action<T> onCompletion = null) where T : Object
8497
{
8598
RuntimeKeyIsValid(reference, out var key, true);
8699

@@ -230,7 +243,7 @@ public static async Task<OperationResult<SceneInstance>> UnloadSceneAsync(AssetR
230243
}
231244

232245
public static async Task<OperationResult<GameObject>> InstantiateAsync(string key, Transform parent = null,
233-
bool inWorldSpace = false, bool trackHandle = true)
246+
bool inWorldSpace = false, bool trackHandle = true, Action<GameObject> onCompletion = null)
234247
{
235248
RuntimeKeyIsValid(key, true);
236249

@@ -240,6 +253,7 @@ public static async Task<OperationResult<GameObject>> InstantiateAsync(string ke
240253
await operation.Task;
241254

242255
OnInstantiateCompleted(operation, key, false);
256+
onCompletion?.Invoke(operation.Result);
243257
return operation;
244258
}
245259
catch (Exception e)
@@ -268,20 +282,42 @@ public static async Task<OperationResult<GameObject>> InstantiateAsync(AssetRefe
268282
}
269283
}
270284

285+
/// <summary>
286+
/// Instantiates game object on the scene asynchronously and adds a trigger to the instance that
287+
/// releases <see cref="AsyncOperationHandle"/> when the instance is destroyed.
288+
/// </summary>
289+
/// <returns>Instantiated GameObject on the scene.</returns>
290+
public static async Task<GameObject> InstantiateAsyncWithAutoRelease(string key, Transform parent = null,
291+
bool inWorldSpace = false, Action<GameObject> onCompletion = null)
292+
{
293+
var operationResult = LoadAssetAsync<GameObject>(key);
294+
await operationResult;
295+
296+
var instantiatedGO = Object.Instantiate(operationResult.Result.Value, parent, inWorldSpace);
297+
AddAutoReleaseAssetTrigger(key, instantiatedGO);
298+
299+
onCompletion?.Invoke(instantiatedGO);
300+
301+
return instantiatedGO;
302+
}
303+
271304
/// <summary>
272305
/// Instantiates game object on the scene asynchronously and adds a trigger to the instance that
273306
/// releases <see cref="AsyncOperationHandle"/> when the instance is destroyed.
274307
/// </summary>
275308
/// <returns>Instantiated game object on the scene.</returns>
276309
public static async Task<GameObject> InstantiateAsyncWithAutoRelease(
277-
AssetReference assetReference, Transform parent = null, bool inWorldSpace = false)
310+
AssetReference assetReference, Transform parent = null, bool inWorldSpace = false,
311+
Action<GameObject> onCompletion = null)
278312
{
279313
var operationResult = LoadAssetAsync((AssetReferenceT<GameObject>)assetReference);
280314
await operationResult;
281315

282316
var instantiatedGO = Object.Instantiate(operationResult.Result.Value, parent, inWorldSpace);
283317
AddAutoReleaseAssetTrigger(assetReference, instantiatedGO);
284318

319+
onCompletion?.Invoke(instantiatedGO);
320+
285321
return instantiatedGO;
286322
}
287323

Runtime/ManageAddressables.BaseMethods.cs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,9 @@ public static void LoadAsset<T>(string key) where T : Object
4444

4545
if (_assets.ContainsKey(key))
4646
{
47-
if (!(_assets[key] is T))
48-
{
49-
if (!SuppressWarningLogs)
50-
Debug.LogWarning(Exceptions.AssetKeyNotInstanceOf<T>(key));
51-
}
47+
if (_assets[key] is T) return;
48+
if (!SuppressWarningLogs)
49+
Debug.LogWarning(Exceptions.AssetKeyNotInstanceOf<T>(key));
5250

5351
return;
5452
}
@@ -231,7 +229,19 @@ public static void Instantiate(AssetReference reference,
231229
throw e;
232230
}
233231
}
234-
232+
233+
/// <summary>
234+
/// USE ONLY IF TARGET LOADED VIA <see cref="ManageAddressables"/>.
235+
/// Adds a trigger to the object and releases the target addressable asset when the game object is destroyed.
236+
/// </summary>
237+
/// <param name="key">Asset provided by a key that will be released upon game object destryed.</param>
238+
/// <param name="targetGO">The object to which the trigger will be attached.</param>
239+
public static void AddAutoReleaseAssetTrigger(string key, GameObject targetGO)
240+
{
241+
targetGO.AddComponent<ReleaseHandleOnDestroy>().OnDestroyEvent +=
242+
() => ReleaseAsset(key);
243+
}
244+
235245
/// <summary>
236246
/// USE ONLY IF TARGET LOADED VIA <see cref="ManageAddressables"/>.
237247
/// Adds a trigger to the object and releases the target addressable asset when the game object is destroyed.
@@ -244,6 +254,19 @@ public static void AddAutoReleaseAssetTrigger(AssetReference assetReference, Gam
244254
() => ReleaseAsset(assetReference);
245255
}
246256

257+
/// <summary>
258+
/// USE ONLY IF TARGET INSTANTIATED VIA <see cref="ManageAddressables"/>.
259+
/// Adds a trigger to the object that is invoked when the object is destroyed
260+
/// and releases the target asset instance.
261+
/// </summary>
262+
/// <param name="key">Asset provided by a key that will be released upon game object destryed.</param>
263+
/// <param name="targetGO">The object to which the trigger will be attached.</param>
264+
public static void AddAutoReleaseInstanceTrigger(string key, GameObject targetGO)
265+
{
266+
targetGO.AddComponent<ReleaseHandleOnDestroy>().OnDestroyEvent +=
267+
() => ReleaseInstance(key, targetGO);
268+
}
269+
247270
/// <summary>
248271
/// USE ONLY IF TARGET INSTANTIATED VIA <see cref="ManageAddressables"/>.
249272
/// Adds a trigger to the object that is invoked when the object is destroyed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "com.inc8877.addressables-master",
3-
"version": "0.1.0",
3+
"version": "0.1.1",
44
"displayName": "AddressablesMaster",
55
"description": "Essential Addressables tool",
66
"homepage": "https://github.com/inc8877/AddressablesMaster#readme",

0 commit comments

Comments
 (0)