A game about forced loneliness, made by TACStudios
at master 309 lines 13 kB view raw
1using System; 2using System.Collections.Generic; 3using System.IO; 4using UnityEngine; 5using UnityEngine.U2D.Animation; 6using Object = UnityEngine.Object; 7 8namespace UnityEditor.U2D.Animation 9{ 10 /// <summary> 11 /// Represents a Sprite Library's label. 12 /// </summary> 13 [Serializable] 14 public class SpriteLibraryLabel : ISpriteLibraryLabel 15 { 16 /// <summary> 17 /// Label's name. 18 /// </summary> 19 public string name => m_Name; 20 21 /// <summary> 22 /// Sprite associated with the label. 23 /// </summary> 24 public Sprite sprite => m_Sprite; 25 26 [SerializeField] 27 string m_Name; 28 29 [SerializeField] 30 Sprite m_Sprite; 31 32 /// <summary> 33 /// Constructs a new label. 34 /// </summary> 35 /// <param name="labelName">Label's name.</param> 36 /// <param name="labelSprite">Label's Sprite.</param> 37 public SpriteLibraryLabel(string labelName, Sprite labelSprite) 38 { 39 m_Name = labelName; 40 m_Sprite = labelSprite; 41 } 42 } 43 44 /// <summary> 45 /// Represents a Sprite Library's category. 46 /// </summary> 47 [Serializable] 48 public class SpriteLibraryCategory : ISpriteLibraryCategory 49 { 50 /// <summary> 51 /// Category's name. 52 /// </summary> 53 public string name => m_Name; 54 55 /// <summary> 56 /// List of labels in category. 57 /// </summary> 58 public IEnumerable<ISpriteLibraryLabel> labels => m_Labels; 59 60 [SerializeField] 61 List<SpriteLibraryLabel> m_Labels; 62 63 [SerializeField] 64 string m_Name; 65 66 /// <summary> 67 /// Constructs a new category. 68 /// </summary> 69 /// <param name="categoryName">Category's name.</param> 70 /// <param name="categoryLabels">Collection of labels in a category.</param> 71 public SpriteLibraryCategory(string categoryName, IEnumerable<SpriteLibraryLabel> categoryLabels) 72 { 73 m_Name = categoryName; 74 m_Labels = new List<SpriteLibraryLabel>(categoryLabels); 75 } 76 } 77 78 /// <summary> 79 /// Class used for creating new Sprite Library Source Assets. 80 /// </summary> 81 public static class SpriteLibrarySourceAssetFactory 82 { 83 /// <summary> 84 /// Sprite Library Source Asset's extension. 85 /// </summary> 86 public const string extension = SpriteLibrarySourceAsset.extension; 87 88 /// <summary> 89 /// Creates a new Sprite Library Source Asset at a given path. 90 /// </summary> 91 /// <param name="path">Save path. Must be within the Assets folder.</param> 92 /// <param name="categories">Collection of categories in the library.</param> 93 /// <param name="mainLibraryPath">A path to the main library. Null if there is no main library.</param> 94 /// <returns>A relative path to the Project with correct extension.</returns> 95 /// <exception cref="InvalidOperationException">Throws when the save path is invalid/</exception> 96 public static string Create(string path, IEnumerable<ISpriteLibraryCategory> categories, string mainLibraryPath = null) 97 { 98 if (string.IsNullOrEmpty(path)) 99 throw new InvalidOperationException("Save path cannot be null or empty."); 100 101 var relativePath = GetRelativePath(path); 102 if (string.IsNullOrEmpty(relativePath)) 103 throw new InvalidOperationException($"{nameof(LoadSpriteLibrarySourceAsset)} can only be saved in the Assets folder."); 104 105 relativePath = Path.ChangeExtension(relativePath, extension); 106 107 SpriteLibraryAsset mainLibrary = null; 108 if (!string.IsNullOrEmpty(mainLibraryPath)) 109 { 110 mainLibrary = AssetDatabase.LoadAssetAtPath<SpriteLibraryAsset>(mainLibraryPath); 111 if (mainLibrary == null) 112 throw new InvalidOperationException($"No {nameof(SpriteLibraryAsset)} found at path: '{mainLibraryPath}'"); 113 } 114 115 var asset = ScriptableObject.CreateInstance<SpriteLibrarySourceAsset>(); 116 var categoryList = new List<SpriteLibCategoryOverride>(); 117 if (categories != null) 118 { 119 foreach (var category in categories) 120 { 121 var spriteLibCategory = new SpriteLibCategoryOverride 122 { 123 name = category.name, 124 overrideEntries = new List<SpriteCategoryEntryOverride>() 125 }; 126 foreach (var label in category.labels) 127 { 128 var spriteCategoryEntryOverride = new SpriteCategoryEntryOverride 129 { 130 name = label.name, 131 spriteOverride = label.sprite 132 }; 133 spriteLibCategory.overrideEntries.Add(spriteCategoryEntryOverride); 134 } 135 136 categoryList.Add(spriteLibCategory); 137 } 138 } 139 140 if (mainLibrary != null) 141 { 142 asset.SetPrimaryLibraryGUID(AssetDatabase.GUIDFromAssetPath(mainLibraryPath).ToString()); 143 144 var newCategories = mainLibrary.categories ?? new List<SpriteLibCategory>(); 145 146 var existingCategories = new List<SpriteLibCategoryOverride>(categoryList); 147 categoryList.Clear(); 148 149 // populate new primary 150 foreach (var newCategory in newCategories) 151 { 152 var labels = new List<SpriteCategoryEntryOverride>(); 153 SpriteLibCategoryOverride existingCategory = null; 154 for (var i = 0; i < existingCategories.Count; i++) 155 { 156 var category = existingCategories[i]; 157 if (category.name == newCategory.name) 158 { 159 existingCategory = category; 160 existingCategory.fromMain = true; 161 existingCategories.RemoveAt(i); 162 break; 163 } 164 } 165 166 var newEntries = newCategory.categoryList; 167 foreach (var newEntry in newEntries) 168 { 169 var sprite = newEntry.sprite; 170 171 labels.Add(new SpriteCategoryEntryOverride 172 { 173 name = newEntry.name, 174 sprite = sprite, 175 spriteOverride = sprite, 176 fromMain = true 177 }); 178 } 179 180 var overrideCount = 0; 181 if (existingCategory != null) 182 { 183 foreach (var existingLabel in existingCategory.overrideEntries) 184 { 185 var foundLabel = false; 186 foreach (var newLabel in labels) 187 { 188 if (existingLabel.name == newLabel.name) 189 { 190 if (newLabel.spriteOverride != existingLabel.spriteOverride) 191 { 192 newLabel.spriteOverride = existingLabel.spriteOverride; 193 overrideCount++; 194 } 195 196 foundLabel = true; 197 break; 198 } 199 } 200 201 if (!foundLabel) 202 { 203 overrideCount++; 204 labels.Add(new SpriteCategoryEntryOverride 205 { 206 name = existingLabel.name, 207 sprite = existingLabel.sprite, 208 spriteOverride = existingLabel.spriteOverride, 209 fromMain = false 210 }); 211 } 212 } 213 } 214 215 categoryList.Add(new SpriteLibCategoryOverride 216 { 217 name = newCategory.name, 218 overrideEntries = labels, 219 fromMain = true, 220 entryOverrideCount = overrideCount 221 }); 222 } 223 224 foreach (var existingCategory in existingCategories) 225 { 226 var keepCategory = false; 227 if (existingCategory.fromMain) 228 { 229 for (var i = existingCategory.overrideEntries.Count; i-- > 0;) 230 { 231 var entry = existingCategory.overrideEntries[i]; 232 if (!entry.fromMain || entry.sprite != entry.spriteOverride) 233 { 234 entry.fromMain = false; 235 entry.sprite = entry.spriteOverride; 236 keepCategory = true; 237 } 238 else 239 existingCategory.overrideEntries.RemoveAt(i); 240 } 241 } 242 243 if (!existingCategory.fromMain || keepCategory) 244 { 245 existingCategory.fromMain = false; 246 existingCategory.entryOverrideCount = 0; 247 categoryList.Add(existingCategory); 248 } 249 } 250 } 251 252 asset.SetLibrary(categoryList); 253 254 SpriteLibrarySourceAssetImporter.SaveSpriteLibrarySourceAsset(asset, relativePath); 255 Object.DestroyImmediate(asset); 256 return relativePath; 257 } 258 259 /// <summary> 260 /// Creates a new Sprite Library Source Asset at a given path. 261 /// </summary> 262 /// <param name="path">Save path. Must be within the Assets folder.</param> 263 /// <param name="spriteLibraryAsset">Sprite Library Asset to be saved.</param> 264 /// <param name="mainLibraryPath">A path to the main library. Null if there is no main library.</param> 265 /// <returns>A relative path to the Project with correct extension.</returns> 266 /// <exception cref="InvalidOperationException">Throws when the save path is invalid/</exception> 267 public static string Create(string path, SpriteLibraryAsset spriteLibraryAsset, string mainLibraryPath = null) 268 { 269 return Create(path, spriteLibraryAsset.categories, mainLibraryPath); 270 } 271 272 /// <summary> 273 /// Creates a new Sprite Library Source Asset at a given path. 274 /// </summary> 275 /// <param name="spriteLibraryAsset">Sprite Library Asset to be saved.</param> 276 /// <param name="path">Save path. Must be within the Assets folder.</param> 277 /// <param name="mainLibraryPath">A path to the main library. Null if there is no main library.</param> 278 /// <returns>A relative path to the Project with correct extension.</returns> 279 /// <exception cref="InvalidOperationException">Throws when the save path is invalid/</exception> 280 public static string SaveAsSourceAsset(this SpriteLibraryAsset spriteLibraryAsset, string path, string mainLibraryPath = null) 281 { 282 return Create(path, spriteLibraryAsset, mainLibraryPath); 283 } 284 285 internal static SpriteLibrarySourceAsset LoadSpriteLibrarySourceAsset(string path) 286 { 287 var loadedObjects = UnityEditorInternal.InternalEditorUtility.LoadSerializedFileAndForget(path); 288 foreach (var obj in loadedObjects) 289 { 290 if (obj is SpriteLibrarySourceAsset asset) 291 return asset; 292 } 293 294 return null; 295 } 296 297 static string GetRelativePath(string path) 298 { 299 if (string.IsNullOrWhiteSpace(path)) 300 return null; 301 302 if (!path.StartsWith("Assets/") && !path.StartsWith(Application.dataPath)) 303 return null; 304 305 var pathStartIndex = path.IndexOf("Assets"); 306 return pathStartIndex == -1 ? null : path.Substring(pathStartIndex); 307 } 308 } 309}