Search code examples
unity-game-engineunity3d-editor

Sprites added to Unity SpriteLibraryAsset programmatically doesn't appear in asset


I am creating a Unity Editor script which takes a texture, slices it (this part works) and adds these sprites to a selected SpriteLibraryAsset:

        foreach(var currentGroup in selectedDefinitionFile.Groups)
        {
            for (int i = 1; i <= currentGroup.Item2; i++) {
                var rects = dataProvider.GetSpriteRects();
                var targetName = String.Format("{0}-{1}", currentGroup.Item1, i);

                var sprite = (Sprite)allSprites.Where(x => x.name == targetName).First();
                spriteLibraryToPopulate.AddCategoryLabel(sprite, currentGroup.Item1, i.ToString());
            }
        }
        // None of these do anything
        spriteLibraryToPopulate.SetDirty();
        EditorUtility.SetDirty(spriteLibraryToPopulate);
        AssetDatabase.SaveAssets();
        AssetDatabase.SaveAssetIfDirty(new UnityEditor.GUID(AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(spriteLibraryToPopulate))));
        AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(spriteLibraryToPopulate));
        AssetDatabase.Refresh();
        UnityEditor.SceneManagement.EditorSceneManager.MarkAllScenesDirty();

If I run this script multiple times and stop on a breakpoint, I can see in Visual Studio that the sprites are being added as expected to the in-memory object. However, when I examine the Sprite Library asset, both in the editor and in the asset file using Notepad++, none of them appear.

Reimporting via the menu in the editor does nothing as well.

Investigation with a debugger shows that internally, Unity uses the class SpriteLibrarySourceAsset when importing a .spriteLib asset and creates the SpriteLibraryAsset I have access to in my scripts. I haven't been able to find how to go the other way.


Solution

  • I finally was able to achieve what I intended using reflection to call some internal Unity APIs.

    // Perform your modification to the sprite library before this
            // The logic to transform the SpriteLibraryAsset to the internal representation works on the selected objects.
            Selection.objects = new UnityEngine.Object[]
            {
                spriteLibraryToPopulate
            };
            var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(spriteLibraryToPopulate)) as SpriteLibrarySourceAssetImporter;
            var saveMethod = typeof(SpriteLibrarySourceAssetImporter).GetMethod("ConvertToSourceAsset", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
            saveMethod.Invoke(null, null);
    
            var rootPath = Path.Join(AssetDatabase.GetAssetPath(spriteLibraryToPopulate), "..");
    
            // The save logic creates a differently named instance of the asset, so we copy the data from our newly created one into the original
            File.Copy(Path.Join(rootPath, string.Format("{0} 1.spriteLib", spriteLibraryToPopulate.name)),
                Path.Join(rootPath, string.Format("{0}.spriteLib", spriteLibraryToPopulate.name)), true);
    
            // Get rid of the extra asset and its associated meta file.
            File.Delete(Path.Join(rootPath, string.Format("{0} 1.spriteLib", spriteLibraryToPopulate.name)));
            File.Delete(Path.Join(rootPath, string.Format("{0} 1.spriteLib.meta", spriteLibraryToPopulate.name)));
    

    At the end, the library should be selected in the Unity Editor. If you try and click away, it will be marked dirty and Unity will prompt you to save or revert; it appears to me that both have the same result.