Creating a SpriteAtlas from code

Is there any simple way to create a SpriteAtlas asset from code?
I know i could “hack” it by calling something like:

EditorApplication.ExecuteMenuItem("Assets/Create/Sprite Atlas");

What i’d like to do, is create an automatic “migration” from the older sprite packer atlases to the new system.
The way it would work - for each existing (legacy) sprite atlas, it will create a new sprite atlas asset and migrate all textures into it automatically.

Is there anything that does this automatically right now? if not, i’d like to create such a system. i believe the only part that’s missing is the ability to create a new sprite atlas asset.

This is not available yet. We will be exposing some of the API related to this at a later date. I can’t provide when at this time.

Hi Johaness, well can you at least when clicking that plus button for adding new sprite add there menu item “Add Selected Sprites”. Which would add all selected sprites from project tab? It would be really helpful until we have full editor api for adding sprites from scripts which we desperately need.

Thank you, Marek.

In the new 2018.2 beta there is a mention of these API’s being added.

Please check here : Unity - Scripting API: SpriteAtlasExtensions
for more info on SpriteAtlas API additions (2018.2)

@Venkify Do you know why this approach was choosen, instead of having a SpriteAtlasImporter and the appropiate callbacks? It seems like this is just another way to do the same thing, which is rather inconsistent.

The same for creating one, why cant we just use AssetDatabase.CreateAsset like any other asset?

1 Like

Is there any API that will allow me to achieve what i wanted to do in the original question ?

1 Like

Hi ,you can read my code
GitHub - hanbim520/Unity2017AutoCreateSpriteAtlas i finished what your wanted

3 Likes

The api is not be exposed until unity 2018.2.x,so need get private code

1 Like

So great !! thanks

is it available in 2018.2 ?

Exactly, what’s the progress?

@Venkify Are there any updates or documentation on how to Create a SpriteAtlas through code in the editor?

@W_M_Reszke @Ben-BearFish @liortal
Here is a simple sample to demonstrate API to create SpriteAtlas via code.
MenuItem: [Assets/SpriteAtlas Migrate] creates a spriteAtlas for each tag for all sprites that has the tag.
MenuItem" [Assets/Create SpriteAtlas for selected Sprites.] creates a spriteatlas for selected sprites.

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.U2D;
using UnityEditor.U2D;

public class SpriteAtlasConverter
{
    private static bool GetTagIfTexture(string Path, ref Dictionary<string, List<string>> dict)
    {
        Object[] data = AssetDatabase.LoadAllAssetsAtPath(Path);
        foreach (Object o in data)
        {
            Texture2D s = o as Texture2D;
            if (s != null)
            {
                TextureImporter ti= AssetImporter.GetAtPath(Path) as TextureImporter;
                List<string> spritesWithTag;
                if (!dict.ContainsKey(Path))
                {
                    spritesWithTag = new List<string>();
                    dict[ti.spritePackingTag] = spritesWithTag;
                }
                else
                {
                    spritesWithTag = dict[ti.spritePackingTag];
                }
                spritesWithTag.Add(Path);
                return true;
            }
        }
        return false;
    }

    private static Texture2D GetTexture(string Path)
    {
        Object[] data = AssetDatabase.LoadAllAssetsAtPath(Path);
        foreach (Object o in data)
        {
            Texture2D s = (Texture2D)o;
            if (s != null)
                return s;
        }
        return null;
    }

    private static bool CacheSpriteAtlasSprites(string Path, ref SortedSet<string> SpritesInAtlas)
    {
        Object[] data = AssetDatabase.LoadAllAssetsAtPath(Path);
        foreach (Object o in data)
        {
            SpriteAtlas sa = o as SpriteAtlas;
            if (sa != null)
            {
                Sprite[] sprites = new Sprite[sa.spriteCount];
                sa.GetSprites(sprites);

                foreach (Sprite sprite in sprites)
                {
                    SpritesInAtlas.Add(AssetDatabase.GetAssetPath(sprite));
                }
            }
            return true;
        }
        return false;
    }

    [MenuItem("Assets/Create SpriteAtlas for selected Sprites.")]
    public static void CreateAtlasForSelectedSprites()
    {
        SpriteAtlas sa = new SpriteAtlas();
        AssetDatabase.CreateAsset(sa, "Assets/sample.spriteatlas");
        foreach (var obj in Selection.objects)
        {
            Object o = obj as Sprite;
            if (o != null)
                SpriteAtlasExtensions.Add(sa, new Object[] { o });
        }
        AssetDatabase.SaveAssets();
    }

    [MenuItem("Assets/SpriteAtlas Migrate")]
    public static void SpriteAtlasMigrator()
    {
        List<string> TexturesList = new List<string>();
        List<string> SpriteAtlasList = new List<string>();
        Dictionary<string, List<string>> spriteTagMap = new Dictionary<string, List<string>>();
        SortedSet<string> SpritesInAtlas = new SortedSet<string>();

        foreach (string s in AssetDatabase.GetAllAssetPaths())
        {
            if (s.StartsWith("Packages") || s.StartsWith("ProjectSettings") || s.Contains("scene"))
                continue;
            bool hasSprite = GetTagIfTexture(s, ref spriteTagMap);
            if (hasSprite)
            {
                TexturesList.Add(s);
            }
            else if (s.Contains("spriteatlas"))
            {
                bool hasSpriteAtlas = CacheSpriteAtlasSprites(s, ref SpritesInAtlas);
                if (hasSpriteAtlas)
                    SpriteAtlasList.Add(s);
            }
        }

        foreach (KeyValuePair<string, List<string>> tag in spriteTagMap)
        {
            bool found = SpriteAtlasList.Contains(tag.Key);
            if (!found)
            {
                string atlasPath = "Assets/" + tag.Key + ".spriteatlas";
                SpriteAtlas sa = new SpriteAtlas();
                AssetDatabase.CreateAsset(sa, atlasPath);
                sa.name = tag.Key;
                List<string> ss = tag.Value;
                foreach (string s in ss)
                {
                    Object o = GetTexture(s);
                    SpriteAtlasExtensions.Add(sa, new Object[] { o });
                }
                AssetDatabase.SaveAssets();
            }
        }

    }
}
9 Likes

Thanks. We are considering SpriteAtlasImporter style workflow which would also make it cache-server friendly. Will post this thread once we have an update.

3 Likes

@Venkify thanks for that sample :slight_smile:
I actually had a migration script for a while now, that i wanted to share with the community.

One thing i did not yet complete is mapping textures by their settings to the corresponding SpriteAtlas.

Previously (using Sprite Packer), you could assign any texture with a tag, and Unity would create different atlases grouped by their settings (e.g: texture format, etc).

In the newer SpriteAtlas, you simply assign textures to the new asset and configure the settings you want there.
When migrating, your code does not check for these settings and will basically group all textures together under the same SpriteAtlas asset. i am not sure this is 100% equivalent of the previous behaviour.

Also, there are other flags (e.g: include in build) that are not set.

Lastly, my example uses AssetDatabase.StartAssetEditing() … AssetDatabase.StopAssetEditing() for speeding up the process.

Well, is it happening? It is so weird that you chose to come up with a completely different API, especially when it doesn’t play nicely with the cache server.

When will the packable API be added to the atlas importer, instead of relying on the helper class?

SpriteAtlas V2 supposedly supports the Accelerator.

The danger with necroing threads is that you end up reading about old things that were bad, and then dogpiling an old complaint when fixes has been made in the four years between the last post and your post.

1 Like

this thread is now the number 1 google hit when trying to figure out how to make a sprite atlas via an editor script.

what is the right way to do this in 2024? for my purposes, I want to create a sprite atlas from a folder of sprites via editor script.

ideally so I end up with something where the folder is listed in the ‘Objects for packing’ list in the inspector for the atlas I don’t need to keep manually updating the atlas

I wrote the Editor generated code for my team, which works well.

Forgive me for not pasting the code directly. The more detailed method is:

  1. Find a spriteatlasv2 and the corresponding meta file.
    You can simply get them through the menu Create->2D->Sprite Atlas.

  2. Cache them.
    Open 2 files using a text editor, copy the content into your generated script, and cache the string as a template

  3. Modify the template string and save the two templates to the path you want.
    Through System.IO.File or other methods you are familiar with.
    About modification, what we need to modify:
    — 3.1 In the template of spriteatlasv2, you can find

    m_ImporterData:
      packables:
        - {fileID: 102900000, guid: xxxxxxx, type: 3}
        - {fileID: 2800000, guid: xxxxxxx, type: 3}

“- {fileID: 102900000, guid: xxxxx, type: 3}” defines which folders are included.
“- {fileID: 2800000, guid: xxxxx, type: 3}” defines which sprites are included. (You can try to modify the import of different content and observe the changes in .spriteatlasv2 to learn the meaning of this field.)
Here you need to modify the guid of each item, and the guid value needs to be obtained through

AssetDatabase.AssetPathToGUID()

— 3.2 In the spriteatlasv2.meta template, you can find a lot of settings.
------- First, you need to modify the guid. GUID.Generate() is what you need.
------- I believe you can understand the corresponding meaning and how to set these simple 0 or 1 or specific values.

  1. Import them.
    After completing the modification of the template string and writing and saving the file, you need to import the newly created asset using
File.WriteAllText()
AssetDatabase.ImportAsset()
  1. Further.
    you can use
target = AssetDatabase.LoadMainAssetAtPath
EditorUtility.FocusProjectWindow
EditorGUIUtility.PingObject(target)
Selection.activeObject = target

to quickly locate the atlas you just created and check its properties in the inspector

If your team use spriteAtlasv2 in 3D(mesh render) and 2D (canvas render), I think you need different settings for them. Just make different template in Step 2, and chose it right in your generate code script