From 7a7809ab6d8a34b700fdb303ea36f90034fa7051 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 13:27:53 +0200 Subject: [PATCH 01/23] Added an extension for getting all assignable classes. --- Scripts/Runtime/Extensions/TypeExtensions.cs | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Scripts/Runtime/Extensions/TypeExtensions.cs b/Scripts/Runtime/Extensions/TypeExtensions.cs index 4c66794..42ee1cb 100644 --- a/Scripts/Runtime/Extensions/TypeExtensions.cs +++ b/Scripts/Runtime/Extensions/TypeExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace BrunoMikoski.ScriptableObjectCollections @@ -49,5 +50,26 @@ public static Type GetBaseGenericType(this Type type) return null; } + + public static Type[] GetAllAssignableClasses( + this Type type, bool includeAbstract = true, bool includeItself = false) + { + IEnumerable allTypes = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(assembly => assembly.GetTypes()); + + if (type.IsGenericType) + { + if (type.IsInterface) + { + return allTypes.Where(t => t.GetInterfaces().Any( + i => i.IsGenericType && i.GetGenericTypeDefinition() == type)).ToArray(); + } + return allTypes.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == type).ToArray(); + } + + return allTypes.Where( + t => type.IsAssignableFrom(t) && (t != type || includeItself) && (includeAbstract || !t.IsAbstract)) + .ToArray(); + } } } From 846e9dcb6435aa9117c61799162d16e101de4b79 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 13:29:41 +0200 Subject: [PATCH 02/23] Added a way to get or add an item without knowing the collection's type. --- Scripts/Runtime/Core/ScriptableObjectCollection.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Scripts/Runtime/Core/ScriptableObjectCollection.cs b/Scripts/Runtime/Core/ScriptableObjectCollection.cs index 5f67850..daf5666 100644 --- a/Scripts/Runtime/Core/ScriptableObjectCollection.cs +++ b/Scripts/Runtime/Core/ScriptableObjectCollection.cs @@ -183,6 +183,15 @@ public ScriptableObject AddNew(Type itemType, string assetName = "") return newItem; } + + public ISOCItem GetOrAddNewBaseItem(string targetName) + { + ISOCItem item = Items.FirstOrDefault(o => o.name.Equals(targetName, StringComparison.Ordinal)) as ISOCItem; + if (item != null) + return item; + + return AddNew(GetItemType(), targetName) as ISOCItem; + } #endif public virtual Type GetItemType() From b99fb09c4b726975394d30eedba6808dc4fad567 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 13:30:03 +0200 Subject: [PATCH 03/23] Added a non-generic way to get a collection of a type. --- Scripts/Runtime/Core/CollectionsRegistry.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Scripts/Runtime/Core/CollectionsRegistry.cs b/Scripts/Runtime/Core/CollectionsRegistry.cs index 4a9629c..24c6983 100644 --- a/Scripts/Runtime/Core/CollectionsRegistry.cs +++ b/Scripts/Runtime/Core/CollectionsRegistry.cs @@ -223,14 +223,14 @@ public ScriptableObjectCollection GetCollectionByGUID(LongGuid guid) return null; } - public bool TryGetCollectionOfType(out T resultCollection) where T: ScriptableObjectCollection + public bool TryGetCollectionOfType(Type type, out ScriptableObjectCollection resultCollection) { for (int i = 0; i < collections.Count; i++) { ScriptableObjectCollection scriptableObjectCollection = collections[i]; - if (scriptableObjectCollection is T collectionT) + if (scriptableObjectCollection.GetType() == type) { - resultCollection = collectionT; + resultCollection = scriptableObjectCollection; return true; } } @@ -239,6 +239,13 @@ public bool TryGetCollectionOfType(out T resultCollection) where T: Scriptabl return false; } + public bool TryGetCollectionOfType(out T resultCollection) where T: ScriptableObjectCollection + { + bool didFind = TryGetCollectionOfType(typeof(T), out ScriptableObjectCollection baseCollection); + resultCollection = baseCollection as T; + return didFind; + } + public bool TryGetCollectionFromItemType(Type targetType, out ScriptableObjectCollection resultCollection) { if (TryGetCollectionsOfItemType(targetType, out List possibleCollections)) From aea3f637c000637f4e3b03436082290df4bb143c Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 13:44:12 +0200 Subject: [PATCH 04/23] Created a setup for generating collection items through simple syntax. --- Scripts/Editor/Generators.meta | 8 + .../Editor/Generators/CollectionGenerators.cs | 216 ++++++++++++++++++ .../Generators/CollectionGenerators.cs.meta | 11 + .../IScriptableObjectCollectionGenerator.cs | 22 ++ ...criptableObjectCollectionGenerator.cs.meta | 11 + 5 files changed, 268 insertions(+) create mode 100644 Scripts/Editor/Generators.meta create mode 100644 Scripts/Editor/Generators/CollectionGenerators.cs create mode 100644 Scripts/Editor/Generators/CollectionGenerators.cs.meta create mode 100644 Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs create mode 100644 Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs.meta diff --git a/Scripts/Editor/Generators.meta b/Scripts/Editor/Generators.meta new file mode 100644 index 0000000..e07e8c2 --- /dev/null +++ b/Scripts/Editor/Generators.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a050f0d6a4653f940a9d6fb7c1a59028 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs new file mode 100644 index 0000000..d0dfe3c --- /dev/null +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace BrunoMikoski.ScriptableObjectCollections +{ + /// + /// Responsible for managing collection generators. + /// + public static class CollectionGenerators + { + private static readonly Dictionary generatorTypeToInstance + = new Dictionary(); + + private static IScriptableObjectCollectionGeneratorBase GetGenerator(Type type) + { + bool existed = generatorTypeToInstance.TryGetValue( + type, out IScriptableObjectCollectionGeneratorBase instance); + if (!existed) + { + instance = (IScriptableObjectCollectionGeneratorBase)Activator.CreateInstance(type); + generatorTypeToInstance.Add(type, instance); + } + + return instance; + } + + [MenuItem("SOC/Generate All", false, int.MaxValue)] + public static void GenerateAll() + { + // Find all the generator types. + Type generatorInterface = typeof(IScriptableObjectCollectionGenerator<,>); + Type[] generatorTypes = generatorInterface.GetAllAssignableClasses(); + + foreach (Type generatorType in generatorTypes) + { + GenerateCollectionInternal(generatorType, false); + } + + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + public static void GenerateCollection(Type generatorType) + { + GenerateCollectionInternal(generatorType, true); + } + + private static void GenerateCollectionInternal(Type generatorType, bool refresh) + { + Type generatorInterface = typeof(IScriptableObjectCollectionGenerator<,>); + + // Figure out what type of generator this is. + Type interfaceType = generatorType.GetInterface(generatorInterface.Name); + Type[] genericArguments = interfaceType.GetGenericArguments(); + Type collectionType = genericArguments[0]; + Type itemTemplateType = genericArguments[1]; + + // Check that the corresponding collection exists. + CollectionsRegistry.Instance.TryGetCollectionOfType( + collectionType, out ScriptableObjectCollection collection); + if (collection == null) + { + Debug.LogWarning( + $"Tried to generate items for collection '{collectionType.Name}' but no such " + + $"collection existed."); + return; + } + + // Get an instance of the generator. + IScriptableObjectCollectionGeneratorBase generator = GetGenerator(generatorType); + + // Make an empty list that will hold the generated item templates. + Type genericListType = typeof(List<>); + Type templateListType = genericListType.MakeGenericType(itemTemplateType); + IList list = (IList)Activator.CreateInstance(templateListType); + + // Make the generator generate item templates. + MethodInfo getItemTemplatesMethod = generatorType.GetMethod( + "GetItemTemplates", BindingFlags.Public | BindingFlags.Instance); + getItemTemplatesMethod.Invoke(generator, new object[] {list, collection}); + + // Now try to find or create corresponding items in the collection and copy the fields over. + foreach (object item in list) + { + ItemTemplate itemTemplate = (ItemTemplate)item; + + if (itemTemplate == null) + continue; + + ISOCItem itemInstance = collection.GetOrAddNewBaseItem(itemTemplate.name); + CopyFieldsFromTemplateToItem(itemTemplate, itemInstance); + } + + if (refresh) + { + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + } + + private static void CopyFieldsFromTemplateToItem(ItemTemplate itemTemplate, ISOCItem itemInstance) + { + SerializedObject serializedObject = new SerializedObject(itemInstance as ScriptableObject); + serializedObject.Update(); + + Type itemTemplateType = itemTemplate.GetType(); + FieldInfo[] fields = itemTemplateType.GetFields( + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + foreach (FieldInfo field in fields) + { + object value = field.GetValue(itemTemplate); + CopyFieldToSerializedProperty(field, value, serializedObject); + } + + serializedObject.ApplyModifiedProperties(); + } + + private static void CopyFieldToSerializedProperty(FieldInfo fieldInfo, object value, SerializedObject serializedObject) + { + // Make sure the field is serializable. + if (fieldInfo.IsPrivate && fieldInfo.GetCustomAttribute() == null) + return; + + // Get the property to copy the value to. + SerializedProperty serializedProperty = serializedObject.FindProperty(fieldInfo.Name); + if (serializedProperty == null) + return; + + switch (serializedProperty.propertyType) + { + case SerializedPropertyType.Integer: + serializedProperty.intValue = (int)value; + break; + case SerializedPropertyType.Boolean: + serializedProperty.boolValue = (bool)value; + break; + case SerializedPropertyType.Float: + serializedProperty.floatValue = (float)value; + break; + case SerializedPropertyType.String: + serializedProperty.stringValue = (string)value; + break; + case SerializedPropertyType.Color: + serializedProperty.colorValue = (Color)value; + break; + case SerializedPropertyType.ObjectReference: + serializedProperty.objectReferenceValue = (UnityEngine.Object)value; + break; + case SerializedPropertyType.LayerMask: + serializedProperty.intValue = (LayerMask)value; + break; + case SerializedPropertyType.Enum: + serializedProperty.intValue = (int)value; + break; + case SerializedPropertyType.Vector2: + serializedProperty.vector2Value = (Vector2)value; + break; + case SerializedPropertyType.Vector3: + serializedProperty.vector3Value = (Vector3)value; + break; + case SerializedPropertyType.Vector4: + serializedProperty.vector4Value = (Vector4)value; + break; + case SerializedPropertyType.Rect: + serializedProperty.rectValue = (Rect)value; + break; + //case SerializedPropertyType.ArraySize: + //break; + case SerializedPropertyType.Character: + serializedProperty.stringValue = ((char)value).ToString(); + break; + case SerializedPropertyType.AnimationCurve: + serializedProperty.animationCurveValue = (AnimationCurve)value; + break; + case SerializedPropertyType.Bounds: + serializedProperty.boundsValue = (Bounds)value; + break; + //case SerializedPropertyType.Gradient: + //break; + case SerializedPropertyType.Quaternion: + serializedProperty.quaternionValue = (Quaternion)value; + break; + //case SerializedPropertyType.ExposedReference: + //break; + //case SerializedPropertyType.FixedBufferSize: + //break; + case SerializedPropertyType.Vector2Int: + serializedProperty.vector2IntValue = (Vector2Int)value; + break; + case SerializedPropertyType.Vector3Int: + serializedProperty.vector3IntValue = (Vector3Int)value; + break; + case SerializedPropertyType.RectInt: + serializedProperty.rectIntValue = (RectInt)value; + break; + case SerializedPropertyType.BoundsInt: + serializedProperty.boundsIntValue = (BoundsInt)value; + break; + //case SerializedPropertyType.ManagedReference: + //break; + //case SerializedPropertyType.Hash128: + //break; + case SerializedPropertyType.Generic: + default: + Debug.LogWarning($"Tried to copy value '{value}' from a template to an SOC item but apparently that's not supported."); + break; + } + + } + } +} diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs.meta b/Scripts/Editor/Generators/CollectionGenerators.cs.meta new file mode 100644 index 0000000..046acae --- /dev/null +++ b/Scripts/Editor/Generators/CollectionGenerators.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2729b17b3256a143aea53a857ae752d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs new file mode 100644 index 0000000..116d01b --- /dev/null +++ b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace BrunoMikoski.ScriptableObjectCollections +{ + public interface IScriptableObjectCollectionGeneratorBase + { + } + + public interface IScriptableObjectCollectionGenerator + : IScriptableObjectCollectionGeneratorBase + where CollectionType : ScriptableObjectCollection + where TemplateType : ItemTemplate + { + void GetItemTemplates(List templates, CollectionType collection); + } + + public abstract class ItemTemplate + { + public string name; + } +} diff --git a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs.meta b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs.meta new file mode 100644 index 0000000..c5fe2dd --- /dev/null +++ b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bc2f7ec4968271b4db3df6738f0f8de4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 133dd3e7834f9f575b750faf9b806f735cb372ce Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 14:26:08 +0200 Subject: [PATCH 05/23] If specified, generators will now also remove items that weren't re-generated. --- .../Editor/Generators/CollectionGenerators.cs | 34 +++++++++++++++++-- .../IScriptableObjectCollectionGenerator.cs | 2 ++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index d0dfe3c..4b09ee6 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -4,6 +4,7 @@ using System.Reflection; using UnityEditor; using UnityEngine; +using Object = UnityEngine.Object; namespace BrunoMikoski.ScriptableObjectCollections { @@ -83,10 +84,39 @@ private static void GenerateCollectionInternal(Type generatorType, bool refresh) "GetItemTemplates", BindingFlags.Public | BindingFlags.Instance); getItemTemplatesMethod.Invoke(generator, new object[] {list, collection}); + // If necessary, first remove any items that weren't re-generated. + bool shouldRemoveNonGeneratedItems = (bool)generatorType + .GetProperty("ShouldRemoveNonGeneratedItems", BindingFlags.Public | BindingFlags.Instance) + .GetValue(generator); + if (shouldRemoveNonGeneratedItems) + { + for (int i = collection.Items.Count - 1; i >= 0; i--) + { + bool didHaveTemplateItemWithSameName = false; + for (int j = 0; j < list.Count; j++) + { + ItemTemplate itemTemplate = (ItemTemplate)list[j]; + if (collection.Items[i].name == itemTemplate.name) + { + didHaveTemplateItemWithSameName = true; + break; + } + } + + if (!didHaveTemplateItemWithSameName) + { + // No corresponding template existed, so remove this item. + ScriptableObject itemToRemove = collection.Items[i]; + collection.RemoveAt(i); + AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(itemToRemove)); + } + } + } + // Now try to find or create corresponding items in the collection and copy the fields over. - foreach (object item in list) + for (int i = 0; i < list.Count; i++) { - ItemTemplate itemTemplate = (ItemTemplate)item; + ItemTemplate itemTemplate = (ItemTemplate)list[i]; if (itemTemplate == null) continue; diff --git a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs index 116d01b..9e0e2ae 100644 --- a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs +++ b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs @@ -12,6 +12,8 @@ public interface IScriptableObjectCollectionGenerator templates, CollectionType collection); } From 5411908829252628beb8e97130258e1329c2f07e Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 14:21:17 +0200 Subject: [PATCH 06/23] Added support for arrays in templates. --- .../SerializedPropertyExtensions.cs | 86 ++++++++++++++ .../Editor/Generators/CollectionGenerators.cs | 105 ++++-------------- 2 files changed, 108 insertions(+), 83 deletions(-) diff --git a/Scripts/Editor/Extensions/SerializedPropertyExtensions.cs b/Scripts/Editor/Extensions/SerializedPropertyExtensions.cs index 572fc6f..8af64b1 100644 --- a/Scripts/Editor/Extensions/SerializedPropertyExtensions.cs +++ b/Scripts/Editor/Extensions/SerializedPropertyExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Reflection; using UnityEditor; +using UnityEngine; namespace BrunoMikoski.ScriptableObjectCollections { @@ -23,5 +24,90 @@ public static FieldInfo GetFieldInfoFromPathByType(this SerializedProperty prope return fieldInfo; } + + public static void SetValue(this SerializedProperty serializedProperty, object value) + { + switch (serializedProperty.propertyType) + { + case SerializedPropertyType.Integer: + serializedProperty.intValue = (int)value; + break; + case SerializedPropertyType.Boolean: + serializedProperty.boolValue = (bool)value; + break; + case SerializedPropertyType.Float: + serializedProperty.floatValue = (float)value; + break; + case SerializedPropertyType.String: + serializedProperty.stringValue = (string)value; + break; + case SerializedPropertyType.Color: + serializedProperty.colorValue = (Color)value; + break; + case SerializedPropertyType.ObjectReference: + serializedProperty.objectReferenceValue = (UnityEngine.Object)value; + break; + case SerializedPropertyType.LayerMask: + serializedProperty.intValue = (LayerMask)value; + break; + case SerializedPropertyType.Enum: + serializedProperty.intValue = (int)value; + break; + case SerializedPropertyType.Vector2: + serializedProperty.vector2Value = (Vector2)value; + break; + case SerializedPropertyType.Vector3: + serializedProperty.vector3Value = (Vector3)value; + break; + case SerializedPropertyType.Vector4: + serializedProperty.vector4Value = (Vector4)value; + break; + case SerializedPropertyType.Rect: + serializedProperty.rectValue = (Rect)value; + break; + //case SerializedPropertyType.ArraySize: + //break; + case SerializedPropertyType.Character: + serializedProperty.stringValue = ((char)value).ToString(); + break; + case SerializedPropertyType.AnimationCurve: + serializedProperty.animationCurveValue = (AnimationCurve)value; + break; + case SerializedPropertyType.Bounds: + serializedProperty.boundsValue = (Bounds)value; + break; + //case SerializedPropertyType.Gradient: + //break; + case SerializedPropertyType.Quaternion: + serializedProperty.quaternionValue = (Quaternion)value; + break; + //case SerializedPropertyType.ExposedReference: + //break; + //case SerializedPropertyType.FixedBufferSize: + //break; + case SerializedPropertyType.Vector2Int: + serializedProperty.vector2IntValue = (Vector2Int)value; + break; + case SerializedPropertyType.Vector3Int: + serializedProperty.vector3IntValue = (Vector3Int)value; + break; + case SerializedPropertyType.RectInt: + serializedProperty.rectIntValue = (RectInt)value; + break; + case SerializedPropertyType.BoundsInt: + serializedProperty.boundsIntValue = (BoundsInt)value; + break; + //case SerializedPropertyType.ManagedReference: + //break; + case SerializedPropertyType.Hash128: + serializedProperty.hash128Value = (Hash128)value; + break; + case SerializedPropertyType.Generic: + default: + Debug.LogWarning( + $"Tried to copy value '{value}' from a template to an SOC item but apparently that's not supported."); + break; + } + } } } diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index 4b09ee6..3415b9c 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine; @@ -143,104 +144,42 @@ private static void CopyFieldsFromTemplateToItem(ItemTemplate itemTemplate, ISOC foreach (FieldInfo field in fields) { - object value = field.GetValue(itemTemplate); - CopyFieldToSerializedProperty(field, value, serializedObject); + CopyFieldToSerializedProperty(field, itemTemplate, serializedObject); } serializedObject.ApplyModifiedProperties(); } - private static void CopyFieldToSerializedProperty(FieldInfo fieldInfo, object value, SerializedObject serializedObject) + private static void CopyFieldToSerializedProperty( + FieldInfo field, object owner, SerializedObject serializedObject) { // Make sure the field is serializable. - if (fieldInfo.IsPrivate && fieldInfo.GetCustomAttribute() == null) + if (field.IsPrivate && field.GetCustomAttribute() == null) return; // Get the property to copy the value to. - SerializedProperty serializedProperty = serializedObject.FindProperty(fieldInfo.Name); + SerializedProperty serializedProperty = serializedObject.FindProperty(field.Name); if (serializedProperty == null) return; - - switch (serializedProperty.propertyType) + + object value = field.GetValue(owner); + + // Support arrays. + if (serializedProperty.isArray && serializedProperty.propertyType == SerializedPropertyType.Generic) { - case SerializedPropertyType.Integer: - serializedProperty.intValue = (int)value; - break; - case SerializedPropertyType.Boolean: - serializedProperty.boolValue = (bool)value; - break; - case SerializedPropertyType.Float: - serializedProperty.floatValue = (float)value; - break; - case SerializedPropertyType.String: - serializedProperty.stringValue = (string)value; - break; - case SerializedPropertyType.Color: - serializedProperty.colorValue = (Color)value; - break; - case SerializedPropertyType.ObjectReference: - serializedProperty.objectReferenceValue = (UnityEngine.Object)value; - break; - case SerializedPropertyType.LayerMask: - serializedProperty.intValue = (LayerMask)value; - break; - case SerializedPropertyType.Enum: - serializedProperty.intValue = (int)value; - break; - case SerializedPropertyType.Vector2: - serializedProperty.vector2Value = (Vector2)value; - break; - case SerializedPropertyType.Vector3: - serializedProperty.vector3Value = (Vector3)value; - break; - case SerializedPropertyType.Vector4: - serializedProperty.vector4Value = (Vector4)value; - break; - case SerializedPropertyType.Rect: - serializedProperty.rectValue = (Rect)value; - break; - //case SerializedPropertyType.ArraySize: - //break; - case SerializedPropertyType.Character: - serializedProperty.stringValue = ((char)value).ToString(); - break; - case SerializedPropertyType.AnimationCurve: - serializedProperty.animationCurveValue = (AnimationCurve)value; - break; - case SerializedPropertyType.Bounds: - serializedProperty.boundsValue = (Bounds)value; - break; - //case SerializedPropertyType.Gradient: - //break; - case SerializedPropertyType.Quaternion: - serializedProperty.quaternionValue = (Quaternion)value; - break; - //case SerializedPropertyType.ExposedReference: - //break; - //case SerializedPropertyType.FixedBufferSize: - //break; - case SerializedPropertyType.Vector2Int: - serializedProperty.vector2IntValue = (Vector2Int)value; - break; - case SerializedPropertyType.Vector3Int: - serializedProperty.vector3IntValue = (Vector3Int)value; - break; - case SerializedPropertyType.RectInt: - serializedProperty.rectIntValue = (RectInt)value; - break; - case SerializedPropertyType.BoundsInt: - serializedProperty.boundsIntValue = (BoundsInt)value; - break; - //case SerializedPropertyType.ManagedReference: - //break; - //case SerializedPropertyType.Hash128: - //break; - case SerializedPropertyType.Generic: - default: - Debug.LogWarning($"Tried to copy value '{value}' from a template to an SOC item but apparently that's not supported."); - break; + IEnumerable collection = (IEnumerable)value; + serializedProperty.arraySize = collection.Count(); + int index = 0; + foreach (object arrayItem in collection) + { + SerializedProperty arrayElement = serializedProperty.GetArrayElementAtIndex(index); + arrayElement.SetValue(arrayItem); + index++; + } + return; } + serializedProperty.SetValue(value); } } } From a185077d4a7dc18feb73a7c863184f91a5175382 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 14:49:33 +0200 Subject: [PATCH 07/23] Added a button to generate a specific collection. --- .../CustomEditors/CollectionCustomEditor.cs | 16 ++++++- .../Editor/Generators/CollectionGenerators.cs | 44 +++++++++++++------ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs index bc7de47..109ac63 100644 --- a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs +++ b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs @@ -31,6 +31,8 @@ public class CollectionCustomEditor : BaseEditor private readonly Dictionary itemIndexToRect = new(); private float? reorderableListYPosition; private Dictionary collectionItemSerializedObjectCache = new(); + + private Type generatorType; protected virtual bool CanBeReorderable => true; protected virtual bool DisplayAddButton => true; @@ -59,6 +61,8 @@ protected virtual void OnEnable() CheckGeneratedCodeLocation(); CheckGeneratedStaticFileName(); ValidateGeneratedFileNamespace(); + + generatorType = CollectionGenerators.GetGeneratorTypeForCollection(collection.GetType()); } private void CreateReorderableList() @@ -560,15 +564,23 @@ private void ValidateCollectionItems() private void DrawBottomMenu() { - using (new EditorGUILayout.HorizontalScope("Box")) + using (new EditorGUILayout.VerticalScope("Box")) { - if (GUILayout.Button($"Generate Static Access File", EditorStyles.miniButtonRight)) + if (GUILayout.Button($"Generate Static Access File", EditorStyles.miniButton)) { EditorApplication.delayCall += () => { CodeGenerationUtility.GenerateStaticCollectionScript(collection); }; } + + if (generatorType != null && GUILayout.Button($"Generate Items", GUILayout.Height(40))) + { + EditorApplication.delayCall += () => + { + CollectionGenerators.GenerateCollection(generatorType); + }; + } } } diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index 3415b9c..227b251 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -16,6 +16,8 @@ public static class CollectionGenerators { private static readonly Dictionary generatorTypeToInstance = new Dictionary(); + + private static Type InterfaceType => typeof(IScriptableObjectCollectionGenerator<,>); private static IScriptableObjectCollectionGeneratorBase GetGenerator(Type type) { @@ -29,14 +31,36 @@ private static IScriptableObjectCollectionGeneratorBase GetGenerator(Type type) return instance; } + private static void GetGeneratorTypes(Type generatorType, out Type collectionType, out Type templateType) + { + Type interfaceType = generatorType.GetInterface(InterfaceType.Name); + Type[] genericArguments = interfaceType.GetGenericArguments(); + collectionType = genericArguments[0]; + templateType = genericArguments[1]; + } + + + private static Type[] GetGeneratorTypes() + { + return InterfaceType.GetAllAssignableClasses(); + } + + public static Type GetGeneratorTypeForCollection(Type collectionType) + { + Type[] generatorTypes = GetGeneratorTypes(); + foreach (Type generatorType in generatorTypes) + { + GetGeneratorTypes(generatorType, out Type generatorCollectionType, out Type generatorTemplateType); + if (generatorCollectionType == collectionType) + return generatorType; + } + + return null; + } - [MenuItem("SOC/Generate All", false, int.MaxValue)] public static void GenerateAll() { - // Find all the generator types. - Type generatorInterface = typeof(IScriptableObjectCollectionGenerator<,>); - Type[] generatorTypes = generatorInterface.GetAllAssignableClasses(); - + Type[] generatorTypes = GetGeneratorTypes(); foreach (Type generatorType in generatorTypes) { GenerateCollectionInternal(generatorType, false); @@ -53,14 +77,8 @@ public static void GenerateCollection(Type generatorType) private static void GenerateCollectionInternal(Type generatorType, bool refresh) { - Type generatorInterface = typeof(IScriptableObjectCollectionGenerator<,>); - - // Figure out what type of generator this is. - Type interfaceType = generatorType.GetInterface(generatorInterface.Name); - Type[] genericArguments = interfaceType.GetGenericArguments(); - Type collectionType = genericArguments[0]; - Type itemTemplateType = genericArguments[1]; - + GetGeneratorTypes(generatorType, out Type collectionType, out Type itemTemplateType); + // Check that the corresponding collection exists. CollectionsRegistry.Instance.TryGetCollectionOfType( collectionType, out ScriptableObjectCollection collection); From 904192a7b4d1a527105e0119cfff43c25a490637 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 14:51:39 +0200 Subject: [PATCH 08/23] Small rename for clarity. --- .../CustomEditors/CollectionCustomEditor.cs | 2 +- .../Editor/Generators/CollectionGenerators.cs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs index 109ac63..2f72bcb 100644 --- a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs +++ b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs @@ -578,7 +578,7 @@ private void DrawBottomMenu() { EditorApplication.delayCall += () => { - CollectionGenerators.GenerateCollection(generatorType); + CollectionGenerators.RunGenerator(generatorType); }; } } diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index 227b251..17d2151 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -58,24 +58,30 @@ public static Type GetGeneratorTypeForCollection(Type collectionType) return null; } - public static void GenerateAll() + public static void RunAllGenerators() { Type[] generatorTypes = GetGeneratorTypes(); foreach (Type generatorType in generatorTypes) { - GenerateCollectionInternal(generatorType, false); + RunGeneratorInternal(generatorType, false); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } - public static void GenerateCollection(Type generatorType) + public static void RunGenerator(Type generatorType) { - GenerateCollectionInternal(generatorType, true); + RunGeneratorInternal(generatorType, true); + } + + public static void RunGenerator() + where GeneratorType : IScriptableObjectCollectionGeneratorBase + { + RunGenerator(typeof(GeneratorType)); } - private static void GenerateCollectionInternal(Type generatorType, bool refresh) + private static void RunGeneratorInternal(Type generatorType, bool refresh) { GetGeneratorTypes(generatorType, out Type collectionType, out Type itemTemplateType); From 63880f61de5f9b980a2a734f6f6d148c4787c3e8 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 14:58:18 +0200 Subject: [PATCH 09/23] Fixed incorrect whitespace. --- Scripts/Editor/Generators/CollectionGenerators.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index 17d2151..ee97ca8 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -31,6 +31,7 @@ private static IScriptableObjectCollectionGeneratorBase GetGenerator(Type type) return instance; } + private static void GetGeneratorTypes(Type generatorType, out Type collectionType, out Type templateType) { Type interfaceType = generatorType.GetInterface(InterfaceType.Name); @@ -39,7 +40,6 @@ private static void GetGeneratorTypes(Type generatorType, out Type collectionTyp templateType = genericArguments[1]; } - private static Type[] GetGeneratorTypes() { return InterfaceType.GetAllAssignableClasses(); From 7d1c121accfc0e67d95b24a9e2de177b018b2051 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 15:02:34 +0200 Subject: [PATCH 10/23] Added some extra descriptions for clarity. --- .../IScriptableObjectCollectionGenerator.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs index 9e0e2ae..5079ecf 100644 --- a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs +++ b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs @@ -7,16 +7,28 @@ public interface IScriptableObjectCollectionGeneratorBase { } + /// + /// Interface for classes that generate items for a Scriptable Object Collection. + /// + /// The type of collection to generate items for. + /// The template class that represents items to add/update. public interface IScriptableObjectCollectionGenerator : IScriptableObjectCollectionGeneratorBase where CollectionType : ScriptableObjectCollection where TemplateType : ItemTemplate { + /// + /// If specified, any items that do not match the returned item templates get removed. + /// Return false if you want to generate items but allow people to manually add items of their own. + /// bool ShouldRemoveNonGeneratedItems { get; } void GetItemTemplates(List templates, CollectionType collection); } + /// + /// Base class for templates that represent which items there should be in a collection. + /// public abstract class ItemTemplate { public string name; From 23ec8ce688f321223b48d51faa85147c0416036f Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 15:08:53 +0200 Subject: [PATCH 11/23] Removed an empty case. --- Scripts/Editor/Extensions/SerializedPropertyExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Scripts/Editor/Extensions/SerializedPropertyExtensions.cs b/Scripts/Editor/Extensions/SerializedPropertyExtensions.cs index 8af64b1..65af165 100644 --- a/Scripts/Editor/Extensions/SerializedPropertyExtensions.cs +++ b/Scripts/Editor/Extensions/SerializedPropertyExtensions.cs @@ -102,7 +102,6 @@ public static void SetValue(this SerializedProperty serializedProperty, object v case SerializedPropertyType.Hash128: serializedProperty.hash128Value = (Hash128)value; break; - case SerializedPropertyType.Generic: default: Debug.LogWarning( $"Tried to copy value '{value}' from a template to an SOC item but apparently that's not supported."); From 6f9c3a244399156d85a9e0364550b5ee4f171a83 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 15:18:13 +0200 Subject: [PATCH 12/23] Added a way to run a specific generator instance. --- .../Editor/Generators/CollectionGenerators.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index ee97ca8..54a15f3 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -80,9 +80,23 @@ public static void RunGenerator() { RunGenerator(typeof(GeneratorType)); } + + public static void RunGenerator(IScriptableObjectCollectionGeneratorBase generator) + { + RunGeneratorInternal(generator, true); + } private static void RunGeneratorInternal(Type generatorType, bool refresh) { + IScriptableObjectCollectionGeneratorBase generator = GetGenerator(generatorType); + + RunGeneratorInternal(generator, refresh); + } + + private static void RunGeneratorInternal(IScriptableObjectCollectionGeneratorBase generator, bool refresh) + { + Type generatorType = generator.GetType(); + GetGeneratorTypes(generatorType, out Type collectionType, out Type itemTemplateType); // Check that the corresponding collection exists. @@ -96,9 +110,6 @@ private static void RunGeneratorInternal(Type generatorType, bool refresh) return; } - // Get an instance of the generator. - IScriptableObjectCollectionGeneratorBase generator = GetGenerator(generatorType); - // Make an empty list that will hold the generated item templates. Type genericListType = typeof(List<>); Type templateListType = genericListType.MakeGenericType(itemTemplateType); From 064614fb540384ed0a287212e4774e089dca3dd4 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 15:34:13 +0200 Subject: [PATCH 13/23] Added the option to also generate static access immediately. --- .../Editor/Generators/CollectionGenerators.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index 54a15f3..cdd47ae 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -70,30 +70,32 @@ public static void RunAllGenerators() AssetDatabase.Refresh(); } - public static void RunGenerator(Type generatorType) + public static void RunGenerator(Type generatorType, bool generateStaticAccess = false) { - RunGeneratorInternal(generatorType, true); + RunGeneratorInternal(generatorType, true, generateStaticAccess); } - public static void RunGenerator() + public static void RunGenerator(bool generateStaticAccess = false) where GeneratorType : IScriptableObjectCollectionGeneratorBase { - RunGenerator(typeof(GeneratorType)); + RunGenerator(typeof(GeneratorType), generateStaticAccess); } - public static void RunGenerator(IScriptableObjectCollectionGeneratorBase generator) + public static void RunGenerator( + IScriptableObjectCollectionGeneratorBase generator, bool generateStaticAccess = false) { - RunGeneratorInternal(generator, true); + RunGeneratorInternal(generator, true, generateStaticAccess); } - private static void RunGeneratorInternal(Type generatorType, bool refresh) + private static void RunGeneratorInternal(Type generatorType, bool refresh, bool generateStaticAccess = false) { IScriptableObjectCollectionGeneratorBase generator = GetGenerator(generatorType); - RunGeneratorInternal(generator, refresh); + RunGeneratorInternal(generator, refresh, generateStaticAccess); } - private static void RunGeneratorInternal(IScriptableObjectCollectionGeneratorBase generator, bool refresh) + private static void RunGeneratorInternal( + IScriptableObjectCollectionGeneratorBase generator, bool refresh, bool generateStaticAccess) { Type generatorType = generator.GetType(); @@ -166,6 +168,9 @@ private static void RunGeneratorInternal(IScriptableObjectCollectionGeneratorBas AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } + + if (generateStaticAccess) + CodeGenerationUtility.GenerateStaticCollectionScript(collection); } private static void CopyFieldsFromTemplateToItem(ItemTemplate itemTemplate, ISOCItem itemInstance) From 6be3b6798d8e51ea870df85290516ca4d08d54d0 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Fri, 25 Aug 2023 20:35:54 +0200 Subject: [PATCH 14/23] Made sure the generators are cleared for projects that don't do domain reloading. --- Scripts/Editor/Generators/CollectionGenerators.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index cdd47ae..c1c5a08 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -18,6 +18,13 @@ private static readonly Dictionary(); private static Type InterfaceType => typeof(IScriptableObjectCollectionGenerator<,>); + + [InitializeOnLoadMethod] + private static void Initialize() + { + // Make sure we clean this when code reloads. + generatorTypeToInstance.Clear(); + } private static IScriptableObjectCollectionGeneratorBase GetGenerator(Type type) { From 510871aca4971902ed136bcdb011feb657106dbc Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Sat, 26 Aug 2023 22:23:41 +0200 Subject: [PATCH 15/23] Added a way to add a new base item. --- Scripts/Runtime/Core/ScriptableObjectCollection.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Scripts/Runtime/Core/ScriptableObjectCollection.cs b/Scripts/Runtime/Core/ScriptableObjectCollection.cs index daf5666..ce53adf 100644 --- a/Scripts/Runtime/Core/ScriptableObjectCollection.cs +++ b/Scripts/Runtime/Core/ScriptableObjectCollection.cs @@ -184,13 +184,18 @@ public ScriptableObject AddNew(Type itemType, string assetName = "") return newItem; } + public ISOCItem AddNewBaseItem(string targetName) + { + return AddNew(GetItemType(), targetName) as ISOCItem; + } + public ISOCItem GetOrAddNewBaseItem(string targetName) { ISOCItem item = Items.FirstOrDefault(o => o.name.Equals(targetName, StringComparison.Ordinal)) as ISOCItem; if (item != null) return item; - return AddNew(GetItemType(), targetName) as ISOCItem; + return AddNewBaseItem(targetName); } #endif From 4467d86a29282cf334bdf338252ad4f2627f7172 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Sat, 26 Aug 2023 22:25:40 +0200 Subject: [PATCH 16/23] Item generators can now optionally match items by index instead of name. --- .../Editor/Generators/CollectionGenerators.cs | 60 ++++++++++++++----- .../GeneratorExistingItemFindingBehaviours.cs | 8 +++ ...ratorExistingItemFindingBehaviours.cs.meta | 3 + .../IScriptableObjectCollectionGenerator.cs | 2 + 4 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs create mode 100644 Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs.meta diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index c1c5a08..7cba962 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -122,12 +122,18 @@ private static void RunGeneratorInternal( // Make an empty list that will hold the generated item templates. Type genericListType = typeof(List<>); Type templateListType = genericListType.MakeGenericType(itemTemplateType); - IList list = (IList)Activator.CreateInstance(templateListType); + IList templates = (IList)Activator.CreateInstance(templateListType); // Make the generator generate item templates. MethodInfo getItemTemplatesMethod = generatorType.GetMethod( "GetItemTemplates", BindingFlags.Public | BindingFlags.Instance); - getItemTemplatesMethod.Invoke(generator, new object[] {list, collection}); + getItemTemplatesMethod.Invoke(generator, new object[] {templates, collection}); + + // Figure out the intended behaviour for finding existing items. + GeneratorExistingItemFindingBehaviours itemFindingBehaviour = + (GeneratorExistingItemFindingBehaviours)generatorType + .GetProperty("ItemFindingBehaviour", BindingFlags.Public | BindingFlags.Instance) + .GetValue(generator); // If necessary, first remove any items that weren't re-generated. bool shouldRemoveNonGeneratedItems = (bool)generatorType @@ -137,18 +143,31 @@ private static void RunGeneratorInternal( { for (int i = collection.Items.Count - 1; i >= 0; i--) { - bool didHaveTemplateItemWithSameName = false; - for (int j = 0; j < list.Count; j++) + bool shouldRemoveItem = false; + + switch (itemFindingBehaviour) { - ItemTemplate itemTemplate = (ItemTemplate)list[j]; - if (collection.Items[i].name == itemTemplate.name) - { - didHaveTemplateItemWithSameName = true; + case GeneratorExistingItemFindingBehaviours.FindByName: + // Remove any items for which there isn't a template by the same name. + for (int j = 0; j < templates.Count; j++) + { + ItemTemplate itemTemplate = (ItemTemplate)templates[j]; + if (collection.Items[i].name == itemTemplate.name) + { + shouldRemoveItem = true; + break; + } + } + break; + case GeneratorExistingItemFindingBehaviours.FindByIndex: + // Remove any items beyond the size of the list of templates that were returned. + shouldRemoveItem = i >= templates.Count; break; - } + default: + throw new ArgumentOutOfRangeException(); } - - if (!didHaveTemplateItemWithSameName) + + if (!shouldRemoveItem) { // No corresponding template existed, so remove this item. ScriptableObject itemToRemove = collection.Items[i]; @@ -159,14 +178,27 @@ private static void RunGeneratorInternal( } // Now try to find or create corresponding items in the collection and copy the fields over. - for (int i = 0; i < list.Count; i++) + for (int i = 0; i < templates.Count; i++) { - ItemTemplate itemTemplate = (ItemTemplate)list[i]; + ItemTemplate itemTemplate = (ItemTemplate)templates[i]; if (itemTemplate == null) continue; - ISOCItem itemInstance = collection.GetOrAddNewBaseItem(itemTemplate.name); + ISOCItem itemInstance; + switch (itemFindingBehaviour) + { + case GeneratorExistingItemFindingBehaviours.FindByName: + itemInstance = collection.GetOrAddNewBaseItem(itemTemplate.name); + break; + case GeneratorExistingItemFindingBehaviours.FindByIndex: + itemInstance = i < collection.Items.Count ? + (ISOCItem)collection.Items[i] : collection.AddNewBaseItem(itemTemplate.name); + break; + default: + throw new ArgumentOutOfRangeException(); + } + CopyFieldsFromTemplateToItem(itemTemplate, itemInstance); } diff --git a/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs b/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs new file mode 100644 index 0000000..713ab91 --- /dev/null +++ b/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs @@ -0,0 +1,8 @@ +namespace BrunoMikoski.ScriptableObjectCollections +{ + public enum GeneratorExistingItemFindingBehaviours + { + FindByName, + FindByIndex, + } +} \ No newline at end of file diff --git a/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs.meta b/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs.meta new file mode 100644 index 0000000..78c22b8 --- /dev/null +++ b/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 68d7f0738fe147f684563b1a64fdbffe +timeCreated: 1693081431 \ No newline at end of file diff --git a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs index 5079ecf..04e1735 100644 --- a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs +++ b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs @@ -23,6 +23,8 @@ public interface IScriptableObjectCollectionGenerator bool ShouldRemoveNonGeneratedItems { get; } + GeneratorExistingItemFindingBehaviours ItemFindingBehaviour { get; } + void GetItemTemplates(List templates, CollectionType collection); } From 9666062b06dc3f948791042565b6674c5d1369f7 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Sat, 26 Aug 2023 22:42:05 +0200 Subject: [PATCH 17/23] Renames / comments for clarity. --- .../Editor/Generators/CollectionGenerators.cs | 16 ++++++++-------- .../GeneratorExistingItemFindingBehaviours.cs | 8 -------- .../GeneratorItemToTemplateMatchingBehaviours.cs | 8 ++++++++ ...atorItemToTemplateMatchingBehaviours.cs.meta} | 0 .../IScriptableObjectCollectionGenerator.cs | 9 ++++++++- 5 files changed, 24 insertions(+), 17 deletions(-) delete mode 100644 Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs create mode 100644 Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs rename Scripts/Editor/Generators/{GeneratorExistingItemFindingBehaviours.cs.meta => GeneratorItemToTemplateMatchingBehaviours.cs.meta} (100%) diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index 7cba962..313bd91 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -130,8 +130,8 @@ private static void RunGeneratorInternal( getItemTemplatesMethod.Invoke(generator, new object[] {templates, collection}); // Figure out the intended behaviour for finding existing items. - GeneratorExistingItemFindingBehaviours itemFindingBehaviour = - (GeneratorExistingItemFindingBehaviours)generatorType + GeneratorItemToTemplateMatchingBehaviours itemToTemplateMatchingBehaviour = + (GeneratorItemToTemplateMatchingBehaviours)generatorType .GetProperty("ItemFindingBehaviour", BindingFlags.Public | BindingFlags.Instance) .GetValue(generator); @@ -145,9 +145,9 @@ private static void RunGeneratorInternal( { bool shouldRemoveItem = false; - switch (itemFindingBehaviour) + switch (itemToTemplateMatchingBehaviour) { - case GeneratorExistingItemFindingBehaviours.FindByName: + case GeneratorItemToTemplateMatchingBehaviours.MatchByName: // Remove any items for which there isn't a template by the same name. for (int j = 0; j < templates.Count; j++) { @@ -159,7 +159,7 @@ private static void RunGeneratorInternal( } } break; - case GeneratorExistingItemFindingBehaviours.FindByIndex: + case GeneratorItemToTemplateMatchingBehaviours.MatchByIndex: // Remove any items beyond the size of the list of templates that were returned. shouldRemoveItem = i >= templates.Count; break; @@ -186,12 +186,12 @@ private static void RunGeneratorInternal( continue; ISOCItem itemInstance; - switch (itemFindingBehaviour) + switch (itemToTemplateMatchingBehaviour) { - case GeneratorExistingItemFindingBehaviours.FindByName: + case GeneratorItemToTemplateMatchingBehaviours.MatchByName: itemInstance = collection.GetOrAddNewBaseItem(itemTemplate.name); break; - case GeneratorExistingItemFindingBehaviours.FindByIndex: + case GeneratorItemToTemplateMatchingBehaviours.MatchByIndex: itemInstance = i < collection.Items.Count ? (ISOCItem)collection.Items[i] : collection.AddNewBaseItem(itemTemplate.name); break; diff --git a/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs b/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs deleted file mode 100644 index 713ab91..0000000 --- a/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace BrunoMikoski.ScriptableObjectCollections -{ - public enum GeneratorExistingItemFindingBehaviours - { - FindByName, - FindByIndex, - } -} \ No newline at end of file diff --git a/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs b/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs new file mode 100644 index 0000000..ec7d51d --- /dev/null +++ b/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs @@ -0,0 +1,8 @@ +namespace BrunoMikoski.ScriptableObjectCollections +{ + public enum GeneratorItemToTemplateMatchingBehaviours + { + MatchByName, + MatchByIndex, + } +} diff --git a/Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs.meta b/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs.meta similarity index 100% rename from Scripts/Editor/Generators/GeneratorExistingItemFindingBehaviours.cs.meta rename to Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs.meta diff --git a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs index 04e1735..038b225 100644 --- a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs +++ b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs @@ -23,7 +23,14 @@ public interface IScriptableObjectCollectionGenerator bool ShouldRemoveNonGeneratedItems { get; } - GeneratorExistingItemFindingBehaviours ItemFindingBehaviour { get; } + /// + /// How to match existing items to templates. Matching them by name allows you to reorder the items and still + /// find the same item it belonged to, matching them by index doesn't handle reordering well but it lets you + /// rename them very easily. Matching by name makes more sense for items generated based on scenes, which tend + /// to come in at a different order every time, but when generating items for tags it makes more sense to do it + /// by index because you're not allowed to reorder tags in the first place and that way it handles renames well. + /// + GeneratorItemToTemplateMatchingBehaviours ItemToTemplateMatchingBehaviour { get; } void GetItemTemplates(List templates, CollectionType collection); } From d7605542c280046327127b82e5471d9c3a722a3c Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Sat, 26 Aug 2023 23:02:39 +0200 Subject: [PATCH 18/23] Certain controls are now removed for auto-generated collections. --- .../CustomEditors/CollectionCustomEditor.cs | 54 ++++++++++++++++--- .../Editor/Generators/CollectionGenerators.cs | 2 +- .../IScriptableObjectCollectionGenerator.cs | 25 +++++---- 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs index 2f72bcb..b0330e2 100644 --- a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs +++ b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs @@ -32,11 +32,51 @@ public class CollectionCustomEditor : BaseEditor private float? reorderableListYPosition; private Dictionary collectionItemSerializedObjectCache = new(); - private Type generatorType; + [NonSerialized] private Type generatorType; + [NonSerialized] private IScriptableObjectCollectionGeneratorBase generator; + + private bool IsAutoGenerated => generatorType != null; + + protected virtual bool CanBeReorderable + { + get + { + // If this is a generated collection and it uses the order of items to match newly generated templates + // to previously existing items, don't let the list be reordered. It just allows you to break things. + if (IsAutoGenerated && generator.ItemToTemplateMatchingBehaviour + == GeneratorItemToTemplateMatchingBehaviours.MatchByIndex) + { + return false; + } + return true; + } + } + + protected virtual bool DisplayAddButton + { + get + { + // If this is a generated collection and it's set to remove items that aren't re-generated, then it + // doesn't make sense for you to add items because they will be removed next time you generate. + if (IsAutoGenerated && generator.ShouldRemoveNonGeneratedItems) + return false; + return true; + } + } + + protected virtual bool DisplayRemoveButton + { + get + { + // If this is a generated collection and it's set to remove items that aren't re-generated, then it + // doesn't make sense for you to remove items because they will be added back next time you generate. + if (IsAutoGenerated && generator.ShouldRemoveNonGeneratedItems) + return false; + + return true; + } + } - protected virtual bool CanBeReorderable => true; - protected virtual bool DisplayAddButton => true; - protected virtual bool DisplayRemoveButton => true; protected virtual bool AllowCustomTypeCreation => true; private static bool IsWaitingForNewTypeBeCreated @@ -53,6 +93,10 @@ protected virtual void OnEnable() CollectionsRegistry.Instance.ReloadCollections(); itemsSerializedProperty = serializedObject.FindProperty("items"); + + // Need to cache this before the reorderable list is created, because it affects how the list is displayed. + generatorType = CollectionGenerators.GetGeneratorTypeForCollection(collection.GetType()); + generator = generatorType == null ? null : CollectionGenerators.GetGenerator(generatorType); ValidateCollectionItems(); @@ -61,8 +105,6 @@ protected virtual void OnEnable() CheckGeneratedCodeLocation(); CheckGeneratedStaticFileName(); ValidateGeneratedFileNamespace(); - - generatorType = CollectionGenerators.GetGeneratorTypeForCollection(collection.GetType()); } private void CreateReorderableList() diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index 313bd91..5d487fc 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -26,7 +26,7 @@ private static void Initialize() generatorTypeToInstance.Clear(); } - private static IScriptableObjectCollectionGeneratorBase GetGenerator(Type type) + public static IScriptableObjectCollectionGeneratorBase GetGenerator(Type type) { bool existed = generatorTypeToInstance.TryGetValue( type, out IScriptableObjectCollectionGeneratorBase instance); diff --git a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs index 038b225..9c8c508 100644 --- a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs +++ b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs @@ -4,25 +4,13 @@ namespace BrunoMikoski.ScriptableObjectCollections { public interface IScriptableObjectCollectionGeneratorBase - { - } - - /// - /// Interface for classes that generate items for a Scriptable Object Collection. - /// - /// The type of collection to generate items for. - /// The template class that represents items to add/update. - public interface IScriptableObjectCollectionGenerator - : IScriptableObjectCollectionGeneratorBase - where CollectionType : ScriptableObjectCollection - where TemplateType : ItemTemplate { /// /// If specified, any items that do not match the returned item templates get removed. /// Return false if you want to generate items but allow people to manually add items of their own. /// bool ShouldRemoveNonGeneratedItems { get; } - + /// /// How to match existing items to templates. Matching them by name allows you to reorder the items and still /// find the same item it belonged to, matching them by index doesn't handle reordering well but it lets you @@ -31,7 +19,18 @@ public interface IScriptableObjectCollectionGenerator GeneratorItemToTemplateMatchingBehaviours ItemToTemplateMatchingBehaviour { get; } + } + /// + /// Interface for classes that generate items for a Scriptable Object Collection. + /// + /// The type of collection to generate items for. + /// The template class that represents items to add/update. + public interface IScriptableObjectCollectionGenerator + : IScriptableObjectCollectionGeneratorBase + where CollectionType : ScriptableObjectCollection + where TemplateType : ItemTemplate + { void GetItemTemplates(List templates, CollectionType collection); } From 6798c1f3e64884b67170703753f06da4388fb4d2 Mon Sep 17 00:00:00 2001 From: Bruno Mikoski Date: Sun, 27 Aug 2023 12:27:31 +0100 Subject: [PATCH 19/23] fix: wrong property name --- Scripts/Editor/Generators/CollectionGenerators.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index 5d487fc..f1961d0 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -132,7 +132,7 @@ private static void RunGeneratorInternal( // Figure out the intended behaviour for finding existing items. GeneratorItemToTemplateMatchingBehaviours itemToTemplateMatchingBehaviour = (GeneratorItemToTemplateMatchingBehaviours)generatorType - .GetProperty("ItemFindingBehaviour", BindingFlags.Public | BindingFlags.Instance) + .GetProperty("ItemToTemplateMatchingBehaviour", BindingFlags.Public | BindingFlags.Instance) .GetValue(generator); // If necessary, first remove any items that weren't re-generated. From 73ae3a09498fc93fb6dac479bbffd062d3b38572 Mon Sep 17 00:00:00 2001 From: Bruno Mikoski Date: Sun, 27 Aug 2023 12:27:52 +0100 Subject: [PATCH 20/23] add: custom generic collection item type for the template --- .../Editor/Generators/CollectionGenerators.cs | 48 ++++++++++++++++++- .../IScriptableObjectCollectionGenerator.cs | 5 ++ .../Core/ScriptableObjectCollection.cs | 18 +++---- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index f1961d0..daf03d4 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -185,16 +185,24 @@ private static void RunGeneratorInternal( if (itemTemplate == null) continue; + + if (!TryGetItemTemplateType(itemTemplate, out Type templateItemType)) + templateItemType = collection.GetItemType(); + ISOCItem itemInstance; switch (itemToTemplateMatchingBehaviour) { case GeneratorItemToTemplateMatchingBehaviours.MatchByName: - itemInstance = collection.GetOrAddNewBaseItem(itemTemplate.name); + { + itemInstance = collection.GetOrAddNew(templateItemType, itemTemplate.name); break; + } case GeneratorItemToTemplateMatchingBehaviours.MatchByIndex: + { itemInstance = i < collection.Items.Count ? - (ISOCItem)collection.Items[i] : collection.AddNewBaseItem(itemTemplate.name); + (ISOCItem)collection.Items[i] : collection.AddNew(templateItemType, itemTemplate.name) as ISOCItem; break; + } default: throw new ArgumentOutOfRangeException(); } @@ -212,6 +220,42 @@ private static void RunGeneratorInternal( CodeGenerationUtility.GenerateStaticCollectionScript(collection); } + private static bool TryGetItemTemplateType(ItemTemplate itemTemplate, out Type resultType) + { + Type itemType = GetGenericItemType(itemTemplate); + if (itemType == null) + { + resultType = null; + return false; + } + + resultType = itemType.GetGenericArguments().First(); + return resultType != null; + } + + public static Type GetTemplateItemType(ItemTemplate itemTemplate) + { + Type itemType = GetGenericItemType(itemTemplate); + if (itemType == null) + return null; + + Type genericType = itemType.GetGenericArguments().First(); + return genericType; + } + + private static Type GetGenericItemType(ItemTemplate itemTemplate) + { + Type baseType = itemTemplate.GetType().BaseType; + + while (baseType != null) + { + if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(ItemTemplate<>)) + return baseType; + baseType = baseType.BaseType; + } + return null; + } + private static void CopyFieldsFromTemplateToItem(ItemTemplate itemTemplate, ISOCItem itemInstance) { SerializedObject serializedObject = new SerializedObject(itemInstance as ScriptableObject); diff --git a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs index 9c8c508..40ba1a6 100644 --- a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs +++ b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs @@ -41,4 +41,9 @@ public abstract class ItemTemplate { public string name; } + + public class ItemTemplate : ItemTemplate where T: ISOCItem + { + } + } diff --git a/Scripts/Runtime/Core/ScriptableObjectCollection.cs b/Scripts/Runtime/Core/ScriptableObjectCollection.cs index ce53adf..ed01cf3 100644 --- a/Scripts/Runtime/Core/ScriptableObjectCollection.cs +++ b/Scripts/Runtime/Core/ScriptableObjectCollection.cs @@ -197,6 +197,16 @@ public ISOCItem GetOrAddNewBaseItem(string targetName) return AddNewBaseItem(targetName); } + + public ISOCItem GetOrAddNew(Type collectionType, string targetName) + { + ISOCItem item = Items.FirstOrDefault(o => o.name.Equals(targetName, StringComparison.Ordinal)) as ISOCItem; + if (item != null) + return item; + + return (ISOCItem) AddNew(collectionType, targetName); + } + #endif public virtual Type GetItemType() @@ -478,14 +488,6 @@ public T GetOrAddNew(string targetName = null) where T : TObjectType return (T) AddNew(typeof(T), targetName); } - public TObjectType GetOrAddNew(Type collectionType, string targetName) - { - TObjectType item = Items.FirstOrDefault(o => o.name.Equals(targetName, StringComparison.Ordinal)) as TObjectType; - if (item != null) - return item; - - return (TObjectType) AddNew(collectionType, targetName); - } public TObjectType GetOrAddNew(string targetName) { From b674eaa3c7b41f8c5299b30eed79b0680c908011 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Sun, 27 Aug 2023 13:51:55 +0200 Subject: [PATCH 21/23] Fixed logic for removing items that weren't re-generated. --- Scripts/Editor/Generators/CollectionGenerators.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index daf03d4..0d7291a 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -149,15 +149,18 @@ private static void RunGeneratorInternal( { case GeneratorItemToTemplateMatchingBehaviours.MatchByName: // Remove any items for which there isn't a template by the same name. + bool foundItemOfSameName = false; for (int j = 0; j < templates.Count; j++) { ItemTemplate itemTemplate = (ItemTemplate)templates[j]; if (collection.Items[i].name == itemTemplate.name) { - shouldRemoveItem = true; + foundItemOfSameName = true; break; } } + if (!foundItemOfSameName) + shouldRemoveItem = true; break; case GeneratorItemToTemplateMatchingBehaviours.MatchByIndex: // Remove any items beyond the size of the list of templates that were returned. @@ -167,7 +170,7 @@ private static void RunGeneratorInternal( throw new ArgumentOutOfRangeException(); } - if (!shouldRemoveItem) + if (shouldRemoveItem) { // No corresponding template existed, so remove this item. ScriptableObject itemToRemove = collection.Items[i]; From fc4de6df54b050e56550e1091136531b5babfb48 Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Sun, 27 Aug 2023 14:24:19 +0200 Subject: [PATCH 22/23] Dropped support for Match by Index. Not as useful as I thought. --- .../CustomEditors/CollectionCustomEditor.cs | 15 +---- .../Editor/Generators/CollectionGenerators.cs | 60 ++++--------------- ...neratorItemToTemplateMatchingBehaviours.cs | 8 --- ...orItemToTemplateMatchingBehaviours.cs.meta | 3 - .../IScriptableObjectCollectionGenerator.cs | 9 --- 5 files changed, 13 insertions(+), 82 deletions(-) delete mode 100644 Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs delete mode 100644 Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs.meta diff --git a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs index b0330e2..02e09a9 100644 --- a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs +++ b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs @@ -37,20 +37,7 @@ public class CollectionCustomEditor : BaseEditor private bool IsAutoGenerated => generatorType != null; - protected virtual bool CanBeReorderable - { - get - { - // If this is a generated collection and it uses the order of items to match newly generated templates - // to previously existing items, don't let the list be reordered. It just allows you to break things. - if (IsAutoGenerated && generator.ItemToTemplateMatchingBehaviour - == GeneratorItemToTemplateMatchingBehaviours.MatchByIndex) - { - return false; - } - return true; - } - } + protected virtual bool CanBeReorderable => true; protected virtual bool DisplayAddButton { diff --git a/Scripts/Editor/Generators/CollectionGenerators.cs b/Scripts/Editor/Generators/CollectionGenerators.cs index 0d7291a..18d83bc 100644 --- a/Scripts/Editor/Generators/CollectionGenerators.cs +++ b/Scripts/Editor/Generators/CollectionGenerators.cs @@ -128,13 +128,7 @@ private static void RunGeneratorInternal( MethodInfo getItemTemplatesMethod = generatorType.GetMethod( "GetItemTemplates", BindingFlags.Public | BindingFlags.Instance); getItemTemplatesMethod.Invoke(generator, new object[] {templates, collection}); - - // Figure out the intended behaviour for finding existing items. - GeneratorItemToTemplateMatchingBehaviours itemToTemplateMatchingBehaviour = - (GeneratorItemToTemplateMatchingBehaviours)generatorType - .GetProperty("ItemToTemplateMatchingBehaviour", BindingFlags.Public | BindingFlags.Instance) - .GetValue(generator); - + // If necessary, first remove any items that weren't re-generated. bool shouldRemoveNonGeneratedItems = (bool)generatorType .GetProperty("ShouldRemoveNonGeneratedItems", BindingFlags.Public | BindingFlags.Instance) @@ -145,32 +139,18 @@ private static void RunGeneratorInternal( { bool shouldRemoveItem = false; - switch (itemToTemplateMatchingBehaviour) + // Remove any items for which there isn't a template by the same name. + bool foundItemOfSameName = false; + for (int j = 0; j < templates.Count; j++) { - case GeneratorItemToTemplateMatchingBehaviours.MatchByName: - // Remove any items for which there isn't a template by the same name. - bool foundItemOfSameName = false; - for (int j = 0; j < templates.Count; j++) - { - ItemTemplate itemTemplate = (ItemTemplate)templates[j]; - if (collection.Items[i].name == itemTemplate.name) - { - foundItemOfSameName = true; - break; - } - } - if (!foundItemOfSameName) - shouldRemoveItem = true; + ItemTemplate itemTemplate = (ItemTemplate)templates[j]; + if (collection.Items[i].name == itemTemplate.name) + { + foundItemOfSameName = true; break; - case GeneratorItemToTemplateMatchingBehaviours.MatchByIndex: - // Remove any items beyond the size of the list of templates that were returned. - shouldRemoveItem = i >= templates.Count; - break; - default: - throw new ArgumentOutOfRangeException(); + } } - - if (shouldRemoveItem) + if (!foundItemOfSameName) { // No corresponding template existed, so remove this item. ScriptableObject itemToRemove = collection.Items[i]; @@ -192,24 +172,8 @@ private static void RunGeneratorInternal( if (!TryGetItemTemplateType(itemTemplate, out Type templateItemType)) templateItemType = collection.GetItemType(); - ISOCItem itemInstance; - switch (itemToTemplateMatchingBehaviour) - { - case GeneratorItemToTemplateMatchingBehaviours.MatchByName: - { - itemInstance = collection.GetOrAddNew(templateItemType, itemTemplate.name); - break; - } - case GeneratorItemToTemplateMatchingBehaviours.MatchByIndex: - { - itemInstance = i < collection.Items.Count ? - (ISOCItem)collection.Items[i] : collection.AddNew(templateItemType, itemTemplate.name) as ISOCItem; - break; - } - default: - throw new ArgumentOutOfRangeException(); - } - + ISOCItem itemInstance = collection.GetOrAddNew(templateItemType, itemTemplate.name); + CopyFieldsFromTemplateToItem(itemTemplate, itemInstance); } diff --git a/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs b/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs deleted file mode 100644 index ec7d51d..0000000 --- a/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace BrunoMikoski.ScriptableObjectCollections -{ - public enum GeneratorItemToTemplateMatchingBehaviours - { - MatchByName, - MatchByIndex, - } -} diff --git a/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs.meta b/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs.meta deleted file mode 100644 index 78c22b8..0000000 --- a/Scripts/Editor/Generators/GeneratorItemToTemplateMatchingBehaviours.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 68d7f0738fe147f684563b1a64fdbffe -timeCreated: 1693081431 \ No newline at end of file diff --git a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs index 40ba1a6..437e175 100644 --- a/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs +++ b/Scripts/Editor/Generators/IScriptableObjectCollectionGenerator.cs @@ -10,15 +10,6 @@ public interface IScriptableObjectCollectionGeneratorBase /// Return false if you want to generate items but allow people to manually add items of their own. /// bool ShouldRemoveNonGeneratedItems { get; } - - /// - /// How to match existing items to templates. Matching them by name allows you to reorder the items and still - /// find the same item it belonged to, matching them by index doesn't handle reordering well but it lets you - /// rename them very easily. Matching by name makes more sense for items generated based on scenes, which tend - /// to come in at a different order every time, but when generating items for tags it makes more sense to do it - /// by index because you're not allowed to reorder tags in the first place and that way it handles renames well. - /// - GeneratorItemToTemplateMatchingBehaviours ItemToTemplateMatchingBehaviour { get; } } /// From 4a2bdedc677e32df34c5765d5ec577b38a61d3ca Mon Sep 17 00:00:00 2001 From: Roy Theunissen Date: Sun, 27 Aug 2023 14:40:36 +0200 Subject: [PATCH 23/23] Added a utility for renaming an item. --- .../Runtime/Core/ScriptableObjectCollection.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Scripts/Runtime/Core/ScriptableObjectCollection.cs b/Scripts/Runtime/Core/ScriptableObjectCollection.cs index ed01cf3..25f8de0 100644 --- a/Scripts/Runtime/Core/ScriptableObjectCollection.cs +++ b/Scripts/Runtime/Core/ScriptableObjectCollection.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using UnityEngine; +using Object = UnityEngine.Object; #if UNITY_EDITOR using UnityEditor; #endif @@ -207,6 +208,20 @@ public ISOCItem GetOrAddNew(Type collectionType, string targetName) return (ISOCItem) AddNew(collectionType, targetName); } + public static void Rename(ISOCItem item, string newName) + { + string path = AssetDatabase.GetAssetPath(item as Object); + + // If the new name includes the full directory path or the wrong extension, get rid of that. + newName = Path.GetFileNameWithoutExtension(newName); + + // Make sure the correct extension is included. + const string extension = ".asset"; + if (!newName.EndsWith(extension)) + newName += extension; + + AssetDatabase.RenameAsset(path, newName); + } #endif public virtual Type GetItemType()