I sure wish I knew the answer to any of this.
One thing to note that AssetDatabase.DeleteAsset()
returns false if the file wasn’t there to delete in the first place (… which can be pretty counter-intuitive, but there you go).
If you close Unity and restart it, it will forget about the guid-to-assetPath mapping.
My (sad) guess is that you understand things correctly, and there’s an oversight in the API because who would want to check if a thing exists? You can just know that it exists and then check the return value when you delete it. … I’d be happy to be proved wrong.
Here’s a cadre of other things I tried that didn’t seem to help (some of these are nonpublic methods you have to invoke via reflection; “results” only verified for 2018.4):
EditorUtility.UnloadUnusedAssetsImmediate(includeMonoReferenceAsRoots:false);
EditorUtility.UnloadUnusedAssetsImmediate(includeMonoReferenceAsRoots:true);
AssetDatabase.ReleaseCachedFileHandles();
AssetDatabase.SaveAssets();
AssetDatabase.CloseCachedFiles();
AssetDatabase.UnregisterAssetFolder();
And this is the Solution I ended up with. It works for deleted folders and a number of other common cases. I’m not 100% sure it works for everything, and I’m somewhat afraid there are going to be false positives… but it’s the best I’ve got (short of quitting and restarting Unity)
public static bool AssetActuallyExists(string assetPath)
{
return !(AssetDatabase.AssetPathToGUID(assetPath) == string.Empty ||
AssetDatabase.GetMainAssetTypeAtPath(assetPath) != null);
}
Test Code:
using NUnit.Framework;
using System;
using UnityEditor;
using UnityEngine;
namespace WhyWasThisSoDifficult
{
[TestFixture]
public class ExistenceTests
{
// Test Utility for creating, copying, and managing different Assets
private class TestAsset
{
public string Path { get; private set; }
public string DeletedPath
{
get
{
var fileName = System.IO.Path.GetFileNameWithoutExtension(Path);
return Path.Replace(fileName, fileName + "_Deleted");
}
}
public TestAsset(string path)
{
Path = System.IO.Path.Combine("Assets", path);
AssetDatabase.CopyAsset(Path, DeletedPath);
AssetDatabase.DeleteAsset(DeletedPath);
}
public TestAsset(string path, bool folder)
{
Path = path;
AssetDatabase.CreateFolder("Assets", Path);
AssetDatabase.CreateFolder("Assets", DeletedPath);
Path = System.IO.Path.Combine("Assets", path);
AssetDatabase.DeleteAsset(DeletedPath);
}
public TestAsset(UnityEngine.Object original, string path)
{
Action<UnityEngine.Object, string> CreateAsset =
(obj, p) => AssetDatabase.CreateAsset(obj, p);
if(original.GetType() == typeof(GameObject))
{
CreateAsset = (obj, p) => PrefabUtility.SaveAsPrefabAsset(obj as GameObject, p);
}
Path = System.IO.Path.Combine("Assets", path);
CreateAsset(original, Path);
UnityEngine.Object clone = UnityEngine.Object.Instantiate(original);
CreateAsset(clone, DeletedPath);
UnityEngine.Object.DestroyImmediate(clone, true);
AssetDatabase.DeleteAsset(DeletedPath);
}
public bool Exists
{
get
{
return !(AssetDatabase.AssetPathToGUID(Path) == null ||
AssetDatabase.GetMainAssetTypeAtPath(Path) == null);
}
}
public bool DeletedExists
{
get
{
return !(AssetDatabase.AssetPathToGUID(DeletedPath) == null ||
AssetDatabase.GetMainAssetTypeAtPath(DeletedPath) == null);
}
}
}
TestAsset prefab;
TestAsset folder;
TestAsset material;
TestAsset text;
TestAsset monoScript;
TestAsset library;
[OneTimeSetUp]
public void SetupFixture()
{
// This can only really be done once, because AFAIK there's no way to get Unity back to a fresh state without restarting it.
folder = new TestAsset("Folder", folder: true);
prefab = new TestAsset(GameObject.CreatePrimitive(PrimitiveType.Cube), "Prefab.prefab");
material = new TestAsset(new Material(Shader.Find("Standard")), "Material.mat");
text = new TestAsset(new TextAsset("This is some text"), "Text.txt");
monoScript = new TestAsset(new MonoScript() { }, "MonoScript.cs");
library = new TestAsset(@"Packages/Runtime/OculusVR/Plugins/1.47.0/Win64/OVRPlugin.dll");
}
[OneTimeTearDown]
public void TeardownFixture()
{
AssetDatabase.DeleteAsset(folder.Path);
AssetDatabase.DeleteAsset(prefab.Path);
AssetDatabase.DeleteAsset(material.Path);
AssetDatabase.DeleteAsset(text.Path);
AssetDatabase.DeleteAsset(monoScript.Path);
}
[Test]
public void Created_Things_Exist()
{
Assert.IsTrue(prefab.Exists, "prefab didn't exist");
Assert.IsTrue(folder.Exists, "folder didn't exist");
Assert.IsTrue(material.Exists, "material didn't exist");
Assert.IsTrue(text.Exists, "text didn't exist");
Assert.IsTrue(monoScript.Exists, "monoScript didn't exist");
Assert.IsTrue(library.Exists, "library didn't exist");
}
[Test]
public void Deleted_Things_Dont_Exist()
{
Assert.IsFalse(prefab.DeletedExists, "deleted prefab _did_ exist!");
Assert.IsFalse(folder.DeletedExists, "deleted folder _did_ exist!");
Assert.IsFalse(material.DeletedExists, "deleted material _did_ exist!");
Assert.IsFalse(text.DeletedExists, "deleted text _did_ exist!");
Assert.IsFalse(monoScript.DeletedExists, "deleted monoScript _did_ exist!");
Assert.IsFalse(library.DeletedExists, "deleted library _did_ exist!");
}
}
}