Files
A-Fairytale-Gone-Bad-Briar-…/Assets/Scripts/Framework/Assets/AddressableManager.cs

204 lines
6.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading;
using BriarQueen.Framework.Assets.Components;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
using VContainer;
using VContainer.Unity;
namespace BriarQueen.Framework.Assets
{
public class AddressableManager : IDisposable
{
private readonly Dictionary<object, AsyncOperationHandle> _assetHandles = new();
private readonly Dictionary<GameObject, AsyncOperationHandle> _instanceHandles = new();
private readonly IObjectResolver _lifetimeContainer;
private readonly object _lock = new();
[Inject]
public AddressableManager(IObjectResolver lifetimeContainer)
{
_lifetimeContainer = lifetimeContainer;
}
public void Dispose()
{
lock (_lock)
{
foreach (var handle in _instanceHandles.Values)
if (handle.IsValid())
Addressables.ReleaseInstance(handle);
_instanceHandles.Clear();
foreach (var handle in _assetHandles.Values)
if (handle.IsValid())
Addressables.Release(handle);
_assetHandles.Clear();
}
}
public async UniTask<T> LoadAssetAsync<T>(AssetReference reference) where T : class
{
var handle = Addressables.LoadAssetAsync<T>(reference);
try
{
await handle;
if (handle.Status == AsyncOperationStatus.Succeeded)
{
lock (_lock)
{
_assetHandles[handle.Result] = handle;
}
return handle.Result;
}
Debug.LogError($"[AddressableManager] Failed to load asset: {reference.RuntimeKey}");
return null;
}
catch (OperationCanceledException)
{
if (handle.IsValid()) Addressables.Release(handle);
throw;
}
}
public void ReleaseAsset<T>(T asset) where T : class
{
if (asset == null) return;
lock (_lock)
{
if (_assetHandles.TryGetValue(asset, out var handle))
{
if (handle.IsValid()) Addressables.Release(handle);
_assetHandles.Remove(asset);
}
}
}
public async UniTask<AsyncOperationHandle<SceneInstance>> LoadSceneAsync(
AssetReference assetReference,
LoadSceneMode loadSceneMode = LoadSceneMode.Additive,
CancellationToken cancellationToken = default,
IProgress<float> progress = null,
bool autoLoad = true
)
{
var handle = Addressables.LoadSceneAsync(assetReference, loadSceneMode, autoLoad);
await handle.ToUniTask(progress, cancellationToken: cancellationToken);
return handle;
}
public async UniTask UnloadSceneAsync(AsyncOperationHandle<SceneInstance> sceneHandle)
{
if (sceneHandle.IsValid()) await Addressables.UnloadSceneAsync(sceneHandle);
}
public async UniTask<GameObject> InstantiateAsync(
AssetReference reference,
Vector3 position = default,
Quaternion rotation = default,
Transform parent = null,
IObjectResolver scope = null,
CancellationToken cancellationToken = default
)
{
var handle = Addressables.InstantiateAsync(reference, position, rotation, parent);
try
{
await handle.WithCancellation(cancellationToken);
if (handle.Status != AsyncOperationStatus.Succeeded)
{
Debug.LogError($"[AddressableManager] Failed to instantiate asset: {reference.RuntimeKey}");
return null;
}
var go = handle.Result;
lock (_lock)
{
_instanceHandles[go] = handle;
}
var prefabScope = go.GetComponent<LifetimeScope>();
var injectionScope = scope ?? prefabScope?.Container ?? _lifetimeContainer;
injectionScope.InjectGameObject(go);
var notify = go.GetComponent<NotifyOnDestruction>();
if (!notify)
{
notify = go.AddComponent<NotifyOnDestruction>();
injectionScope.Inject(notify);
}
void OnInstanceDestroyed()
{
TryReleaseInstance(go);
notify.OnDestroyedCalled -= OnInstanceDestroyed;
}
notify.OnDestroyedCalled += OnInstanceDestroyed;
return go;
}
catch (OperationCanceledException)
{
if (handle.IsValid()) Addressables.ReleaseInstance(handle);
throw;
}
}
public bool TryReleaseInstance(GameObject instance)
{
if (instance == null)
return false;
Debug.Log($"[AddressableManager] Trying to release instance: {instance}");
lock (_lock)
{
if (_instanceHandles.TryGetValue(instance, out var handle))
{
if (handle.IsValid()) Addressables.ReleaseInstance(handle);
_instanceHandles.Remove(instance);
return true;
}
}
return false;
}
public void ReleaseInstance(GameObject instance)
{
if (instance == null) return;
lock (_lock)
{
if (_instanceHandles.TryGetValue(instance, out var handle))
{
if (handle.IsValid()) Addressables.ReleaseInstance(handle);
_instanceHandles.Remove(instance);
}
}
}
}
}