玩多了 Editor script, 就希望 tools 弄得更 flexible 一點,
把最常用的 Resource 弄一個 property drawer 出來.
直接把 string 用作 resourceFolder 的 path, 含 checking
先準備好基本的 structure.
namespace Kit.Resource { /// <summary>Resource folder attribute.</summary> [Serializable] public class ResourceFolderAttribute : PropertyAttribute { public readonly string title; public readonly string defaultName; public readonly string helpMessage; /// <summary>Initializes a new instance of the <see cref="Kit.Resource.ResourceFolderAttribute"/> class.</summary> /// <param name="title">Title of popup screen.</param> /// <param name="defaultName">Default name to search.</param> /// <param name="helpMessage">Help message, when not matching.</param> public ResourceFolderAttribute (string title, string defaultName, string helpMessage) { this.title = title; this.defaultName = defaultName; this.helpMessage = helpMessage; } public ResourceFolderAttribute() : this("Select Resource Folder","","Folder must put in \"Resources\" folder") {} } }
需要留意的是 class 的命名方式, ResourceFolderAttribute 有 Attribute 字樣方便日後調用.
並且 extend PropertyAttribute class. 再弄一個 PropertyDrawer 給這個 class.
[CustomPropertyDrawer(typeof(ResourceFolderAttribute))] public class ResourceDrawer : PropertyDrawer { const string RESOURCE_FOLDER = "resources/"; const float BUTTON_HEIGHT = 16f; const float HELP_HEIGHT = 30f; // Provide easy access to the RegexAttribute for reading information from it. // ResourceFolderAttribute resourceFolder { get { return ((ResourceFolderAttribute)attribute); } } ResourceFolderAttribute resourceFolder { get { return (ResourceFolderAttribute)attribute; } } public override void OnGUI (UnityEngine.Rect position, SerializedProperty property, UnityEngine.GUIContent label) { Rect _buttonPosition = EditorGUI.PrefixLabel(position, label); _buttonPosition.height = BUTTON_HEIGHT; string _btn = "Select Folder"; if( IsResourseFolder(property) ) _btn = "Resources:/ "+ property.stringValue; if ( GUI.Button(_buttonPosition,_btn) ) { string _string = EditorUtility.OpenFolderPanel(resourceFolder.title, "", resourceFolder.defaultName); if( !string.IsNullOrEmpty(_string) ) { bool _check = _string.ToLower().IndexOf(RESOURCE_FOLDER) > 0 && !_string.ToLower().EndsWith(RESOURCE_FOLDER); if( _check ) { property.stringValue = GetShortPath(_string); } else { property.stringValue = string.Empty; } } } Rect _helpPosition = EditorGUI.IndentedRect(position); _helpPosition.y += _buttonPosition.height; _helpPosition.height = 30f; if ( !IsResourseFolder(property) ) { EditorGUI.HelpBox(_helpPosition, resourceFolder.helpMessage, MessageType.Warning); } } public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { if( IsResourseFolder(property) ) return base.GetPropertyHeight(property,label); else return base.GetPropertyHeight(property,label) + HELP_HEIGHT; } bool IsResourseFolder(SerializedProperty _prop) { return !string.IsNullOrEmpty(_prop.stringValue); } string GetShortPath(string fullPath) { return fullPath.Substring( fullPath.ToLower().IndexOf(RESOURCE_FOLDER)+ RESOURCE_FOLDER.Length ); } }
由於 Resources asset 需放進名為 \Resources 的 folder 中, 用之作檢查為條件.
並更新原本的 property.stringValue 為處理後的 short path.
調用方法也很簡單.
[ResourceFolderAttribute] public string myFolder; [ResourceFolder "Select XYZ Folder","","Folder must put in \"Resources\" folder"] public string myFolder2;
由於 Attribute 是可以忽略的字串, 原本的 [ResourceFolderAttribute] 也可以寫為 [ResourceFolder]
尾隨的 string 是 constructer 的 overload method, 需基於原本的 structure 來作正確寫入.
Ref :