Search Unity

Creating a SpriteAtlas from code

Discussion in '2D' started by liortal, Jan 6, 2018.

  1. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Is there any simple way to create a SpriteAtlas asset from code?
    I know i could "hack" it by calling something like:
    Code (CSharp):
    1. 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.
     
  2. Johaness_Reuben

    Johaness_Reuben

    Joined:
    Jan 27, 2016
    Posts:
    253
    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.
     
  3. Foriero

    Foriero

    Joined:
    Jan 24, 2012
    Posts:
    584
    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.
     
  4. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    In the new 2018.2 beta there is a mention of these API's being added.
     
  5. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
  6. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    @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?
     
    Last edited: Apr 25, 2018
    Xarbrough likes this.
  7. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Helllllllo likes this.
  8. gfs

    gfs

    Joined:
    Jun 25, 2015
    Posts:
    4
    xiamisya, Alex_May and xinzhi like this.
  9. gfs

    gfs

    Joined:
    Jun 25, 2015
    Posts:
    4
    The api is not be exposed until unity 2018.2.x,so need get private code
     
    xinzhi likes this.
  10. tiancaiwrk

    tiancaiwrk

    Joined:
    Nov 23, 2017
    Posts:
    35
  11. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    is it available in 2018.2 ?
     
  12. CuriousPatient

    CuriousPatient

    Joined:
    Aug 12, 2014
    Posts:
    9
    Exactly, what's the progress?
     
  13. Ben-BearFish

    Ben-BearFish

    Joined:
    Sep 6, 2011
    Posts:
    1,204
    @Venkify Are there any updates or documentation on how to Create a SpriteAtlas through code in the editor?
     
  14. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    @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.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEditor;
    4. using UnityEngine;
    5. using UnityEngine.U2D;
    6. using UnityEditor.U2D;
    7.  
    8. public class SpriteAtlasConverter
    9. {
    10.     private static bool GetTagIfTexture(string Path, ref Dictionary<string, List<string>> dict)
    11.     {
    12.         Object[] data = AssetDatabase.LoadAllAssetsAtPath(Path);
    13.         foreach (Object o in data)
    14.         {
    15.             Texture2D s = o as Texture2D;
    16.             if (s != null)
    17.             {
    18.                 TextureImporter ti= AssetImporter.GetAtPath(Path) as TextureImporter;
    19.                 List<string> spritesWithTag;
    20.                 if (!dict.ContainsKey(Path))
    21.                 {
    22.                     spritesWithTag = new List<string>();
    23.                     dict[ti.spritePackingTag] = spritesWithTag;
    24.                 }
    25.                 else
    26.                 {
    27.                     spritesWithTag = dict[ti.spritePackingTag];
    28.                 }
    29.                 spritesWithTag.Add(Path);
    30.                 return true;
    31.             }
    32.         }
    33.         return false;
    34.     }
    35.  
    36.     private static Texture2D GetTexture(string Path)
    37.     {
    38.         Object[] data = AssetDatabase.LoadAllAssetsAtPath(Path);
    39.         foreach (Object o in data)
    40.         {
    41.             Texture2D s = (Texture2D)o;
    42.             if (s != null)
    43.                 return s;
    44.         }
    45.         return null;
    46.     }
    47.  
    48.     private static bool CacheSpriteAtlasSprites(string Path, ref SortedSet<string> SpritesInAtlas)
    49.     {
    50.         Object[] data = AssetDatabase.LoadAllAssetsAtPath(Path);
    51.         foreach (Object o in data)
    52.         {
    53.             SpriteAtlas sa = o as SpriteAtlas;
    54.             if (sa != null)
    55.             {
    56.                 Sprite[] sprites = new Sprite[sa.spriteCount];
    57.                 sa.GetSprites(sprites);
    58.  
    59.                 foreach (Sprite sprite in sprites)
    60.                 {
    61.                     SpritesInAtlas.Add(AssetDatabase.GetAssetPath(sprite));
    62.                 }
    63.             }
    64.             return true;
    65.         }
    66.         return false;
    67.     }
    68.  
    69.     [MenuItem("Assets/Create SpriteAtlas for selected Sprites.")]
    70.     public static void CreateAtlasForSelectedSprites()
    71.     {
    72.         SpriteAtlas sa = new SpriteAtlas();
    73.         AssetDatabase.CreateAsset(sa, "Assets/sample.spriteatlas");
    74.         foreach (var obj in Selection.objects)
    75.         {
    76.             Object o = obj as Sprite;
    77.             if (o != null)
    78.                 SpriteAtlasExtensions.Add(sa, new Object[] { o });
    79.         }
    80.         AssetDatabase.SaveAssets();
    81.     }
    82.  
    83.     [MenuItem("Assets/SpriteAtlas Migrate")]
    84.     public static void SpriteAtlasMigrator()
    85.     {
    86.         List<string> TexturesList = new List<string>();
    87.         List<string> SpriteAtlasList = new List<string>();
    88.         Dictionary<string, List<string>> spriteTagMap = new Dictionary<string, List<string>>();
    89.         SortedSet<string> SpritesInAtlas = new SortedSet<string>();
    90.  
    91.         foreach (string s in AssetDatabase.GetAllAssetPaths())
    92.         {
    93.             if (s.StartsWith("Packages") || s.StartsWith("ProjectSettings") || s.Contains("scene"))
    94.                 continue;
    95.             bool hasSprite = GetTagIfTexture(s, ref spriteTagMap);
    96.             if (hasSprite)
    97.             {
    98.                 TexturesList.Add(s);
    99.             }
    100.             else if (s.Contains("spriteatlas"))
    101.             {
    102.                 bool hasSpriteAtlas = CacheSpriteAtlasSprites(s, ref SpritesInAtlas);
    103.                 if (hasSpriteAtlas)
    104.                     SpriteAtlasList.Add(s);
    105.             }
    106.         }
    107.  
    108.         foreach (KeyValuePair<string, List<string>> tag in spriteTagMap)
    109.         {
    110.             bool found = SpriteAtlasList.Contains(tag.Key);
    111.             if (!found)
    112.             {
    113.                 string atlasPath = "Assets/" + tag.Key + ".spriteatlas";
    114.                 SpriteAtlas sa = new SpriteAtlas();
    115.                 AssetDatabase.CreateAsset(sa, atlasPath);
    116.                 sa.name = tag.Key;
    117.                 List<string> ss = tag.Value;
    118.                 foreach (string s in ss)
    119.                 {
    120.                     Object o = GetTexture(s);
    121.                     SpriteAtlasExtensions.Add(sa, new Object[] { o });
    122.                 }
    123.                 AssetDatabase.SaveAssets();
    124.             }
    125.         }
    126.  
    127.     }
    128. }
    129.  
     
  15. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    Thanks. We are considering SpriteAtlasImporter style workflow which would also make it cache-server friendly. Will post this thread once we have an update.
     
  16. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    @Venkify thanks for that sample :)
    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.
     
  17. ThisIsNotMyName123

    ThisIsNotMyName123

    Joined:
    May 10, 2022
    Posts:
    51
    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?
     
  18. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    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.
     
    Venkify likes this.
  19. robochase

    robochase

    Joined:
    Mar 1, 2014
    Posts:
    244
    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


     
    Last edited: Jan 27, 2024