Skip to content

Instantly share code, notes, and snippets.

@jeffvella
Created May 3, 2020 16:10
Show Gist options
  • Save jeffvella/779736613a6d36b26aac0029cfa71f72 to your computer and use it in GitHub Desktop.
Save jeffvella/779736613a6d36b26aac0029cfa71f72 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using Object = UnityEngine.Object;
public class AddressableLabels
{
public const string Default = "default";
public const string Initialization = "initialization";
public const string Instantiate = "instantiate";
}
public class AddressablesLoader : MonoBehaviour
{
void Awake()
{
Addressables.ResourceManager.CreateChainOperation(AssetCache.LoadAsync(AddressableLabels.Initialization),
op1 => Addressables.LoadAssetsAsync<GameObject>(AddressableLabels.Instantiate, go => Instantiate(go)));
}
}
public static class AssetCache
{
private static Dictionary<string, CachedAssetResource> _assetReferences = new Dictionary<string, CachedAssetResource>();
public static AsyncOperationHandle DownloadResourcesHandle { get; private set; }
public static AsyncOperationHandle<IList<Object>> LoadAssetsHandle { get; private set; }
private static Dictionary<string, AsyncOperationHandle> _ops;
private static AsyncOperationHandle<IList<Object>> _loadChain;
private static Dictionary<int, string> _keysByIndex = new Dictionary<int, string>();
private static Dictionary<string, int> _indexByKey = new Dictionary<string, int>();
public delegate void AssetCacheEventHandler();
public static event AssetCacheEventHandler LoadCompleted;
public static AsyncOperationHandle<IList<Object>> LoadAsync(object key)
{
DownloadResourcesHandle = Addressables.DownloadDependenciesAsync(key);
// Now go through each addressable and cache the object.
LoadAssetsHandle = Addressables.ResourceManager.CreateChainOperation(DownloadResourcesHandle, LoadAssets);
return LoadAssetsHandle;
}
public static T Instantiate<T>(object key) where T : Object
{
// doesn't work yet on labels etc
return UnityEngine.Object.Instantiate(GetAsset<T>(key));
}
private static AsyncOperationHandle<IList<Object>> LoadAssets(AsyncOperationHandle arg)
{
var locs = Addressables.ResourceLocators;
var toLoad = new List<IResourceLocation>();
var index = 0;
for (int i = 0; i < locs.Count; i++)
{
var loc = locs[i];
foreach (object objKey in loc.Keys)
{
if (!(objKey is string key))
continue;
if (!Guid.TryParse(key, out Guid keyGuid))
continue;
loc.Locate(key, typeof(UnityEngine.Object), out var locationsFromKey);
// Everything except those with a RuntimeKey have already been excluded;
var location = locationsFromKey[0];
var entry = new CachedAssetResource
{
RunTimeKeyGuid = keyGuid,
RunTimeKeyString = key,
ResourceLocation = location
};
toLoad.Add(location);
_assetReferences[location.ToString()] = entry;
_assetReferences[key] = entry;
_keysByIndex[index] = key;
_indexByKey[key] = index;
index++;
//Debug.Log($"Processed Addressable: Key={key}, Path={location}");
}
}
_loadChain = Addressables.LoadAssetsAsync<UnityEngine.Object>(toLoad, OnItemsLoadCompleted);
_loadChain.Completed += LoadChain_Completed;
return _loadChain;
}
private static void LoadChain_Completed(AsyncOperationHandle<IList<Object>> obj)
{
for (int i = 0; i < obj.Result.Count; i++)
{
var resource = _assetReferences[_keysByIndex[i]];
resource.Asset = obj.Result[i];
}
var handler = LoadCompleted;
handler?.Invoke();
//Debug.Log($"LoadChain_Completed: TotalAssets={obj.Result.Count}");
}
private static void OnItemsLoadCompleted(Object obj)
{
//Debug.Log($"Loaded Asset: {obj.name}");
}
public class CachedAssetResource
{
public IResourceLocation ResourceLocation;
public Object Asset;
public Guid RunTimeKeyGuid;
public string RunTimeKeyString;
}
public static T GetAsset<T>(object runtimeKey) where T : UnityEngine.Object
{
var key = EvaluateKey(runtimeKey);
if (_assetReferences.TryGetValue(key, out var assetReference))
{
return (T)assetReference.Asset;
}
return default;
}
private static string EvaluateKey(object obj)
{
return (string)(obj is IKeyEvaluator evaluator ? evaluator.RuntimeKey : obj);
}
}
using System;
using Unity.Collections;
using Unity.Entities;
using UnityEngine;
using UnityEngine.AddressableAssets;
/// <summary>
/// Implicitly convertable version of AssetReference, that can be stored in components.
/// Holds the RuntimeKey used to find it again at runtime through Addressables.
/// </summary>
public struct NativeAssetReference : IComponentData, IEquatable<NativeAssetReference>, IKeyEvaluator
{
public NativeString512 RuntimeKey;
public int Hash;
public static implicit operator AssetReference(NativeAssetReference instance)
{
return new AssetReference(instance.RuntimeKey.ToString());
}
public static implicit operator NativeAssetReference(AssetReference instance)
{
Debug.Assert(instance.RuntimeKeyIsValid(), $"The AssetReference is not valid. Key={instance.RuntimeKey}");
var key = (string)instance.RuntimeKey;
var nativeKey = new NativeString512(key);
var result = new NativeAssetReference
{
RuntimeKey = nativeKey,
Hash = CreateHash(key)
};
return result;
}
public bool IsValid => Hash != 0;
public static int CreateHash(string input)
{
var hash = 0;
foreach (var t in input)
hash = (hash << 5) + hash + t;
return hash;
}
public bool Equals(NativeAssetReference other) => Hash == other.Hash;
public override bool Equals(object obj) => obj is NativeAssetReference other && Equals(other);
public override int GetHashCode() => Hash;
public override string ToString() => $"{Hash}, Key={RuntimeKey.ToString()}";
object IKeyEvaluator.RuntimeKey => RuntimeKey.ToString();
bool IKeyEvaluator.RuntimeKeyIsValid() => IsValid;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment