Search Unity

Sprite Atlas Creating a SpriteAtlas from code

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

  1. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,234
    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

    Unity Technologies

    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:
    464
    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:
    411
    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:
    193
  6. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    411
    @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
  7. liortal

    liortal

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

    gfs

    Joined:
    Jun 25, 2015
    Posts:
    4
    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,234
    is it available in 2018.2 ?
     
  12. W_M_Reszke

    W_M_Reszke

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

    Ben-BearFish

    Joined:
    Sep 6, 2011
    Posts:
    1,175
    @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:
    193
    @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.  
     
    Ben-BearFish and Peter77 like this.
  15. Venkify

    Venkify

    Unity Technologies

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

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,234
    @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.