From 04048a4c0c42d6a244a2fd522df93a8f075d1729 Mon Sep 17 00:00:00 2001 From: DhafinFawwaz Date: Wed, 18 Dec 2024 01:04:34 +0700 Subject: [PATCH] feat: new attribute AutoTypeMenuAttribute, caching, and example --- Assets/Example/Example.cs | 79 +++++++++++++++++ Assets/Example/Example.unity | 84 ++++++++----------- .../Editor/AdvancedTypePopup.cs | 20 +++-- .../Editor/SubclassSelectorDrawer.cs | 1 + .../Editor/TypeMenuUtility.cs | 7 +- .../Runtime/AutoTypeMenuAttribute.cs | 44 ++++++++++ .../Runtime/AutoTypeMenuAttribute.cs.meta | 11 +++ 7 files changed, 188 insertions(+), 58 deletions(-) create mode 100644 Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Runtime/AutoTypeMenuAttribute.cs create mode 100644 Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Runtime/AutoTypeMenuAttribute.cs.meta diff --git a/Assets/Example/Example.cs b/Assets/Example/Example.cs index 94a7acc..7ad9f7b 100644 --- a/Assets/Example/Example.cs +++ b/Assets/Example/Example.cs @@ -52,6 +52,85 @@ public Banana () } } +[Serializable] +public class Snack : Food +{ + public Snack () + { + name = "Snack"; + kcal = 100f; + } +} +[Serializable] +public class Candy : Snack +{ + public Candy () + { + name = "Candy"; + kcal = 100f; + } +} + +[Serializable, AutoTypeMenu] +public class Chocolate : Snack +{ + public Chocolate () + { + name = "Chocolate"; + kcal = 100f; + } +} + +[Serializable] +public class ChocolateBar : Chocolate +{ + public ChocolateBar () + { + name = "ChocolateBar"; + kcal = 100f; + } +} + +[Serializable] +public class ChocolateIceCream : Chocolate +{ + public ChocolateIceCream () + { + name = "ChocolateIceCream"; + kcal = 100f; + } +} + +[Serializable] +public class ChocolateBean : Chocolate +{ + public ChocolateBean () + { + name = "ChocolateBean"; + kcal = 100f; + } +} + +[Serializable] +public class ChocolateBeanAlmond : ChocolateBean +{ + public ChocolateBeanAlmond () + { + name = "ChocolateBeanAlmond"; + kcal = 100f; + } +} + +[Serializable] +public class ChocolateBeanHazelnut : ChocolateBean +{ + public ChocolateBeanHazelnut () + { + name = "ChocolateBeanHazelnut"; + kcal = 100f; + } +} + public class Example : MonoBehaviour { diff --git a/Assets/Example/Example.unity b/Assets/Example/Example.unity index 09287f1..fbab794 100644 --- a/Assets/Example/Example.unity +++ b/Assets/Example/Example.unity @@ -13,7 +13,7 @@ OcclusionCullingSettings: --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 - serializedVersion: 10 + serializedVersion: 9 m_Fog: 0 m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} m_FogMode: 3 @@ -38,12 +38,13 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 serializedVersion: 12 + m_GIWorkflowMode: 1 m_GISettings: serializedVersion: 2 m_BounceScale: 1 @@ -66,6 +67,9 @@ LightmapSettings: m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 m_ReflectionCompression: 2 m_MixedBakeMode: 2 m_BakeBackend: 1 @@ -100,7 +104,7 @@ NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 3 + serializedVersion: 2 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -113,7 +117,7 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - buildHeightMesh: 0 + accuratePlacement: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: @@ -159,17 +163,9 @@ Camera: m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 - m_Iso: 200 - m_ShutterSpeed: 0.005 - m_Aperture: 16 - m_FocusDistance: 10 - m_FocalLength: 50 - m_BladeCount: 5 - m_Curvature: {x: 2, y: 11} - m_BarrelClipping: 0.25 - m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 @@ -203,13 +199,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 126803971} - serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 1, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &586792171 GameObject: @@ -251,18 +247,34 @@ MonoBehaviour: foodTwo: rid: 9020349853700980767 foodThree: - rid: 9020349853700980768 + rid: 5331542178295447552 foodsOne: - rid: 9020349853700980769 - rid: 9020349853700980770 - rid: 9020349853700980771 foodsTwo: - rid: 9020349853700980772 - - rid: 9020349853700980773 + - rid: 5331542178476589060 - rid: 9020349853700980774 + - rid: 5331542178476589059 references: version: 2 RefIds: + - rid: 5331542178295447552 + type: {class: Grape, ns: , asm: Assembly-CSharp} + data: + name: Grape + kcal: 100 + - rid: 5331542178476589059 + type: {class: ChocolateBean, ns: , asm: Assembly-CSharp} + data: + name: ChocolateBar + kcal: 100 + - rid: 5331542178476589060 + type: {class: Grape, ns: , asm: Assembly-CSharp} + data: + name: Peach + kcal: 100 - rid: 9020349853700980763 type: {class: Apple, ns: , asm: Assembly-CSharp} data: @@ -288,11 +300,6 @@ MonoBehaviour: data: name: Peach kcal: 100 - - rid: 9020349853700980768 - type: {class: Grape, ns: , asm: Assembly-CSharp} - data: - name: Grape - kcal: 100 - rid: 9020349853700980769 type: {class: Apple, ns: , asm: Assembly-CSharp} data: @@ -313,11 +320,6 @@ MonoBehaviour: data: name: Apple kcal: 100 - - rid: 9020349853700980773 - type: {class: Peach, ns: , asm: Assembly-CSharp} - data: - name: Peach - kcal: 100 - rid: 9020349853700980774 type: {class: Grape, ns: , asm: Assembly-CSharp} data: @@ -330,13 +332,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 586792171} - serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &994260143 GameObject: @@ -367,19 +369,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: dff76005e1dfac84287448b12c4b160e, type: 3} m_Name: m_EditorClassIdentifier: - contravarianceActions: - - rid: 3354424774140624943 - covarianceActions: - - rid: 3354424774140624944 - references: - version: 2 - RefIds: - - rid: 3354424774140624943 - type: {class: DerivedAction1, ns: , asm: Assembly-CSharp} - data: - - rid: 3354424774140624944 - type: {class: NetworkActorAction1, ns: , asm: Assembly-CSharp} - data: --- !u!4 &994260145 Transform: m_ObjectHideFlags: 0 @@ -387,13 +376,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 994260143} - serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1127138992 GameObject: @@ -420,8 +409,9 @@ Light: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1127138992} m_Enabled: 1 - serializedVersion: 11 + serializedVersion: 10 m_Type: 1 + m_Shape: 0 m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} m_Intensity: 1 m_Range: 10 @@ -480,19 +470,11 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1127138992} - serializedVersion: 2 m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} ---- !u!1660057539 &9223372036854775807 -SceneRoots: - m_ObjectHideFlags: 0 - m_Roots: - - {fileID: 126803974} - - {fileID: 1127138994} - - {fileID: 586792173} - - {fileID: 994260145} diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/AdvancedTypePopup.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/AdvancedTypePopup.cs index 04f769e..7065477 100644 --- a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/AdvancedTypePopup.cs +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/AdvancedTypePopup.cs @@ -24,7 +24,7 @@ public class AdvancedTypePopup : AdvancedDropdown { const int kMaxNamespaceNestCount = 16; - public static void AddTo (AdvancedDropdownItem root,IEnumerable types) { + public static void AddTo (AdvancedDropdownItem root,IEnumerable types, Type baseType) { int itemCount = 0; // Add null item. @@ -39,10 +39,18 @@ public static void AddTo (AdvancedDropdownItem root,IEnumerable types) { bool isSingleNamespace = true; string[] namespaces = new string[kMaxNamespaceNestCount]; foreach (Type type in typeArray) { - string[] splittedTypePath = TypeMenuUtility.GetSplittedTypePath(type); + string[] splittedTypePath = TypeMenuUtility.GetSplittedTypePath(type, baseType); + + // If any type has AutoTypeMenuAttribute, it is not single namespace. + if(Attribute.GetCustomAttribute(type, typeof(AutoTypeMenuAttribute)) as AutoTypeMenuAttribute != null) { + isSingleNamespace = false; + break; + } + if (splittedTypePath.Length <= 1) { continue; } + // If they explicitly want sub category, let them do. if (TypeMenuUtility.GetAttribute(type) != null) { isSingleNamespace = false; @@ -66,7 +74,7 @@ public static void AddTo (AdvancedDropdownItem root,IEnumerable types) { // Add type items. foreach (Type type in typeArray) { - string[] splittedTypePath = TypeMenuUtility.GetSplittedTypePath(type); + string[] splittedTypePath = TypeMenuUtility.GetSplittedTypePath(type, baseType); if (splittedTypePath.Length == 0) { continue; } @@ -110,12 +118,14 @@ static AdvancedDropdownItem GetItem (AdvancedDropdownItem parent,string name) { static readonly float k_HeaderHeight = EditorGUIUtility.singleLineHeight * 2f; Type[] m_Types; + Type m_BaseType; public event Action OnItemSelected; - public AdvancedTypePopup (IEnumerable types,int maxLineCount,AdvancedDropdownState state) : base(state) { + public AdvancedTypePopup (IEnumerable types, Type baseType,int maxLineCount,AdvancedDropdownState state) : base(state) { SetTypes(types); minimumSize = new Vector2(minimumSize.x,EditorGUIUtility.singleLineHeight * maxLineCount + k_HeaderHeight); + m_BaseType = baseType; } public void SetTypes (IEnumerable types) { @@ -124,7 +134,7 @@ public void SetTypes (IEnumerable types) { protected override AdvancedDropdownItem BuildRoot () { var root = new AdvancedDropdownItem("Select Type"); - AddTo(root,m_Types); + AddTo(root,m_Types,m_BaseType); return root; } diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs index f87f59c..756894e 100644 --- a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs @@ -152,6 +152,7 @@ TypePopupCache GetTypePopup (SerializedProperty property) { Type baseType = ManagedReferenceUtility.GetType(managedReferenceFieldTypename); var popup = new AdvancedTypePopup( TypeSearch.GetTypes(baseType), + baseType, k_MaxTypePopupLineCount, state ); diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs index 354d9d4..b5e6557 100644 --- a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Collections.Generic; using UnityEditor; +using System.Diagnostics; namespace MackySoft.SerializeReferenceExtensions.Editor { @@ -13,11 +14,13 @@ public static AddTypeMenuAttribute GetAttribute (Type type) { return Attribute.GetCustomAttribute(type,typeof(AddTypeMenuAttribute)) as AddTypeMenuAttribute; } - public static string[] GetSplittedTypePath (Type type) { + public static string[] GetSplittedTypePath (Type type, Type baseType) { AddTypeMenuAttribute typeMenu = GetAttribute(type); if (typeMenu != null) { return typeMenu.GetSplittedMenuName(); - } else { + } else if(AutoTypeMenuAttribute.TryGetPathIfThisOrAnyParentContainAutoTypeMenuAttribute(type, baseType, out string[] path)) { + return path; + } else { int splitIndex = type.FullName.LastIndexOf('.'); if (splitIndex >= 0) { return new string[] { type.FullName.Substring(0,splitIndex),type.FullName.Substring(splitIndex + 1) }; diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Runtime/AutoTypeMenuAttribute.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Runtime/AutoTypeMenuAttribute.cs new file mode 100644 index 0000000..560856f --- /dev/null +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Runtime/AutoTypeMenuAttribute.cs @@ -0,0 +1,44 @@ + +using System; +using System.Collections.Generic; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] +public sealed class AutoTypeMenuAttribute : Attribute +{ + public AutoTypeMenuAttribute(){} + + static Dictionary _cachePath = new (); + + public static bool TryGetPathIfThisOrAnyParentContainAutoTypeMenuAttribute(Type type, Type baseType, out string[] path) { + if (_cachePath.TryGetValue(type, out (string[], bool) cachedPath)) { + path = cachedPath.Item1; + return cachedPath.Item2; + } + + Type currentType = type; + Stack pathStack = new(); + while (currentType != null) { + if(currentType != baseType) pathStack.Push(currentType.Name); + AutoTypeMenuAttribute autoPath = Attribute.GetCustomAttribute(currentType, typeof(AutoTypeMenuAttribute)) as AutoTypeMenuAttribute; + if (autoPath != null) { + path = StackToReversedArray(pathStack); + _cachePath[type] = (path, true); + return true; + } + currentType = currentType.BaseType; + } + _cachePath[type] = (null, false); + path = null; + return false; + } + + static string[] StackToReversedArray(Stack stack) { + string[] arr = new string[stack.Count]; + int i = 0; + foreach (var item in stack) { + arr[i] = item; + i++; + } + return arr; + } +} \ No newline at end of file diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Runtime/AutoTypeMenuAttribute.cs.meta b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Runtime/AutoTypeMenuAttribute.cs.meta new file mode 100644 index 0000000..9ade4d3 --- /dev/null +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Runtime/AutoTypeMenuAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 10a16afb0a33e5a438cdaaaa8a023877 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: