Skip to content

Commit

Permalink
remove extra call to ImportIfNotImported, update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeSmile-0000011110110111 committed Dec 26, 2023
1 parent 3248287 commit 7c7ff29
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 128 deletions.
36 changes: 19 additions & 17 deletions Editor/Asset.File.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ public static void BatchEditing([NotNull] Action massAssetFileEditAction)
/// - <see cref="CodeSmileEditor.Asset.File.Create(String,CodeSmileEditor.Asset.Path)" />
/// - <see cref="CodeSmileEditor.Asset.File.Create(Object,CodeSmileEditor.Asset.Path)" />
/// </seealso>
public static Object Create([NotNull] Byte[] contents, [NotNull] Path path) => CreateInternal(contents, path);
public static Object Create([NotNull] Byte[] contents, [NotNull] Path path) =>
CreateInternal(contents, path);

/// <summary>
/// Writes the byte array to disk, then imports and loads the asset. Generates a unique file name
Expand Down Expand Up @@ -128,7 +129,8 @@ public static Object CreateAsNew([NotNull] Byte[] contents, [NotNull] Path path)
/// - <see cref="CodeSmileEditor.Asset.File.Create(Byte[],CodeSmileEditor.Asset.Path)" />
/// - <see cref="CodeSmileEditor.Asset.File.Create(Object,CodeSmileEditor.Asset.Path)" />
/// </seealso>
public static Object Create([NotNull] String contents, [NotNull] Path path) => CreateInternal(contents, path);
public static Object Create([NotNull] String contents, [NotNull] Path path) =>
CreateInternal(contents, path);

/// <summary>
/// Writes the string to disk, then imports and loads the asset. Generates a unique file name
Expand Down Expand Up @@ -160,7 +162,8 @@ public static Object CreateAsNew([NotNull] String contents, [NotNull] Path path)
/// - <see cref="CodeSmileEditor.Asset.File.CreateOrLoad{T}" />
/// - <a href="https://docs.unity3d.com/ScriptReference/AssetDatabase.CreateAsset.html">AssetDatabase.CreateAsset</a>
/// </seealso>
public static Object Create([NotNull] Object instance, [NotNull] Path path) => CreateInternal(instance, path);
public static Object Create([NotNull] Object instance, [NotNull] Path path) =>
CreateInternal(instance, path);

/// <summary>
/// Writes the object to disk. Generates a unique file name if an asset exists at the path.
Expand Down Expand Up @@ -301,14 +304,9 @@ public static void Import([NotNull] Path path, ImportAssetOptions options = Impo
/// -
/// <a href="https://docs.unity3d.com/ScriptReference/AssetDatabase.LoadAssetAtPath.html">AssetDatabase.LoadAssetAtPath</a>
/// </seealso>
public static T ImportAndLoad<T>([NotNull] Path path, ImportAssetOptions options = ImportAssetOptions.Default)
where T : Object
{
ThrowIf.ArgumentIsNull(path, nameof(path));

ImportIfNotImported(path, options);
return Load<T>(path);
}
public static T ImportAndLoad<T>([NotNull] Path path,
ImportAssetOptions options = ImportAssetOptions.Default)
where T : Object => Load<T>(path);

/// <summary>
/// Imports multiple paths that were created or modified 'externally'.
Expand All @@ -323,7 +321,8 @@ public static T ImportAndLoad<T>([NotNull] Path path, ImportAssetOptions options
/// - <see cref="CodeSmileEditor.Asset.File.BatchEditing" />
/// - <a href="https://docs.unity3d.com/ScriptReference/AssetDatabase.ImportAsset.html">AssetDatabase.ImportAsset</a>
/// </seealso>
public static void Import([NotNull] Path[] paths, ImportAssetOptions options = ImportAssetOptions.Default) =>
public static void
Import([NotNull] Path[] paths, ImportAssetOptions options = ImportAssetOptions.Default) =>
Import(Path.ToStrings(paths), options);

/// <summary>
Expand All @@ -339,7 +338,8 @@ public static void Import([NotNull] Path[] paths, ImportAssetOptions options = I
/// - <see cref="CodeSmileEditor.Asset.File.BatchEditing" />
/// - <a href="https://docs.unity3d.com/ScriptReference/AssetDatabase.ImportAsset.html">AssetDatabase.ImportAsset</a>
/// </seealso>
public static void Import([NotNull] String[] paths, ImportAssetOptions options = ImportAssetOptions.Default) =>
public static void
Import([NotNull] String[] paths, ImportAssetOptions options = ImportAssetOptions.Default) =>
BatchEditing(
() =>
{
Expand Down Expand Up @@ -491,9 +491,10 @@ public static AssetDatabaseLoadOperation LoadAsync([NotNull] Path path, Int64 lo
/// </a>
/// </seealso>
[ExcludeFromCodeCoverage] // simple relay
public static String[] Find([NotNull] String filter, String[] searchInFolders = null) => searchInFolders == null
? AssetDatabase.FindAssets(filter)
: AssetDatabase.FindAssets(filter, searchInFolders);
public static String[] Find([NotNull] String filter, String[] searchInFolders = null) =>
searchInFolders == null
? AssetDatabase.FindAssets(filter)
: AssetDatabase.FindAssets(filter, searchInFolders);

/// <summary>
/// Finds asset GUIDs by the given filter criteria.
Expand Down Expand Up @@ -670,7 +671,8 @@ public static Boolean Rename([NotNull] Path path, String newFileName) =>
/// <a href="https://docs.unity3d.com/ScriptReference/AssetDatabase.CanOpenAssetInEditor.html">AssetDatabase.CanOpenAssetInEditor</a>
/// </seealso>
[ExcludeFromCodeCoverage] // simple relay
public static Boolean CanOpenInEditor([NotNull] Object instance) => CanOpenInEditor(instance.GetInstanceID());
public static Boolean CanOpenInEditor([NotNull] Object instance) =>
CanOpenInEditor(instance.GetInstanceID());

/// <summary>
/// Returns true if the given object can be opened (edited) by the Unity editor.
Expand Down
3 changes: 2 additions & 1 deletion Editor/Asset.SubAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ public static Boolean Extract([NotNull] Object subAsset, [NotNull] Path destinat
}

/// <summary>
/// Adds an object as sub-asset to the asset.
/// Adds an object as sub-asset to the asset. This change is implicitly saved to disk.
/// </summary>
/// <remarks>You do NOT need to call Save() afterwards.</remarks>
/// <param name="subAssetInstance">The object to add as a sub-asset. It must not already be an asset.</param>
/// <param name="asset">Instance of an asset.</param>
/// <seealso cref="">
Expand Down
1 change: 1 addition & 0 deletions Editor/Asset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ public void ExportPackage(String packagePath, ExportPackageOptions options = Exp
/// <summary>
/// Adds an object as a sub-object to the asset. The object must not already be an asset.
/// </summary>
/// <remarks>This implicitly saves the change to disk - you do NOT need to call Save() afterwards.</remarks>
/// <param name="instance">The object instance to add as subobject to this asset.</param>
/// <seealso cref="">
/// - <see cref="CodeSmileEditor.Asset.RemoveSubAsset" />
Expand Down
155 changes: 45 additions & 110 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,155 +1,90 @@
# CodeSmile AssetDatabase

It's Unity's AssetDatabase except now it's clean, concise, consistent and powerful.
It's Unity's age-old AssetDatabase - in clean code form! It will make you smile. :)

## Examples
## Who needs this?

My name is CodeSmile for a reason. Read on. :)
I spent a great deal of time to make AssetDatabase tasks dead simple for a layperson or just anyone who doesn't want to be bothered with how all of this works and what all the edge-cases and side-effects might be.

### Load or create asset
## But .. why?

A common use case: if an asset cannot be loaded, create it. That only slightly complicates things with the AssetDatabase:
Unload your mind. Put yourself at ease.

```
public static LevelData GetLevelDataAsset(int level)
{
string assetPath = "Assets/DataStore/Level{level}/LevelData.asset";
For every task there is a single call and you are DONE!

var levelData = AssetDatabase.LoadAssetAtPath<LevelData>(assetPath);
if (levelData == null)
{
// Load returns null does *NOT* mean the file doesn't exist!
// A user may have 'Auto Refresh' disabled => the asset may not have been imported
AssetDatabase.ImportAsset(assetPath);
// try loading it again now:
levelData = AssetDatabase.LoadAssetAtPath<LevelData>(assetPath);
}
The structure and naming is intended to be EXTREMELY simple to find your way around and then to call the appropriate method with fewer parameters, with names that speak for themselves.

if (levelData == null)
{
// create datastore folder if not exists
if (!AssetDatabase.IsValidFolder(@"Assets/DataStore"))
AssetDatabase.CreateFolder("Assets", "DataStore");
No longer do you need to wonder what a 'valid folder' might be. Or ponder what it means to 'force reserialize all assets'.

// create level folder if not exists
if (!AssetDatabase.IsValidFolder(@"Assets/DataStore/Level{level}"))
AssetDatabase.CreateFolder("Assets/DataStore", "Level{level}");
Let alone the ubiquitous 'SaveAllAssets' followed by 'Refresh' - are you calling that in your scripts? 99% chance you just put it there out of habit. You never gave it any thought. You have no idea what it really does. Not even that it can cripple editor performance. Or when calling it is **NOT** needed. (Hint: almost every time.)

levelData = CreateInstance<LevelData>();
AssetDatabase.CreateAsset(levelData, assetPath);
}
return levelData;
}
```
Or just being confused, once again, about whether you need to use `AssetDatabase.GetTextMetaFilePathFromAssetPath` or `AssetDatabase.GetAssetPathFromTextMetaFilePath`. Or the unholy trinity: `AssetPath.AssetPathToGUID`~`AssetPath.GUIDFromAssetPath`~`AssetPath.GUIDToAssetPath`.

Now what used to be over 20 lines of code is just this, with all edge-cases taken care of:
## I don't trust this ..

```
public static LevelData GetLevelDataAsset(int level)
{
string assetPath = "Assets/DataStore/Level{level}/LevelData.asset";
return Asset.LoadOrCreate<LevelData>(assetPath, () => CreateInstance<LevelData>());
}
```
The implementation is utmost CORRECT - there are no unnecessary, performance-degrading calls such as 'Refresh' and 'SaveAllAssets' littered throughout like you'll find in most editor scripts - unfortunately even in popular assets/libraries!

### Load an asset
It is also extensively unit TESTED to be correct.

Easy:
And I happen to love correct, clean code. Most developers move on when their code works. I cannot move on until I understand **why** my code works.

`Asset levelData = "Assets/Example/LevelData.asset";`
## What about support?

Wait .. wut?? :)
Yup, that's correct!
[The documentation](https://codesmile-0000011110110111.github.io/de.codesmile.assetdatabase/html/index.html) is more complete with more details and caveats mentioned than Unity's.

You have a GUID? In that case:
And if there's anything out of the ordinary, open an issue or [contact me](mailto:[email protected]). I also have a [Discord channel](https://discord.gg/JN3Jz8qkeV).

`Asset levelData = thisIsYourGuid;`
## Example Code Snippets

Or if you already have an instance loaded:
`Asset data = "Assets/Folder/Data.asset";` // Load an asset from its path

`Asset levelData = yourAssetObject;`
`data.ForceSave();` // mark asset as dirty and save it

The opposite also works, of course:
`data.AddSubAsset(subData);` // Add a sub-asset (implicitly saved)

`UnityEngine.Object obj = asset;`
`data.ActiveImporter = typeof(MyDataImporter);` // Change asset's importer

Not the right type? Just cast it:
`data.ExportPackage("I:/leveldata.unitypackage");` // Export as .unitypackage

`var levelData = (LevelData)asset;`
`var dataObject = data.MainObject;` // Get asset's UnityEngine.Object instance

The Asset instance provides you access to everything you might want to do with an asset. Save, copy, rename, delete, you name it.
`var levelData = data.GetAs<LevelData>();` // Get it as specific type (may be null)

You also get an asset's Labels, GUID, FileId, path, .meta path and so on. It's right where you expect it.
`var levelData = (LevelData)data;` // Cast to a type (may throw)

### Get an asset's path
`var obj = Asset.File.Create(str, "Assets/Folder/Data.asset");` // Create (overwrite) asset from string

Assuming you have an `asset` instance:
`var obj = Asset.File.CreateAsNew(bytes, "Assets/Folder/Data.asset");` // Create new asset from byte[]

`var assetPath = asset.AssetPath;`
`var asset = new Asset(bytes, "Assets/Folder/Data.asset");` // Same as above using Asset ctor

Oh right, you need the meta file path?
Well, you could inquire the AssetDatabase:
The 'create' methods above cover EVERY ASPECT and edge-cases:
- Error checking (null arguments, path validation, ..)
- Create non-existing folders of the path
- Generate a unique filename (unless overwriting)
- Write the string/bytes to file
- Import the new asset file
- Load the new asset file

`var metaPath = AssetDatabase.GetTextMetaFilePathFromAssetPath(assetPath);`
`var actualPath = asset.AssetPath;` // Filename might have changed, eg "Data (3).asset"

But I'd much rather have you do this:
`var subAssets = asset.SubAssets;` // Do I need to keep explaining these calls?

`var metaPath = asset.MetaPath;`
`var assetDupe = asset.Duplicate();` // Because you need a duplicate ..

Or if you just need to work with just paths:
`assetDupe.Delete();` // .. but then decided you don't.

```
Asset.Path assetPath = "Assets/Folder/LevelData.asset";
Asset.Path metaPath = assetPath.MetaPath;
```
`var newAsset = asset.SaveAsNew("Assets/Elsewhere/Daydah.asset");` // Now you want a copy?

The secret sauce behind Asset.Path is that you never EVER need to worry about the path being malformed, containing illegal characters, leaving leading/trailing slashes, or the paths not working on Mac OS or Linux due to backslashes.

Asset.Path also accepts absolute paths and makes them relative. But if the path is pointing outside the project, perhaps another project because you copy/pasted that code, it'll throw an exception rather than letting you modify assets in unrelated (!) projects. Yes, that can happen, with potentially devastating results!

Asset.Path also gives access to commonly used System.IO.Path functionality:

```
Path assetPath = "Assets/Folder/LevelData.asset";
Path assetDir = assetPath.DirectoryPath;
String assetFileName = assetPath.FileName;
String assetFileNameNoExt = assetPath.FileNameWithoutExtension;
String assetExtension = assetPath.Extension;
```

### Be nice, be concise!

Thus far you were forced to write rather verbose, convoluted code. This package will improve your work-smile balance. :)

---

`string[] paths = AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(bundleName, assetName);`

`string[] paths = Asset.Bundle.GetPaths(bundleName, assetName);`

---

`uint count = AssetDatabase.UnregisterCustomDependencyPrefixFilter(prefix);`

`uint count = Asset.Dependency.Remove(prefix);`

---

`var metaPath = AssetDatabase.GetTextMetaFilePathFromAssetPath(assetPath);`

`var metaPath = Asset.Path.ToMeta(assetPath);`

Mind you, those are the static methods. The instance methods are even more concise! For instance:

`var metaPath = asset.MetaPath;`
`newAsset.Trash();` // Okay. Either you're bored or excited to work with the AssetDatabase for the first time EVER. :)

`var msg = Asset.GetLastErrorMessage();` // A file operation failed? Show this!

## Documentation

- [Scripting API Reference](https://codesmile-0000011110110111.github.io/de.codesmile.editor.assetdatabase/html/index.html)
- [Scripting API Reference](https://codesmile-0000011110110111.github.io/de.codesmile.assetdatabase/html/index.html)
- [Transition Guide](https://docs.google.com/spreadsheets/d/134BEPXTx3z80snNAF3Gafgq3j5kEhmFzFBKT_z1s6Rw/edit?usp=sharing) (AssetDatabase method mapping)
- [Changelog](https://github.com/CodeSmile-0000011110110111/de.codesmile.assetdatabase/blob/main/CHANGELOG.md)

## Requirements

Expand Down

0 comments on commit 7c7ff29

Please sign in to comment.