UnityのAddressable Asset Systemは、アプリケーションのアセットを効率的に管理し、ダイナミックなロードやアンロードを可能にする強力なツールです。このシステムを使用することで、ゲームやアプリの開発者は、リソースの管理を容易にし、ユーザー体験を向上させることができます。
しかし、Addressable Asset Systemのカタログに登録されていないアセットをロードしようとすると、通常はエラーが発生します。この問題を解決するために、UnityのEditor上で動作する便利な処理を開発しました。
こちらの処理は Unity Editor上でのみ動作します。そのため、開発中にアセットをダイナミックにロードしたい場合に有用です。
この機能は、指定されたアドレスのアセットを非同期でロードすることです。しかし、そのアドレスがAddressableのカタログに登録されていない場合、自動的にカタログに登録する処理が行われます。これにより、アプリケーションの実行時にエラーが発生することなく、必要なアセットを動的にロードすることが可能になります。
ただし、TryGetPathToAddressの処理は Addressable パスの付け方によって異なると思うので、環境に合った変換処理が必要になります
using System.IO; using System.Linq; using System.Threading.Tasks; using UnityEditor; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.ResourceManagement.ResourceProviders; public static class AddressableUtility { private static readonly string LOCATION_ID = "Temp"; /// <summary> /// カタログにアドレスが登録されていないのであれば登録する /// </summary> public static async Task<T> LoadAssetAsync<T>(string address) { var handle = Addressables.LoadResourceLocationsAsync(address); await handle.Task; // ロード成功した if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result != null && handle.Result.Count > 0) { return (T) handle.Result.ElementAt(0); } // アドレスに対応したファイルを探す if (!TryGetPathToAddress(address, out var path)) { Debug.LogError("Not Found"); return default; } var resourceProviders = Addressables.ResourceManager.ResourceProviders; if (resourceProviders.All(v => v.GetType() != typeof(AssetDatabaseProvider))) { resourceProviders.Add(new AssetDatabaseProvider()); } var resourceLocationMap = Addressables.ResourceLocators .Where(v => v.GetType() == typeof(ResourceLocationMap)) .FirstOrDefault(v => v.LocatorId == LOCATION_ID); if (resourceLocationMap == null) { resourceLocationMap = new ResourceLocationMap(LOCATION_ID); Addressables.AddResourceLocator(resourceLocationMap); } var location = new ResourceLocationBase( address, path, typeof(AssetDatabaseProvider).FullName, AssetDatabase.GetMainAssetTypeAtPath(path) ); ((ResourceLocationMap)resourceLocationMap).Add(address, location); var reloadHandle = Addressables.LoadAssetAsync<T>(address); await reloadHandle.Task; return reloadHandle.Result; } /// <summary> /// 対象のアドレスからパスを検索する /// </summary> private static bool TryGetPathToAddress(string address, out string path) { var addressDirName = Path.GetDirectoryName(address); var addressFileName = Path.GetFileNameWithoutExtension(address); var guids = AssetDatabase.FindAssets(addressFileName, new[] {addressDirName}); foreach (var guid in guids) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); var fileName = Path.GetFileNameWithoutExtension(assetPath); if (fileName != addressFileName) continue; path = assetPath; return true; } path = null; return false; } }
呼び出し処理は以下のようにします
ただし今回の処理では Handle を返してないので内部でリークしてしまうのでよしなに調整する必要があります
public class Sample : MonoBehaviour { public string Address; public string Address2; private async void Start() { var obj = await AddressableUtility.LoadAssetAsync<GameObject>(Address); Instantiate(obj); obj = await AddressableUtility.LoadAssetAsync<GameObject>(Address2); Instantiate(obj); } }