PropertyDrawer 的 ResourceFolder 試作

玩多了 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 :

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

*

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料