204 lines
6.2 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |