Create SpriteLibraryAsset by Scripts

Hello, I’m trying to create a SpriteLibraryAsset pipeline, since creating one by assigning sprites to each category is really time consuming. It would be a lot more easier if I can just create sprite library assets and add sprites into the category according to sprite name or folder name it belongs to.

Here’s what I tried:

public class SpriteLibraryAssetFactory : EditorWindow
{
    [MenuItem("Window/Example")]
    public static void ShowWindow()
    {
        GetWindow<SpriteLibraryAssetFactory>();
    }

    private void OnGUI()
    {
        if (GUILayout.Button("Create"))
        {
            var sprites = ResourcesFolderLoader.LoadAllSpritesInDefault();
           
            var asset = ScriptableObject.CreateInstance<SpriteLibraryAsset>();
            foreach (var sprite in sprites)
            {
                asset.AddCategoryLabel(sprite, "test", "test2");
            }
            AssetDatabase.CreateAsset(asset, "Assets/test.spriteLib");
            AssetDatabase.SaveAssets();
           
        }
    }

}

If I try to use AssetDatabase.CreateAsset(so, “Assets/test.asset”); to generate ScriptableObject, this is fine. And since SpriteLibraryAsset is derived from ScriptableObject, I suppose this should work. However, I encountered these error:

Question now is what is the correct way to create SpriteLibraryAsset by code. Any documentation, examples or explanations is appreciated! Thanks.

2 Likes

Did you get anywhere with this? I’m trying to get started on a similar project myself.

Currently we do not support creating or modifying a Sprite Library Asset during edit time. SpriteLibraryAsset.AddCategoryLabel is there so that you can add a Sprite into the SpriteLibraryAsset during runtime.

What are you trying to solve by creating a Sprite Library Asset at edit time? Could you describe how our current flow doesn’t work for you?

My current setup for a top down RPG has a sprite library asset used to have matching animations across a variety of animations. The player character sprites, the clothing/armor sprites, the weapon sprites, the npc sprites, all each have their own sprite library asset with identical categories and all use the same animator, so I only have to set up a type of animation once. I expect to have quite a few different character sprites and a much higher quantity of different npcs, enemies, different kinds of armor and clothes, etc, and while I can manually create a sprite library asset for each one with a preset, adding a couple dozen sprite slices to the correct slots in the library is time consuming and error prone. Ideally, I’d like to have an automated workflow such that I can drop a sprite image into a watched directory, and on import have it automatically sliced and create a new library (if one doesn’t exist) and add its sprites to the correct categories in the correct order, based on a consistent slicing layout and filename conventions. Then I can just create a new instance of a prefab and apply the different library to it and all the animations just work.

3 Likes

Thank you for walking me through how you work with your project and your needs in how to make it more convenient to iterate on. I’ll take this feedback with me when we are planning future work for the 2D Animation package.

1 Like

:slight_smile: me too …

@BaspooGameDev does your setup and requirements match that of adunster? or do you have some other setup/requirements we should know about?

I am also trying to use AddCategoryLabel to create, expand, and evolve libraries as new assets are imported. We are trying to use naming conventions to specify which library and category a sprite goes into so we can then use sprite swapping for different eyes, hairstyles, clothing, etc.

We are navigating a ton of complexity here. For instance, when a hairstyle changes I need to swap many sprites at once since I have several views of character, front, back, side, etc. And each hairstyle might have a foreground a background sprite with each view.

I keep encountering things within Unity that just seem to be developed in a way that doesn’t anticipate an iterative, prototyping mindset. If I can do it in the editor, I should be able to do it in code, right? I find it REALLY odd that this isn’t the case here.

2 Likes

I too have this issue, and wish there was a way to save the SpriteLibraryAsset created in the edit-time to my Assets folder.

I have many different player character sprites, which use the same animation. So in order not to drag and drop >100 individual sprites, I wanted to create a script that runs during edit-time, which creates the SpriteLibraryAsset for each character, however even if there was functionality to save it in edit-time, I can’t find the “Main Library” attribute, which would allow animations to be reused…

Would greatly appreciate if there was code support for saving the SpriteLibraryAsset in edit-time into Assets folder, as well as setting the “Main Library” attribute through code.

1 Like

Thank you @tonytopper and @dmitry_veshchikov , it is great to know your use cases so we can more easily evaluate it against other feature requests that come in to us. I’ll add these items to our list of suggestions for future improvements to the 2D feature set.

1 Like

This is also a feature that would make our lives easier. I am currently building a rig generator to streamline the rig creation process for our artists and designers and would like to automatically generate and fill the sprite libraries based on a regex match of sprite assets.

We’ve already modified the 2D animation package (the new warning about an invalid signature in packages should be able to be disabled) to add in things like skeleton inheritance (another feature that would be useful) and expose a lot of the useful internals to the normal editor.

The new asset mapped to .spriteLib is an internal SpriteLibrarySourceAsset, so in the meantime (if you’re willing to use reflection or modify the package) you can use that. The SpriteLibUpgrader has some code showing how to serialize a new SpriteLibrarySourceAsset.

So in the past few days I’ve been talking with my colleagues about the history and API changes of Sprite Library and its friends. I’ll write down some insights here to correct some of my previous statements and also to update our collective perception of the feature.

So in Unity we have two types of assets, “source assets” such as .psd, .png and fbx; and “runtime assets” such as Texture, Sprite and Mesh. When a source asset is imported into Unity, an importer associated with the file ending of the source asset takes over to read the content of the file and its meta file, and generates one or more runtime assets. For a .png it looks something like this:
A .png is added into a Unity project → Texture Importer reads the .png and its .meta file → A Texture asset (and sometimes Sprite assets) is generated.

In Unity 2021.1, we updated Sprite Library Asset to follow the same design. We introduced the .spriteLib as Sprite Library’s source asset, and kept the Sprite Library Asset as its runtime asset.

The benefit of this design mainly comes down to decoupling the source asset from the runtime asset. With this decoupling, we are able to:

  • Modify the data without touching the source asset data (e.g. apply compression and remove unnecessary data not needed in runtime).
  • Allowing different source assets to generate the same runtime assets (e.g. .png, .psd and .tga all have a Texture generated on import by the TextureImporter).
  • Allowing the same source assets to generate different runtime assets (e.g. a .psd can be processed by the TextureImporter or the PSD Importer, which will generate a different set of assets).

One common API structure these pipelines share is that Unity tends to not have an API in place to generate source assets, but has an API in place to generate runtime assets. E.g. a Texture can be generated by creating a new Texture2D object, a Mesh can be generated by creating a new Mesh object and a Sprite can be generated by using the Sprite.Create method. The same thing goes for a Sprite Library Asset. Here is some code to showcase how you could create a new Sprite Library Asset and save it on disk:

public static class SpriteLibCreator
{
    [MenuItem("Tools/Create Sprite Lib")]
    static void CreateLib()
    {
        const string spriteLibName = "MySpriteLib.asset";
           
        var spriteLib = ScriptableObject.CreateInstance<SpriteLibraryAsset>();
        spriteLib.AddCategoryLabel(null, "Cat", "Label");
           
        AssetDatabase.CreateAsset(spriteLib, "Assets/" + spriteLibName);
    }
}

You could also go more advanced, and create your own source asset with its unique file ending and create a custom importer for it. This would allow you to author Sprite Library data outside (or inside) of Unity and then generate it exactly how you would like it.

During my dive into this area, I saw that SpriteLibraryAsset.CreateAsset is internal. To follow the same design as the other pipelines, I believe we should make this a public method to ease the way a Sprite Library Asset can be generated.

Apart from this change, are there any other changes to the Sprite Library Asset API that you would like to see?

2 Likes

I understand the pattern you’re going for, but the difference between the SpriteLibrarySourceAsset’s and, say, a model or a PNG is that SpriteLibrarySourceAsset is unique to Unity. There are plenty of ways to create, edit, modify and import PNG’s, or even models, in code. Simply moving them into the project directory and updating the asset database lets you load/reload them. Effectively, modifying these types of source assets is not ‘internal’ functionality. Unity does not need to provide an API for these files because plenty of API’s already exist.

To only way to achieve a similar result with the SpriteLibrarySourceAsset (without breaking open the internals as we did) is to save a new file based on an existing template. The example code you gave creates an asset that prompts the user to “Upgrade” to the new type of SpriteLibrary when it is clicked on. This experience is not ideal.

SpriteLibrarySourceAsset is itself a ScriptableObject, ScriptableObjects are most easily edited through Unity’s codebase, so keeping it internal with no API to create or edit instances of this source asset is artificially limiting in ways that the other types of source assets are not.

If what you’re ultimately trying to do is totally prevent users from creating SpriteLibrarySourceAsset’s at runtime, then a simple editor-only API with SpriteLibraryAsset that lets users stage and then “Commit” changes to it would work. This is essentially what your editor code already does when upgrading assets.

Thank you for your feedback.

Yes, this text should be updated so that it doesn’t seem like a user is doing something wrong by having these .asset files in their project.

If we zero in on what you are doing with the sprite libraries in edit time, @ScionOfDesign , apart from the label being displayed on a Sprite Library Asset, what issues do you have with the path I outlined in my post?

Functionality-wise? Not much, other than the fact that the user may give up whatever performance benefits there are of using SpriteLibrarySourceAsset instead.

But at its core it seems very arbitrary and inconsistent that there needs to be a difference between user-generated Sprite Libraries from code and Libraries generated with a click of a button in the Unity Editor. Maybe I just have not encountered them yet, but I am unaware of any other asset in Unity behaving the same way. (Having the source asset be a ScriptableObject and preventing the user from creating said ScriptableObject).

If we ever decided to release an asset store package of the powerful rig generator we are building, this would just be another annoying issue to work-around using reflection or some other unnecessarily hacky method.

I might not have explained the relationship between the Sprite Library Source Asset (.spriteLib) and Sprite Library Asset clearly in my previous post.

The Sprite Library Source Asset class is the code representation of the .spriteLib file. Once a .spriteLib file is imported into a Unity project, the Sprite Library Source Asset Importer opens the file (and its meta file), reads its content and generates a Sprite Library Asset. The Sprite Library Asset is then used in runtime for any Sprite Swap operation the project is performing. This means that the runtime performance is identical if you start off with a .spriteLib file (containing Sprite Library Source Asset data) or an .asset file (containing Sprite Library Asset data).

Hopefully this clears things up a bit more.
Thanks again for your feedback!

That makes the proposed solution better, but also begs the question of why SpriteLibrarySourceAsset’s were even needed in the first place?
If SpriteLibrarySourceAsset led to increased performance and functionality, then the user should have the ability to create them. In my opinion, this includes the behavior of not committing any changes immediately. (Speaking of, can you even permanently modify a SpriteLibrarySourceAsset-backed SpriteLibrary with the existing API? Or will no changes ever be written to disk?).
If SpriteLibrarySourceAsset doesn’t lead to increased performance and functionality, then why does it exist except to cause confusion when users try to create a SpriteLibrary asset?

Is it simply to conform to a design pattern used within Unity that may lead to changes in some future update? Is it to prevent unintended modification of the serialized asset?

I can definitely see there being a lot of user confusion between libraries saved as .asset and libraries saved as .spriteLib. Especially if you are working on a large team with some artists creating libraries from the editor and others using a custom tool to generate them.

There are multiple reasons for this design choice, some of which I highlighted in my previous post:

Another reason is of course to conform with Unity’s import pipeline. This makes current and future work easier to perform.

Not sure what you mean with a SpriteLibrarySourceAsset-backed SpriteLibrary. Again, if a change is made to the Sprite Library Source Asset and a reimport of the file is trigged, the Sprite Library Source Asset Importer will generate a new Sprite Library Asset based on the content of the Sprite Library Source Asset (much like if you make a change inside the Sprite Editor and press Apply, the TextureImporter will generate updated Sprite assets). The APIs to modify the Sprite Library Source Asset is currently internal.

All in all, it is never our intention to create confusing APIs, we are constantly trying to make sure we have simple yet extensible APIs for our feature sets. For this feature set, it does indeed seem like there is quite some confusion. Moving forward, what would help us is to understand your real use cases, and where the current feature set/API offerings doesn’t work/is confusing. This way we can gather insights from multiple sources and make sure we provide the best solution for most users.

If you save a SpriteLibrary like you showcased in your SpriteLibCreator code, it this is what it looks like in the editor:
7908838--1008337--upload_2022-2-18_16-27-9.png
Notice how it is simply a “Sprite Library Asset”. It is not a “SpriteLibrarySource Asset imported as a SpriteLibraryAsset” like what happens when you create a new SpriteLibrary through the menu, as seen here:
7908838--1008340--upload_2022-2-18_16-29-33.png

The regular “SpriteLibraryAsset” does not have the “Main Library/Override” functionality of the SpriteLibrarySourceAssets either.

We want to be able to create a tool that generates sprite library assets and fills them up with sprites at editor time. We want the Sprite Libraries created through our code to be no different than the sprite libraries created by clicking the menu in the editor. We want to be able to do this without breaking into the Animation package and forcing it to give us access to its internals.

Thanks for the feedback, @ScionOfDesign . I’ll add this to our internal list of feedback for the 2d feature sets.