6
\$\begingroup\$

I'm working with AssetDatabase.CreateAsset and all over the internet (not so much in the official documentation) I see

AssetDatabase.CreateAsset(blah, blahPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();

Why?

SaveAssets says it Writes all unsaved asset changes to disk so I assume that there's a situation in which you can have changes to your assets in memory, but not on disk. Is this the case when you create an asset with CreateAsset? I tried ending the Unity process right after creating a file but it looks like it writes to disk immediately, but I've only tried in-editor on Windows. Maybe it behaves differently in runtime or on different platforms.

Refresh says it will import any assets that have changed their content modification data or have been added-removed to the project folder. The editor UI gets updated when I call CreateAsset even if I don't Refresh, so is this for updates from other sources, or what?

I'm trying to understand the process and not do things just because everyone does it like that.

\$\endgroup\$
1
  • \$\begingroup\$ I'm not familiar with AssetDatabases, but the general rule is, if the documentation doesn't support something, you are not supposed to use it that way. Obviously there are exceptions, but then its up to you. \$\endgroup\$ Commented Aug 31, 2018 at 15:14

2 Answers 2

8
\$\begingroup\$

Unity lacks clarity in its documentation on when to use different AssetDatabase methods. That's why a lot of people call all the available methods after an asset change just to be sure.

Robert C. Martin provides a good piece of advice in his "Clean Code" book: When you are not sure how API works, write unit tests for it. Not only will you learn how to use it, but when there are new releases, just running those tests again will detect if there are changes you should know of.

That's what I did, and you can run those tests on your machine too: https://gist.github.com/SolidAlloy/3027f88e69b63700b9ae530360cfd0eb

Here is what I discovered:

  • CreateAsset() and ImportAsset() add the file to AssetDatabase automatically, so you don't have to call any other methods.
  • AssetDatabase.Refresh() is only needed when a file was added through System.IO (like File.WriteAllText()). Without it, you will not be able to get the asset GUID or load a Unity.Object from the asset.
  • AssetDatabase.SaveAssets() is only needed when you made changes to a Unity.Object instance (e.g. ScriptableObject), marked the object as dirty, and want to see the changes written to disk. Unity Editor writes all the changes to disk upon exit, so the use of SaveAssets() is very limited. It is only helpful when you changed a scriptable object and want to open the .asset file in a text editor and see the changes there without closing the editor.

I also discovered a weird bug/feature that may cause a lot of headaches if you are not aware of it:
If you create an asset and then remove it, AssetDatabase will store the deleted asset GUID and path until the Editor closes. No method can remove the GUID from the database.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Great answer! Thanks for sharing all these details and the tests to back it up! \$\endgroup\$
    – DMGregory
    Commented Jan 20, 2021 at 14:23
1
\$\begingroup\$

To add some details to the already great answer:

  • Using SaveAssets is overstepping your boundaries!

Call SaveAssetIfDirty (since Unity 2020) with your asset path instead. Leave the other unsaved assets alone, other scripts or the user need to take care of them. They may not want them to be saved to begin with.

  • NO AssetDatabase method requires calling Refresh afterwards. EVER!

SaveAssets and Refresh are being appended to way too many AssetDatabase methods ONLY out of habit, following a ritual, a cargo cult. STOP DOING THAT!

Refresh is only needed when a file or folder was modified outside the AssetDatabase AND you cannot possibly know which file(s)/folder(s) were modified.

Typically you DO know EXACTLY what was modified. In that case, you call ImportAsset for each modified file/folder.

The AssetDatabase methods are very unfortunately named. Refresh should have been called "CheckAllAssetFilesForModificationsAndThenUnloadUnusedResources". Just to be in line with the usual length of AssetDatabase names. I called mine "ImportAll".

Refresh's issue is that it goes over each file in your project to check if one has been added, modified or removed! This naturally takes time. More so, the more assets you have in the project.

Refresh also calls "unload unused resources" internally so any asset that is loaded (cached) in memory but currently not referenced gets unloaded. The next time you click on an asset in the project view or a script that calls LoadAsset will have to load that asset from disk rather than using the cached instance.

Problem is: the scripts get usually created in simple projects or early on. Especially tool developers never get to experience, and profile, their scripts on actual user projects. Then users of big projects get to suffer and complain about how slow Unity is. Neither side even THINKS about the cause being inefficient editor scripts because everyone is so used to seeing SaveAsset + Refresh in every script.

I made a video about such incorrect AssetDatabase uses and what they do to editor performance. I found even worse examples than I could have imagined.

I also rewrote the entire AssetDatabase to hopefully end this crud. At the least look at the test cases. They all succeed without a single call to Refresh.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .