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