Files
Hasebe/Library/PackageCache/com.unity.addressables@42676f2a154e/Runtime/AssetReference.cs
T
2026-09-21 12:15:57 +03:00

962 lines
36 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine.AddressableAssets.Utility;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
using UnityEngine.Serialization;
using UnityEngine.U2D;
#if UNITY_EDITOR
using System.IO;
using UnityEditor;
#endif
namespace UnityEngine.AddressableAssets
{
/// <summary>
/// Generic version of AssetReference class. This should not be used directly as CustomPropertyDrawers do not support generic types. Instead use the concrete derived classes such as AssetReferenceGameObject.
/// </summary>
/// <typeparam name="TObject">The type of object to use with this AssetReference</typeparam>
[Serializable]
public class AssetReferenceT<TObject> : AssetReference where TObject : Object
{
/// <summary>
/// Construct a new AssetReference object.
/// </summary>
/// <param name="guid">The guid of the asset.</param>
public AssetReferenceT(string guid)
: base(guid)
{
}
#if UNITY_EDITOR
protected override internal Type DerivedClassType => typeof(TObject);
#endif
/// <summary>
/// Load the referenced asset as type TObject.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use <see cref="Addressables.LoadAssetAsync{TObject}(object)"/> and pass your AssetReference in as the key.
/// on an AssetReference, use Addressables.LoadAssetAsync&lt;&gt;() and pass your AssetReference in as the key.
///
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <returns>The load operation.</returns>
public virtual AsyncOperationHandle<TObject> LoadAssetAsync()
{
return LoadAssetAsync<TObject>();
}
/// <inheritdoc/>
public override bool ValidateAsset(Object obj)
{
var type = obj.GetType();
return typeof(TObject).IsAssignableFrom(type);
}
/// <summary>
/// Validates that the asset located at a path is allowable for this asset reference. An asset is allowable if
/// it is of the correct type or if one of its sub-asset is.
/// </summary>
/// <param name="mainAssetPath">The path to the asset in question.</param>
/// <returns>Whether the referenced asset is valid.</returns>
public override bool ValidateAsset(string mainAssetPath)
{
#if UNITY_EDITOR
Type objType = typeof(TObject);
if (objType.IsAssignableFrom(AssetDatabase.GetMainAssetTypeAtPath(mainAssetPath)))
return true;
var types = AssetPathToTypes.GetTypesForAssetPath(mainAssetPath);
return types.Contains(objType);
#else
return false;
#endif
}
#if UNITY_EDITOR
/// <summary>
/// Type-specific override of parent editorAsset. Used by the editor to represent the main asset referenced.
/// </summary>
/// <returns>Editor Asset as type TObject, else null</returns>
public new TObject editorAsset
{
get => base.editorAsset as TObject;
}
#endif
}
/// <summary>
/// GameObject only asset reference.
/// </summary>
[Serializable]
public class AssetReferenceGameObject : AssetReferenceT<GameObject>
{
/// <summary>
/// Constructs a new reference to a GameObject.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceGameObject(string guid) : base(guid)
{
}
}
/// <summary>
/// Texture only asset reference.
/// </summary>
[Serializable]
public class AssetReferenceTexture : AssetReferenceT<Texture>
{
/// <summary>
/// Constructs a new reference to a Texture.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceTexture(string guid) : base(guid)
{
}
}
/// <summary>
/// Texture2D only asset reference.
/// </summary>
[Serializable]
public class AssetReferenceTexture2D : AssetReferenceT<Texture2D>
{
/// <summary>
/// Constructs a new reference to a Texture2D.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceTexture2D(string guid) : base(guid)
{
}
}
/// <summary>
/// Texture3D only asset reference
/// </summary>
[Serializable]
public class AssetReferenceTexture3D : AssetReferenceT<Texture3D>
{
/// <summary>
/// Constructs a new reference to a Texture3D.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceTexture3D(string guid) : base(guid)
{
}
}
/// <summary>
/// <see cref="AssetReference"/> that only allows <see cref="Sprite"/> objects.
/// </summary>
[Serializable]
public class AssetReferenceSprite : AssetReferenceT<Sprite>
{
/// <summary>
/// Constructs a new reference to a AssetReferenceSprite.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceSprite(string guid) : base(guid)
{
}
/// <summary>
/// Checks whether the asset located at a path is valid for this asset reference. An asset is valid if
/// it is of the correct type or if one of its sub-assets are.
/// </summary>
/// <param name="path">The file path to the asset in question.</param>
/// <returns>Whether the referenced asset is valid.</returns>
/// <remarks>
/// The asset can be either a SpriteAtlas or a Sprite.
/// </remarks>
/// <example>
/// <para>
/// The example below uses ValidateAsset to check if an asset at a specific path can be set.</para>
/// <code source="../Tests/Editor/DocExampleCode/ScriptReference/UsingAssetRefSpriteValidateAsset.cs" region="SAMPLE"/>
/// </example>
public override bool ValidateAsset(string path)
{
#if UNITY_EDITOR
if (AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(SpriteAtlas))
return true;
var type = AssetDatabase.GetMainAssetTypeAtPath(path);
bool isTexture = typeof(Texture2D).IsAssignableFrom(type);
if (isTexture)
{
var importer = AssetImporter.GetAtPath(path) as TextureImporter;
return (importer != null) && (importer.spriteImportMode != SpriteImportMode.None);
}
#endif
return false;
}
#if UNITY_EDITOR
/// <summary>
/// Typeless override of parent editorAsset. Used by the editor to represent the main asset referenced.
/// </summary>
public new Object editorAsset
{
get => GetEditorAssetInternal();
}
internal override Object GetEditorAssetInternal()
{
if (CachedAsset != null || string.IsNullOrEmpty(m_AssetGUID))
return CachedAsset;
var assetPath = AssetDatabase.GUIDToAssetPath(m_AssetGUID);
Type mainAssetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
Object asset = mainAssetType == typeof(SpriteAtlas) ? AssetDatabase.LoadAssetAtPath(assetPath, typeof(SpriteAtlas)) : AssetDatabase.LoadAssetAtPath(assetPath, DerivedClassType);
if (DerivedClassType == null)
return CachedAsset = asset;
if (asset == null)
Debug.LogWarning($"Assigned editorAsset does not match type {typeof(SpriteAtlas)} or {DerivedClassType}. EditorAsset will be null.");
return CachedAsset = asset;
}
internal override bool SetEditorAssetInternal(Object value)
{
if (value is SpriteAtlas)
return OnSetEditorAsset(value, typeof(SpriteAtlas));
if (value is Texture2D)
return OnSetEditorAsset(value, typeof(Texture2D));
return base.SetEditorAssetInternal(value);
}
#endif
}
/// <summary>
/// <see cref="AssetReference"/> that only allows atlassed <see cref="Sprite"/> objects. This will prevent legacy sprites from being used in this reference.
/// If legacy sprite usage is needed, <see cref="AssetReferenceSprite"/> can be used instead.
/// </summary>
[Serializable]
public class AssetReferenceAtlasedSprite : AssetReferenceT<Sprite>
{
/// <summary>
/// Constructs a new reference to a AssetReferenceAtlasedSprite.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceAtlasedSprite(string guid) : base(guid)
{
}
#if UNITY_EDITOR
internal override Object GetEditorAssetInternal()
{
if (CachedAsset != null || string.IsNullOrEmpty(m_AssetGUID))
return CachedAsset;
var assetPath = AssetDatabase.GUIDToAssetPath(m_AssetGUID);
Object asset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(SpriteAtlas));
if (asset == null)
Debug.LogWarning($"Assigned editorAsset does not match type {typeof(SpriteAtlas)}. EditorAsset will be null.");
return CachedAsset = asset;
}
internal override bool SetEditorAssetInternal(Object value)
{
return OnSetEditorAsset(value, typeof(SpriteAtlas));
}
#endif
/// <inheritdoc/>
public override bool ValidateAsset(Object obj)
{
return obj is SpriteAtlas;
}
/// <inheritdoc/>
public override bool ValidateAsset(string path)
{
#if UNITY_EDITOR
return AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(SpriteAtlas);
#else
return false;
#endif
}
#if UNITY_EDITOR
/// <summary>
/// SpriteAtlas Type-specific override of parent editorAsset. Used by the editor to represent the main asset referenced.
/// </summary>
public new SpriteAtlas editorAsset
{
get
{
if (CachedAsset != null || string.IsNullOrEmpty(AssetGUID))
return CachedAsset as SpriteAtlas;
var assetPath = AssetDatabase.GUIDToAssetPath(AssetGUID);
var main = AssetDatabase.LoadMainAssetAtPath(assetPath) as SpriteAtlas;
if (main != null)
CachedAsset = main;
return main;
}
}
#endif
}
/// <summary>
/// Reference to an addressable asset. This can be used in script to provide fields that can be easily set in the editor and loaded dynamically at runtime.
/// To determine if the reference is set, use RuntimeKeyIsValid().
/// </summary>
[Serializable]
public class AssetReference : IKeyEvaluator
{
/// <summary>
/// The GUID of an asset
/// </summary>
[FormerlySerializedAs("m_assetGUID")]
[SerializeField]
protected internal string m_AssetGUID = "";
[SerializeField]
string m_SubObjectName;
[SerializeField]
string m_SubObjectType = null;
AsyncOperationHandle m_Operation;
#if UNITY_EDITOR
// we store the SubObjectGUID in the Editor to track things like renames
// and still point to the correct object in the UI
[SerializeField]
string m_SubObjectGUID;
virtual protected internal Type DerivedClassType { get; }
#endif
/// <summary>
/// The AsyncOperationHandle currently being used by the AssetReference.
/// For example, if you call AssetReference.LoadAssetAsync, this property will return a handle to that operation.
/// </summary>
public AsyncOperationHandle OperationHandle
{
get { return m_Operation; }
internal set
{
m_Operation = value;
#if UNITY_EDITOR
if (m_Operation.Status != AsyncOperationStatus.Failed)
m_ActiveAssetReferences.Add(this);
#endif
}
}
/// <summary>
/// The actual key used to request the asset at runtime. RuntimeKeyIsValid() can be used to determine if this reference was set.
/// </summary>
public virtual object RuntimeKey
{
get
{
if (m_AssetGUID == null)
m_AssetGUID = string.Empty;
if (!string.IsNullOrEmpty(m_SubObjectName))
return string.Format("{0}[{1}]", m_AssetGUID, m_SubObjectName);
return m_AssetGUID;
}
}
/// <summary>
/// Stores the guid of the asset.
/// </summary>
public virtual string AssetGUID
{
get { return m_AssetGUID; }
}
/// <summary>
/// Stores the name of the sub object (commonly referred to as a sub-asset).
/// </summary>
/// <remarks>
/// Some assets contain multiple sub-asset objects which can be independently loaded without loading the main object.
/// For example, the meshes inside a model and the sprites inside a sprite atlas can be referenced from an AssetReference
/// by the name of the object.
/// </remarks>
public virtual string SubObjectName
{
get { return m_SubObjectName; }
set { m_SubObjectName = value; }
}
#if UNITY_EDITOR
/// <summary>
/// Stores the guid of the sub object (if available).
/// This is used in the context of SpriteAtlases.
/// </summary>
public virtual string SubObjectGUID
{
get { return m_SubObjectGUID; }
set { m_SubObjectGUID = value; }
}
#endif
internal virtual Type SubObjectType
{
get
{
#if UNITY_EDITOR
if (!string.IsNullOrEmpty(m_SubObjectGUID) && m_SubObjectType != null)
return Type.GetType(m_SubObjectType);
#endif
if (!string.IsNullOrEmpty(m_SubObjectName) && m_SubObjectType != null)
return Type.GetType(m_SubObjectType);
return null;
}
}
/// <summary>
/// Returns the state of the internal operation. If the load has not been started or if it has been released, the operation will not be valid and this will return false.
/// This can be used to determine if an AssetReference has started loading or not.
/// </summary>
/// <returns>True if the operation is valid. A valid operation is created when loading begins and it is invalidated when it has been released.</returns>
public bool IsValid()
{
return m_Operation.IsValid();
}
/// <summary>
/// Get the loading status of the internal operation.
/// </summary>
/// <value>True if the operation is completed. If the operation is not valid, it will return false as well.</value>
public bool IsDone
{
get { return m_Operation.IsDone; }
}
#if UNITY_EDITOR
[InitializeOnLoadMethod]
static void RegisterForPlaymodeChange()
{
EditorApplication.playModeStateChanged -= EditorApplicationOnplayModeStateChanged;
EditorApplication.playModeStateChanged += EditorApplicationOnplayModeStateChanged;
}
static HashSet<AssetReference> m_ActiveAssetReferences = new HashSet<AssetReference>();
static void EditorApplicationOnplayModeStateChanged(PlayModeStateChange state)
{
if (EditorSettings.enterPlayModeOptionsEnabled && Addressables.reinitializeAddressables)
{
foreach (AssetReference reference in m_ActiveAssetReferences)
{
reference.ReleaseHandleWhenPlaymodeStateChanged(state);
}
}
}
void ReleaseHandleWhenPlaymodeStateChanged(PlayModeStateChange state)
{
if (m_Operation.IsValid())
m_Operation.Release();
}
#endif
/// <summary>
/// Construct a new AssetReference object.
/// </summary>
public AssetReference()
{
}
#if UNITY_EDITOR
~AssetReference()
{
m_ActiveAssetReferences.Remove(this);
}
#endif
/// <summary>
/// Construct a new AssetReference object.
/// </summary>
/// <param name="guid">The guid of the asset.</param>
public AssetReference(string guid)
{
m_AssetGUID = guid;
}
/// <summary>
/// The loaded asset. This value is only set after the AsyncOperationHandle returned from LoadAssetAsync completes.
/// It will not be set if only InstantiateAsync is called. It will be set to null if release is called.
/// </summary>
public virtual Object Asset
{
get
{
if (!m_Operation.IsValid())
return null;
return m_Operation.Result as Object;
}
}
#if UNITY_EDITOR
Object m_CachedAsset;
string m_CachedGUID = "";
/// <summary>
/// Cached Editor Asset.
/// </summary>
protected internal Object CachedAsset
{
get
{
if (m_CachedGUID != m_AssetGUID)
{
m_CachedAsset = null;
m_CachedGUID = "";
}
return m_CachedAsset;
}
set
{
m_CachedAsset = value;
m_CachedGUID = m_AssetGUID;
}
}
#endif
/// <summary>
/// String representation of asset reference.
/// </summary>
/// <returns>The asset guid as a string.</returns>
public override string ToString()
{
#if UNITY_EDITOR
return "[" + m_AssetGUID + "]" + CachedAsset;
#else
return "[" + m_AssetGUID + "]";
#endif
}
static AsyncOperationHandle<T> CreateFailedOperation<T>()
{
//this needs to be set in order for ResourceManager.ExceptionHandler to get hooked up to AddressablesImpl.LogException.
Addressables.InitializeAsync();
return Addressables.ResourceManager.CreateCompletedOperation(default(T), new Exception("Attempting to load an asset reference that has no asset assigned to it.").Message);
}
/// <summary>
/// Load the referenced asset as type TObject.
/// </summary>
/// <typeparam name="TObject">The object type.</typeparam>
/// <returns>The load operation if there is not a valid cached operation, otherwise return default operation.</returns>
/// <remarks>
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use <see cref="Addressables.LoadAssetAsync{TObject}(object)"/> and pass your AssetReference in as the key.
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </remarks>
public virtual AsyncOperationHandle<TObject> LoadAssetAsync<TObject>()
{
AsyncOperationHandle<TObject> result = default(AsyncOperationHandle<TObject>);
if (m_Operation.IsValid())
Debug.LogError("Attempting to load AssetReference that has already been loaded. Handle is exposed through getter OperationHandle");
else
{
result = Addressables.LoadAssetAsync<TObject>(RuntimeKey);
OperationHandle = result;
}
return result;
}
/// <summary>
/// Loads the reference as a scene.
/// </summary>
/// <param name="loadMode">Scene load mode.</param>
/// <param name="activateOnLoad">If false, the scene will load but not activate (for background loading). The SceneInstance returned has an Activate() method that can be called to do this at a later point.</param>
/// <param name="priority">Async operation priority for scene loading.</param>
/// <returns>The operation handle for the request if there is not a valid cached operation, otherwise return default operation</returns>
/// <remarks>
/// This cannot be used a second time until the first load is unloaded. If you wish to call load multiple times
/// on an AssetReference, use Addressables.LoadSceneAsync() and pass your AssetReference in as the key.
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </remarks>
public virtual AsyncOperationHandle<SceneInstance> LoadSceneAsync(LoadSceneMode loadMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100)
{
AsyncOperationHandle<SceneInstance> result = default(AsyncOperationHandle<SceneInstance>);
if (m_Operation.IsValid())
Debug.LogError("Attempting to load AssetReference Scene that has already been loaded. Handle is exposed through getter OperationHandle");
else
{
result = Addressables.LoadSceneAsync(RuntimeKey, loadMode, activateOnLoad, priority);
OperationHandle = result;
}
return result;
}
/// <summary>
/// Unloads the reference as a scene.
/// </summary>
/// <returns>The operation handle for the scene load.</returns>
public virtual AsyncOperationHandle<SceneInstance> UnLoadScene()
{
return Addressables.UnloadSceneAsync(m_Operation, true);
}
/// <summary>
/// Instantiates the referenced asset as a GameObject at the specified position and rotation, with an optional parent.
/// </summary>
/// <param name="position">Position of the instantiated object.</param>
/// <param name="rotation">Rotation of the instantiated object.</param>
/// <param name="parent">The parent of the instantiated object.</param>
/// <returns>The handle for the operation.</returns>
/// <remarks>
/// You can't use this again until you release the first load. If you want to call load multiple times
/// on an AssetReference, use `Addressables.InstantiateAsync` and pass the AssetReference in as the key.
/// Refer to the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </remarks>
public virtual AsyncOperationHandle<GameObject> InstantiateAsync(Vector3 position, Quaternion rotation, Transform parent = null)
{
return Addressables.InstantiateAsync(RuntimeKey, position, rotation, parent, true);
}
/// <summary>
/// Instantiates the referenced asset as a GameObject with an optional parent and world space option.
/// </summary>
/// <param name="parent">The parent of the instantiated object.</param>
/// <param name="instantiateInWorldSpace">Option to retain world space when instantiated with a parent.</param>
/// <returns>The handle for the operation.</returns>
/// <remarks>
/// You can't use this again until you release the first load. If you want to call load multiple times
/// on an AssetReference, use `Addressables.InstantiateAsync` and pass the AssetReference in as the key.
/// Refer to the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </remarks>
public virtual AsyncOperationHandle<GameObject> InstantiateAsync(Transform parent = null, bool instantiateInWorldSpace = false)
{
return Addressables.InstantiateAsync(RuntimeKey, parent, instantiateInWorldSpace, true);
}
/// <inheritdoc/>
public virtual bool RuntimeKeyIsValid()
{
Guid result;
string guid = RuntimeKey.ToString();
int subObjectIndex = guid.IndexOf('[');
if (subObjectIndex != -1) //This means we're dealing with a sub-object and need to convert the runtime key.
guid = guid.Substring(0, subObjectIndex);
return Guid.TryParse(guid, out result);
}
/// <summary>
/// Release the internal operation handle.
/// </summary>
public virtual void ReleaseAsset()
{
if (!m_Operation.IsValid())
{
Debug.LogWarning("Cannot release a null or unloaded asset.");
return;
}
m_Operation.Release();
m_Operation = default(AsyncOperationHandle);
}
/// <summary>
/// Release an instantiated object.
/// </summary>
/// <param name="obj">The object to release.</param>
public virtual void ReleaseInstance(GameObject obj)
{
Addressables.ReleaseInstance(obj);
}
/// <summary>
/// Validates that the referenced asset allowable for this asset reference.
/// </summary>
/// <param name="obj">The Object to validate.</param>
/// <returns>Whether the referenced asset is valid.</returns>
public virtual bool ValidateAsset(Object obj)
{
return true;
}
/// <summary>
/// Validates that the referenced asset allowable for this asset reference.
/// </summary>
/// <param name="path">The path to the asset in question.</param>
/// <returns>Whether the referenced asset is valid.</returns>
public virtual bool ValidateAsset(string path)
{
return true;
}
#if UNITY_EDITOR
[SerializeField]
#pragma warning disable CS0414
bool m_EditorAssetChanged;
#pragma warning restore CS0414
/// <summary>
/// Used by the editor to represent the main asset referenced.
/// </summary>
public virtual Object editorAsset
{
get { return GetEditorAssetInternal(); }
}
/// <summary>
/// Helper function that can be used to override the base class editorAsset accessor.
/// </summary>
/// <returns>Returns the main asset referenced used in the editor.</returns>
internal virtual Object GetEditorAssetInternal()
{
if (CachedAsset != null || string.IsNullOrEmpty(m_AssetGUID))
return CachedAsset;
var asset = FetchEditorAsset();
if (DerivedClassType == null)
return CachedAsset = asset;
if (asset == null || (asset != null && !DerivedClassType.IsAssignableFrom(asset.GetType())))
{
Debug.LogWarning("Assigned editorAsset does not match type " + DerivedClassType + ". EditorAsset will be null.");
return CachedAsset = null;
}
return CachedAsset = asset;
}
internal Object FetchEditorAsset()
{
var assetPath = AssetDatabase.GUIDToAssetPath(m_AssetGUID);
Object asset = null;
if (DerivedClassType != null)
asset = AssetDatabase.LoadAssetAtPath(assetPath, DerivedClassType);
// if we can't retrieve the asset by the DerivedClassType, try the MainAssetType
// This is useful for dealing with subclasses
if (asset == null)
asset = AssetDatabase.LoadAssetAtPath(assetPath, AssetDatabase.GetMainAssetTypeAtPath(assetPath));
return asset;
}
/// <summary>
/// Sets the main asset on the AssetReference. Only valid in the editor, this sets both the editorAsset attribute,
/// and the internal asset GUID, which drives the RuntimeKey attribute. If the reference uses a sub object,
/// then it will load the editor asset during edit mode and load the sub object during runtime. For example, if
/// the AssetReference is set to a sprite within a sprite atlas, the editorAsset is the atlas (loaded during edit mode)
/// and the sub object is the sprite (loaded during runtime). If called by AssetReferenceT, will set the editorAsset
/// to the requested object if the object is of type T, and null otherwise.
/// <param name="value">Object to reference</param>
/// </summary>
public virtual bool SetEditorAsset(Object value)
{
return SetEditorAssetInternal(value);
}
internal virtual bool SetEditorAssetInternal(Object value)
{
return OnSetEditorAsset(value, DerivedClassType);
}
internal bool OnSetEditorAsset(Object value, Type derivedType)
{
if (value == null)
{
CachedAsset = null;
m_AssetGUID = string.Empty;
m_SubObjectName = null;
m_SubObjectGUID = string.Empty;
m_EditorAssetChanged = true;
return true;
}
if (CachedAsset != value)
{
m_SubObjectName = null;
m_SubObjectGUID = string.Empty;
var path = AssetDatabase.GetAssetOrScenePath(value);
if (string.IsNullOrEmpty(path))
{
Addressables.LogWarningFormat("Invalid object for AssetReference {0}.", value);
return false;
}
if (!ValidateAsset(path))
{
Addressables.LogWarningFormat("Invalid asset for AssetReference path = '{0}'.", path);
return false;
}
else
{
m_AssetGUID = AssetDatabase.AssetPathToGUID(path);
Object mainAsset;
if (derivedType != null)
mainAsset = LocateEditorAssetForTypedAssetReference(value, path, derivedType);
else
{
mainAsset = AssetDatabase.LoadMainAssetAtPath(path);
if (value != mainAsset)
SetEditorSubObject(value);
}
CachedAsset = mainAsset;
}
}
m_EditorAssetChanged = true;
return true;
}
internal Object LocateEditorAssetForTypedAssetReference(Object value, string path, Type type)
{
Object mainAsset;
if (!type.IsAssignableFrom(value.GetType()))
{
mainAsset = null;
}
else
{
var actualType = AssetDatabase.GetMainAssetTypeAtPath(path);
mainAsset = AssetDatabase.LoadAssetAtPath(path, actualType);
if (mainAsset != value)
{
mainAsset = null;
var subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(path);
foreach (var asset in subAssets)
{
if (type.IsAssignableFrom(asset.GetType()) && value == asset)
{
mainAsset = asset;
break;
}
}
}
}
if (mainAsset == null)
Debug.LogWarning("Assigned editorAsset does not match type " + type + ". EditorAsset will be null.");
return mainAsset;
}
/// <summary>
/// Sets the sub object for this asset reference.
/// </summary>
/// <param name="value">The sub object.</param>
/// <returns>True if set correctly.</returns>
public virtual bool SetEditorSubObject(Object value)
{
if (value == null)
{
m_SubObjectName = null;
m_SubObjectGUID = string.Empty;
m_SubObjectType = null;
m_EditorAssetChanged = true;
return true;
}
if (editorAsset == null)
return false;
if (editorAsset.GetType() == typeof(SpriteAtlas))
{
var spriteName = AssetReferenceUtilities.FormatName(value.name);
var atlas = editorAsset as SpriteAtlas;
var foundMatch = false;
var subObjects = AssetReferenceUtilities.GetAtlasSpritesAndPackables(ref atlas);
foreach ((Object sprite, string guid) in subObjects)
{
var namesMatch = AssetReferenceUtilities.FormatName(sprite.name) == spriteName;
if (namesMatch)
{
foundMatch = true;
m_SubObjectGUID = guid;
}
}
if (!foundMatch)
{
Debug.LogWarningFormat("Unable to find sprite {0} in atlas {1}.", spriteName, editorAsset.name);
return false;
}
m_SubObjectName = spriteName;
m_SubObjectType = typeof(Sprite).AssemblyQualifiedName;
m_EditorAssetChanged = true;
return true;
}
var subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(AssetDatabase.GUIDToAssetPath(m_AssetGUID));
foreach (var s in subAssets)
{
if (s.name == value.name && s.GetType() == value.GetType())
{
m_SubObjectGUID = String.Empty;
m_SubObjectName = s.name;
m_SubObjectType = s.GetType().AssemblyQualifiedName;
m_EditorAssetChanged = true;
return true;
}
}
return false;
}
#endif
}
#if UNITY_EDITOR
class AssetPathToTypes : AssetPostprocessor
{
internal static Dictionary<string, HashSet<Type>> s_PathToTypes = new Dictionary<string, HashSet<Type>>();
public static HashSet<Type> GetTypesForAssetPath(string path)
{
AssetDatabase.SaveAssetIfDirty(AssetDatabase.GUIDFromAssetPath(path));
if (s_PathToTypes.TryGetValue(path, out HashSet<Type> value))
return value;
var objectsForAsset = AssetDatabase.LoadAllAssetRepresentationsAtPath(path);
value = AddTypesToPath(objectsForAsset, path);
return value;
}
internal static HashSet<Type> AddTypesToPath(Object[] objectsForAsset, string path)
{
HashSet<Type> value = new HashSet<Type>();
foreach (Object o in objectsForAsset)
{
if (o != null)
value.Add(o.GetType());
}
s_PathToTypes.Add(path, value);
return value;
}
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (string str in importedAssets)
s_PathToTypes.Remove(str);
foreach (string str in deletedAssets)
s_PathToTypes.Remove(str);
for (int i = 0; i < movedFromAssetPaths.Length; ++i)
{
if (s_PathToTypes.TryGetValue(movedFromAssetPaths[i], out var values))
{
s_PathToTypes.Remove(movedFromAssetPaths[i]);
s_PathToTypes.Add(movedAssets[i], values);
}
}
}
}
#endif
}