diff --git a/Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs b/Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs new file mode 100644 index 0000000..d2b0c13 --- /dev/null +++ b/Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs @@ -0,0 +1,107 @@ +using PashaBibko.Pacore.Shared.Attributes; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using System; + +namespace PashaBibko.Pacore.Editor.Drawers +{ + public static class MonoScriptCache + { + private static Dictionary Cache { get; } = new(); + + static MonoScriptCache() + { + /* Finds all MonoScripts and adds them to the dictionary by name */ + MonoScript[] scripts = Resources.FindObjectsOfTypeAll(); + foreach (MonoScript script in scripts) + { + Type scriptType = script.GetClass(); + if (scriptType is not null) + { + string name = scriptType.FullName; + Cache.TryAdd(name, script); + } + } + } + + public static MonoScript Get(string name) + { + /* Fetches the value (if there is one) without creating a default val */ + Cache.TryGetValue(name, out MonoScript script); + return script; + } + } + + [CustomPropertyDrawer(typeof(MonoScriptAttribute))] + public class MonoScriptDrawer : PropertyDrawer + { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + /* I'm not sure if this will ever happen */ + if (attribute is not MonoScriptAttribute attr) + { + return; // Stops NullReferenceExceptions + } + + /* Makes sure the type is a string */ + if (property.propertyType != SerializedPropertyType.String) + { + Debug.LogError($"Attribute [{nameof(MonoScriptAttribute)}] must be attached to a string"); + return; + } + + /* Draws the label of the script variable */ + Rect rect = EditorGUI.PrefixLabel(position, label); + + /* Fetches the script if there is a value assigned to the property */ + string propValue = property.stringValue; + MonoScript script = null; + if (!string.IsNullOrEmpty(propValue)) + { + script = MonoScriptCache.Get(propValue); + } + + /* Draws the selected script and checks for changes */ + script = EditorGUI.ObjectField(rect, script, typeof(MonoScript), allowSceneObjects: false) as MonoScript; + if (!GUI.changed) + { + return; // No changes to check + } + + /* Makes sure the script is valid */ + if (script is null) + { + property.stringValue = string.Empty; // No type means empty string + return; + } + + /* Fetches the type of the attached script, and checks if it is valid */ + Type type = script.GetClass(); + if (type is null || (attr.MonoType is not null && !attr.MonoType.IsAssignableFrom(type))) + { + Debug.LogError($"The script file [{script.name}] doesn't contain an assignable MonoBehaviour class"); + return; + } + + /* If a forced inheritance has been set, checks its validity */ + if (attr.InheritedFrom is not null && !attr.InheritedFrom.IsAssignableFrom(type)) + { + property.stringValue = type.FullName; // Still applies the changes to make it appear like it is functioning + + string attachedObject = property.serializedObject.targetObject.name; + string inherited = attr.InheritedFrom.FullName; + + Debug.LogError + ( + $"Field [{property.name}] as part of [{attachedObject}] is invalid.\n" + + $"The type must inherit from [{inherited}]. Currently it is [{type.FullName}]" + ); + return; + } + + /* Assigns the name of the type to the property so it can be created */ + property.stringValue = type.FullName; + } + } +} \ No newline at end of file diff --git a/Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs.meta b/Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs.meta new file mode 100644 index 0000000..d46e0e6 --- /dev/null +++ b/Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1274bfae0f704d379ae7f2c80bb8aaaf +timeCreated: 1769262124 \ No newline at end of file diff --git a/Assets/Pacore/Shared/Attributes/MonoScriptAttribute.cs b/Assets/Pacore/Shared/Attributes/MonoScriptAttribute.cs new file mode 100644 index 0000000..0c43277 --- /dev/null +++ b/Assets/Pacore/Shared/Attributes/MonoScriptAttribute.cs @@ -0,0 +1,24 @@ +using System; +using UnityEngine; + +namespace PashaBibko.Pacore.Shared.Attributes +{ + [AttributeUsage(AttributeTargets.Field)] + public class MonoScriptAttribute : PropertyAttribute + { + public Type InheritedFrom { get; } + public Type MonoType { get; } + + public MonoScriptAttribute(Type inherited, Type type = null) + { + InheritedFrom = inherited; + MonoType = type; + } + + public MonoScriptAttribute(Type type = null) + { + InheritedFrom = null; + MonoType = type; + } + } +} \ No newline at end of file diff --git a/Assets/Pacore/Shared/Attributes/MonoScriptAttribute.cs.meta b/Assets/Pacore/Shared/Attributes/MonoScriptAttribute.cs.meta new file mode 100644 index 0000000..87aaebc --- /dev/null +++ b/Assets/Pacore/Shared/Attributes/MonoScriptAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5c8bb7579c6d4fd69d3dfe51ad980516 +timeCreated: 1769261125 \ No newline at end of file diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 6a2cce6..1433e06 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -273,6 +273,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: go: {fileID: 0} + Spawnable: PashaBibko.Pacore.Editor.Drawers.DetectInspectorChangesDrawer Test: 0 --- !u!1 &410087039 GameObject: diff --git a/Assets/TestMonoBehaviour.cs b/Assets/TestMonoBehaviour.cs index ba90daf..896eeda 100644 --- a/Assets/TestMonoBehaviour.cs +++ b/Assets/TestMonoBehaviour.cs @@ -4,12 +4,20 @@ using UnityEngine; public class TestMonoBehaviour : MonoBehaviour { [InspectorReadOnly, SerializeField] private GameObject go; + + [MonoScript(inherited: typeof(MonoBehaviour))] public string Spawnable; [DetectInspectorChanges("OnTestChange")] public int Test; private void OnTestChange() => LogTestValue(); + [InspectorCallable(nameof(LogSpawnableType))] + public void LogSpawnableType() + { + Debug.Log(Spawnable); + } + [InspectorCallable("Test button")] public void LogTestValue() { Debug.Log($"Test value [{Test}]");