diff --git a/Assets/Example/Example.unity b/Assets/Example/Example.unity index 50af01c..09287f1 100644 --- a/Assets/Example/Example.unity +++ b/Assets/Example/Example.unity @@ -13,7 +13,7 @@ OcclusionCullingSettings: --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 - serializedVersion: 9 + serializedVersion: 10 m_Fog: 0 m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} m_FogMode: 3 @@ -38,13 +38,12 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 serializedVersion: 12 - m_GIWorkflowMode: 1 m_GISettings: serializedVersion: 2 m_BounceScale: 1 @@ -67,9 +66,6 @@ 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 @@ -104,7 +100,7 @@ NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 2 + serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -117,7 +113,7 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: @@ -163,9 +159,17 @@ 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 @@ -199,13 +203,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: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &586792171 GameObject: @@ -326,13 +330,70 @@ 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_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &994260143 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 994260145} + - component: {fileID: 994260144} + m_Layer: 0 + m_Name: Example_Generics + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &994260144 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 994260143} + m_Enabled: 1 + m_EditorHideFlags: 0 + 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 + m_CorrespondingSourceObject: {fileID: 0} + 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: @@ -359,9 +420,8 @@ Light: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1127138992} m_Enabled: 1 - serializedVersion: 10 + serializedVersion: 11 m_Type: 1 - m_Shape: 0 m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} m_Intensity: 1 m_Range: 10 @@ -420,11 +480,19 @@ 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: 1 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/Example/Example_Generics.cs b/Assets/Example/Example_Generics.cs new file mode 100644 index 0000000..bc4f604 --- /dev/null +++ b/Assets/Example/Example_Generics.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +public interface IActor { } +public interface IStandardActor : IActor { } +public interface INetworkActor : IActor { } + +public interface IContravarianceAction where T : IActor { + void DoAction (T actor); +} + +public interface ICovarianceAction where T : IActor +{ + T Actor { get; } +} + +public interface IActorAction : IContravarianceAction, ICovarianceAction { } +public interface IStandardActorAction : IContravarianceAction, ICovarianceAction { } +public interface INetworkActorAction : IContravarianceAction, ICovarianceAction { } + +[Serializable] +public sealed class StandardActorAction : IContravarianceAction, ICovarianceAction +{ + public void DoAction (IStandardActor actor) + { + } + public IStandardActor Actor => null; +} + +[Serializable] +public sealed class ActorAction : IContravarianceAction, ICovarianceAction +{ + public void DoAction (IActor actor) + { + } + public IActor Actor => null; +} + +[Serializable] +public abstract class BaseAction : IContravarianceAction, ICovarianceAction where T : IActor +{ + public void DoAction (T actor) { + } + public T Actor => default; +} + +[Serializable] +public sealed class DerivedAction1 : BaseAction { } + +[Serializable] +public sealed class DerivedAction2 : BaseAction { } + +[Serializable] +public sealed class DerivedAction3 : BaseAction { } + +[Serializable] +public sealed class NetworkActorAction1 : INetworkActorAction +{ + public void DoAction (INetworkActor actor) + { + } + public INetworkActor Actor => null; +} + +[Serializable] +public sealed class NetworkActorAction2 : IContravarianceAction, ICovarianceAction +{ + public void DoAction (INetworkActor actor) + { + } + public INetworkActor Actor => null; +} + +[Serializable] +public sealed class NetworkActorAction3 : IContravarianceAction, ICovarianceAction +{ + public void DoAction (IActor actor) + { + } + public IActor Actor => null; +} + +public class Example_Generics : MonoBehaviour +{ + + [SerializeReference, SubclassSelector] + public List> contravarianceActions = new List>(); + + [SerializeReference, SubclassSelector] + public List> covarianceActions = new List>(); + +} diff --git a/Assets/Example/Example_Generics.cs.meta b/Assets/Example/Example_Generics.cs.meta new file mode 100644 index 0000000..e533ce3 --- /dev/null +++ b/Assets/Example/Example_Generics.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dff76005e1dfac84287448b12c4b160e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs index 3de6ce1..e9b9953 100644 --- a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/SubclassSelectorDrawer.cs @@ -149,7 +149,7 @@ namespace MackySoft.SerializeReferenceExtensions.Editor Type baseType = ManagedReferenceUtility.GetType(managedReferenceFieldTypename); var popup = new AdvancedTypePopup( - TypeMenuUtility.GetTypes(baseType), + TypeSearch.GetTypes(baseType), k_MaxTypePopupLineCount, state ); diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs index 28d74b3..354d9d4 100644 --- a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeMenuUtility.cs @@ -3,23 +3,11 @@ using System.Linq; using System.Collections.Generic; using UnityEditor; -namespace MackySoft.SerializeReferenceExtensions.Editor { +namespace MackySoft.SerializeReferenceExtensions.Editor +{ public static class TypeMenuUtility { public const string k_NullDisplayName = ""; - static readonly Type k_UnityObjectType = typeof(UnityEngine.Object); - - public static IEnumerable GetTypes (Type baseType) - { - return TypeCache.GetTypesDerivedFrom(baseType).Append(baseType).Where(p => - (p.IsPublic || p.IsNestedPublic || p.IsNestedPrivate) && - !p.IsAbstract && - !p.IsGenericType && - !k_UnityObjectType.IsAssignableFrom(p) && - Attribute.IsDefined(p, typeof(SerializableAttribute)) && - !Attribute.IsDefined(p, typeof(HideInTypeMenuAttribute)) - ); - } public static AddTypeMenuAttribute GetAttribute (Type type) { return Attribute.GetCustomAttribute(type,typeof(AddTypeMenuAttribute)) as AddTypeMenuAttribute; diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs new file mode 100644 index 0000000..7b6bd97 --- /dev/null +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs @@ -0,0 +1,142 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using UnityEditor; +using System.Reflection; + +namespace MackySoft.SerializeReferenceExtensions.Editor +{ + public static class TypeSearch + { + +#if UNITY_2023_2_OR_NEWER + static readonly Dictionary> m_TypeCache = new Dictionary>(); +#endif + + public static IEnumerable GetTypes (Type baseType) + { +#if UNITY_2023_2_OR_NEWER + // NOTE: This is a generics solution for Unity 2023.2 and later. + // 2023.2 because SerializeReference supports generic type instances and because the behaviour is stable. + if (baseType.IsGenericType) + { + return GetTypesWithGeneric(baseType); + } + else + { + return GetTypesUsingTypeCache(baseType); + } +#else + return GetTypesUsingTypeCache(baseType); +#endif + } + + static IEnumerable GetTypesUsingTypeCache (Type baseType) + { + return TypeCache.GetTypesDerivedFrom(baseType) + .Append(baseType) + .Where(IsValidType); + } + +#if UNITY_2023_2_OR_NEWER + static IEnumerable GetTypesWithGeneric (Type baseType) + { + if (m_TypeCache.TryGetValue(baseType, out List result)) + { + return result; + } + + result = new List(); + Type genericTypeDefinition = null; + Type[] targetTypeArguments = null; + Type[] genericTypeParameters = null; + + if (baseType.IsGenericType) + { + genericTypeDefinition = baseType.GetGenericTypeDefinition(); + targetTypeArguments = baseType.GetGenericArguments(); + genericTypeParameters = genericTypeDefinition.GetGenericArguments(); + } + else + { + genericTypeDefinition = baseType; + targetTypeArguments = Type.EmptyTypes; + genericTypeParameters = Type.EmptyTypes; + } + + IEnumerable types = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(x => x.GetTypes()) + .Where(IsValidType); + + foreach (Type type in types) + { + Type[] interfaceTypes = type.GetInterfaces(); + foreach (Type interfaceType in interfaceTypes) + { + if (!interfaceType.IsGenericType || interfaceType.GetGenericTypeDefinition() != genericTypeDefinition) + { + continue; + } + + Type[] sourceTypeArguments = interfaceType.GetGenericArguments(); + + bool allParametersMatch = true; + + for (int i = 0; i < genericTypeParameters.Length; i++) + { + var variance = genericTypeParameters[i].GenericParameterAttributes & GenericParameterAttributes.VarianceMask; + + Type sourceTypeArgument = sourceTypeArguments[i]; + Type targetTypeArgument = targetTypeArguments[i]; + + if (variance == GenericParameterAttributes.Contravariant) + { + if (!sourceTypeArgument.IsAssignableFrom(targetTypeArgument)) + { + allParametersMatch = false; + break; + } + } + else if (variance == GenericParameterAttributes.Covariant) + { + if (!targetTypeArgument.IsAssignableFrom(sourceTypeArgument)) + { + allParametersMatch = false; + break; + } + } + else + { + if (sourceTypeArgument != targetTypeArgument) + { + allParametersMatch = false; + break; + } + } + } + + if (allParametersMatch) + { + result.Add(type); + break; + } + } + } + + m_TypeCache.Add(baseType, result); + return result; + } +#endif + + static bool IsValidType (Type type) + { + return + (type.IsPublic || type.IsNestedPublic || type.IsNestedPrivate) && + !type.IsAbstract && + !type.IsGenericType && + !typeof(UnityEngine.Object).IsAssignableFrom(type) && + Attribute.IsDefined(type, typeof(SerializableAttribute)) && + !Attribute.IsDefined(type, typeof(HideInTypeMenuAttribute)); + } + } +} \ No newline at end of file diff --git a/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs.meta b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs.meta new file mode 100644 index 0000000..4198c09 --- /dev/null +++ b/Assets/MackySoft/MackySoft.SerializeReferenceExtensions/Editor/TypeSearch.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5ea24172520aa2646954c1d246e7ba5d \ No newline at end of file