diff --git a/Assets/NaughtyAttributes/Samples/DemoScene/DemoScene.unity b/Assets/NaughtyAttributes/Samples/DemoScene/DemoScene.unity index 3c134139..492bd73e 100644 --- a/Assets/NaughtyAttributes/Samples/DemoScene/DemoScene.unity +++ b/Assets/NaughtyAttributes/Samples/DemoScene/DemoScene.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641258, b: 0.5748172, a: 1} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -364,6 +364,98 @@ MonoBehaviour: readOnlyFloat: 3.14 nest2: readOnlyString: +--- !u!1 &325876183 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 325876184} + - component: {fileID: 325876185} + m_Layer: 0 + m_Name: RequiredType + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &325876184 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 325876183} + 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_Children: [] + m_Father: {fileID: 1148579784} + m_RootOrder: 33 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &325876185 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 325876183} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ab31f5807f7c4f1bafb31e268ebf7b16, type: 3} + m_Name: + m_EditorClassIdentifier: + gameObjectMustHaveRigidbody: {fileID: 0} + transformMustHaveRigidbody: {fileID: 0} + gameObjectMustHaveInterface: {fileID: 0} + gameObjectMustHaveComponent: {fileID: 0} + shouldNotShowInfoMessageWhenEmpty: {fileID: 0} + gameObjectMustHaveMultipleType: {fileID: 0} +--- !u!1 &341717952 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 341717953} + - component: {fileID: 341717954} + m_Layer: 0 + m_Name: RequiredTypeTestObject2InheritsAll + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &341717953 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 341717952} + 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_Children: [] + m_Father: {fileID: 1148579784} + m_RootOrder: 35 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &341717954 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 341717952} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3b286cc1b60e4afe9a9a6b50a0210ab0, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &369789276 GameObject: m_ObjectHideFlags: 0 @@ -943,6 +1035,9 @@ Transform: - {fileID: 1706612702} - {fileID: 369789277} - {fileID: 1463483878} + - {fileID: 325876184} + - {fileID: 1333094825} + - {fileID: 341717953} m_Father: {fileID: 0} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -1183,6 +1278,49 @@ MonoBehaviour: int1: 0 nest2: vector2: {x: 0.25, y: 0.75} +--- !u!1 &1333094824 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1333094825} + - component: {fileID: 1333094826} + m_Layer: 0 + m_Name: RequiredTypeTestObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1333094825 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1333094824} + 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_Children: [] + m_Father: {fileID: 1148579784} + m_RootOrder: 34 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1333094826 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1333094824} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f1057b85cb7f4211be24e47f29b7f98d, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &1380469384 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/NaughtyAttributes/Scripts/Core/ValidatorAttributes/RequiredTypeAttribute.cs b/Assets/NaughtyAttributes/Scripts/Core/ValidatorAttributes/RequiredTypeAttribute.cs new file mode 100644 index 00000000..06e2f0e6 --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Core/ValidatorAttributes/RequiredTypeAttribute.cs @@ -0,0 +1,24 @@ +using System; + +namespace NaughtyAttributes +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)] + public class RequiredTypeAttribute : ValidatorAttribute + { + public Type[] RequiredTypes { get; private set; } + + public bool ShowInfoMessageWhenEmpty { get; private set; } + + public RequiredTypeAttribute(params Type[] requiredTypes) + { + RequiredTypes = requiredTypes; + ShowInfoMessageWhenEmpty = true; + } + + public RequiredTypeAttribute(bool showInfoMessageWhenEmpty, params Type[] requiredTypes) + { + RequiredTypes = requiredTypes; + ShowInfoMessageWhenEmpty = showInfoMessageWhenEmpty; + } + } +} \ No newline at end of file diff --git a/Assets/NaughtyAttributes/Scripts/Core/ValidatorAttributes/RequiredTypeAttribute.cs.meta b/Assets/NaughtyAttributes/Scripts/Core/ValidatorAttributes/RequiredTypeAttribute.cs.meta new file mode 100644 index 00000000..4d74b85c --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Core/ValidatorAttributes/RequiredTypeAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c195e25255d047588a414edcdca6c907 +timeCreated: 1668079150 \ No newline at end of file diff --git a/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/PropertyValidatorBase.cs b/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/PropertyValidatorBase.cs index 240cdb27..69f10ad3 100644 --- a/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/PropertyValidatorBase.cs +++ b/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/PropertyValidatorBase.cs @@ -20,6 +20,7 @@ static ValidatorAttributeExtensions() _validatorsByAttributeType[typeof(MaxValueAttribute)] = new MaxValuePropertyValidator(); _validatorsByAttributeType[typeof(RequiredAttribute)] = new RequiredPropertyValidator(); _validatorsByAttributeType[typeof(ValidateInputAttribute)] = new ValidateInputPropertyValidator(); + _validatorsByAttributeType[typeof(RequiredTypeAttribute)] = new RequiredTypePropertyValidator(); } public static PropertyValidatorBase GetValidator(this ValidatorAttribute attr) diff --git a/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/RequiredTypePropertyValidator.cs b/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/RequiredTypePropertyValidator.cs new file mode 100644 index 00000000..2141b8c8 --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/RequiredTypePropertyValidator.cs @@ -0,0 +1,99 @@ +using System.Text; +using UnityEditor; +using UnityEngine; + +namespace NaughtyAttributes.Editor +{ + public class RequiredTypePropertyValidator : PropertyValidatorBase + { + public override void ValidateProperty(SerializedProperty property) + { + RequiredTypeAttribute requiredTypeAttribute = PropertyUtility.GetAttribute(property); + + if (requiredTypeAttribute == null) + { + return; + } + + if (property.propertyType != SerializedPropertyType.ObjectReference) + { + NaughtyEditorGUI.HelpBox_Layout(requiredTypeAttribute.GetType().Name + " works only on reference types", + MessageType.Warning, + context: property.serializedObject.targetObject + ); + return; + } + + if (property.objectReferenceValue == null) + { + if (requiredTypeAttribute.ShowInfoMessageWhenEmpty) + { + StringBuilder infoMessage = new StringBuilder(); + infoMessage.AppendLine(property.name + " must have "); + + foreach (var baseType in requiredTypeAttribute.RequiredTypes) + { + infoMessage.AppendLine("\"" + baseType.FullName + "\""); + } + + infoMessage.Append(requiredTypeAttribute.RequiredTypes.Length > 1 + ? " or one of their derived types" + : " or derived type"); + + NaughtyEditorGUI.HelpBox_Layout(infoMessage.ToString(), MessageType.Info, + context: property.serializedObject.targetObject); + } + + return; + } + + if (HasGameObject(property.objectReferenceValue, out GameObject gameObject)) + { + bool hasValidationError = false; + StringBuilder errorMessage = new StringBuilder(); + + foreach (var baseType in requiredTypeAttribute.RequiredTypes) + { + var hasComponent = gameObject.GetComponent(baseType); + if (!hasComponent) + { + hasValidationError = true; + errorMessage.AppendLine(property.name + " must have \"" + baseType.FullName + "\" or derived type"); + } + } + + if (hasValidationError) + { + NaughtyEditorGUI.HelpBox_Layout(errorMessage.ToString(), MessageType.Error, + context: property.serializedObject.targetObject); + } + } + else + { + NaughtyEditorGUI.HelpBox_Layout( + requiredTypeAttribute.GetType().Name + " works only on types has GameObject", + MessageType.Warning, + context: property.serializedObject.targetObject + ); + } + } + + private bool HasGameObject(Object obj, out GameObject gameObject) + { + if (obj is GameObject go) + { + gameObject = go; + return true; + } + + if (obj is Component component) + { + gameObject = component.gameObject; + return gameObject != null; + } + + gameObject = null; + return false; + } + } +} \ No newline at end of file diff --git a/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/RequiredTypePropertyValidator.cs.meta b/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/RequiredTypePropertyValidator.cs.meta new file mode 100644 index 00000000..506d47f3 --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Editor/PropertyValidators/RequiredTypePropertyValidator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 59ed82e4a16344f78a2759fb74bf232b +timeCreated: 1668079329 \ No newline at end of file diff --git a/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTest.cs b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTest.cs new file mode 100644 index 00000000..0edcac50 --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTest.cs @@ -0,0 +1,36 @@ +using UnityEngine; + +namespace NaughtyAttributes.Test +{ + public interface IRequiredTypeTestInterface + { + } + + public interface IRequiredTypeTestInterface2 + { + } + + public class RequiredTypeTest : MonoBehaviour + { + [RequiredType(typeof(Rigidbody))] + public GameObject gameObjectMustHaveRigidbody; + + [RequiredType(typeof(Rigidbody))] + public Transform transformMustHaveRigidbody; + + [RequiredType(typeof(IRequiredTypeTestInterface))] + public GameObject gameObjectMustHaveInterface; + + [RequiredType(typeof(RequiredTypeTestTestObject))] + public GameObject gameObjectMustHaveComponent; + + [RequiredType(showInfoMessageWhenEmpty: false, typeof(IRequiredTypeTestInterface))] + public GameObject shouldNotShowInfoMessageWhenEmpty; + + [RequiredType(typeof(IRequiredTypeTestInterface), typeof(IRequiredTypeTestInterface2))] + public GameObject gameObjectMustHaveMultipleType; + + [RequiredType(typeof(RequiredTypeTestTestObject))] + public RequiredTypeTest componentMustHaveAnotherComponent; + } +} \ No newline at end of file diff --git a/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTest.cs.meta b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTest.cs.meta new file mode 100644 index 00000000..29ae6809 --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ab31f5807f7c4f1bafb31e268ebf7b16 +timeCreated: 1668080412 \ No newline at end of file diff --git a/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject.cs b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject.cs new file mode 100644 index 00000000..d60980f0 --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject.cs @@ -0,0 +1,8 @@ +using UnityEngine; + +namespace NaughtyAttributes.Test +{ + public class RequiredTypeTestTestObject : MonoBehaviour, IRequiredTypeTestInterface + { + } +} \ No newline at end of file diff --git a/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject.cs.meta b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject.cs.meta new file mode 100644 index 00000000..4b52a5fe --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f1057b85cb7f4211be24e47f29b7f98d +timeCreated: 1668081146 \ No newline at end of file diff --git a/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject2InheritsAll.cs b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject2InheritsAll.cs new file mode 100644 index 00000000..312b9c0d --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject2InheritsAll.cs @@ -0,0 +1,8 @@ +using UnityEngine; + +namespace NaughtyAttributes.Test +{ + public class RequiredTypeTestTestObject2InheritsAll : RequiredTypeTestTestObject, IRequiredTypeTestInterface2 + { + } +} \ No newline at end of file diff --git a/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject2InheritsAll.cs.meta b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject2InheritsAll.cs.meta new file mode 100644 index 00000000..03f7a8e0 --- /dev/null +++ b/Assets/NaughtyAttributes/Scripts/Test/RequiredTypeTestTestObject2InheritsAll.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3b286cc1b60e4afe9a9a6b50a0210ab0 +timeCreated: 1668082490 \ No newline at end of file