Unity---Odin Inspector

Odin界面编辑器

记录 2312



一、Odin Inspector介绍

Odin Inspector(Version:3.1.6) 是 Unity 的一个插件,是一款可自定义的、功能强大的界面编辑器。可以使用插件自身提供的100多个插件轻松的编辑出美观的界面,让开发者更容易使用。

二、特性

1.Unity与Odin类似特性的对比

Unity也有一些特性,但涉及界面开发的并不多。而Odin作为界面编辑器,它提供了100多个特性,不过基本上都是作用与字段上的特性,同时有很多和Unity特性类似的,就界面开发来说非常的全面。
以下举例几个类似的特性对比:

1、InspectorNameAttribute 和 LabelText,字段别名显示
//Unity Attribute , 作用与枚举值,可以给枚举值起个别名
public enum unityEnum{
    
    
	[inspcetorName("a的别名")]
	a,
	[inspcetorName("b的别名")]
	b,
}
//Odin Attribute  ,给字段和枚举值个名字
 public enum odinEnum{
    
    
	[LabelText("a的别名")]
	a,
	//注意这里Odin使用$意思是使用字段D的值,但是在枚举里是不起作用的,实际上显"$D"
	[LabelText("$D")]   
	b,
}
//在Odin特性里,使用@代表引用一个方法
[LabelText("@dataTime.Now.Tostring(\"HH:mm:ss\")")]
public string D = "name:D";
2、SpaceAttribute和 PropertySpace 填充
    //Unity Space 
    [Space(10)] //只能用于字段,用于当前字段和下一字段的空白填充
    public int unitySpace;
    
    //Odin  PropertySpace
    [PropertySpace(spaceBefore:10,spaceAfter:10)]//可用于字段,可以对字段前后都进行填充,更灵活
    public int odinSpace;
    [PropertySpace(spaceBefore:40,spaceAfter:100)]//也可用于属性,可以对字段前后都进行填充,更灵活
    public int property {
    
     get; set; }
3、PropertyRange
    ///Unity Range
    [Range(0, 10)] //只能用于字段
    public int unityRange = 1;
    
    //Odin PropertyRange
    [PropertyRange(0,10)] //可用于属性
    public int odinProperRange {
    
     get; set; }
    [PropertyRange("unityRange", 10)] //也可以应用其他变量作为最大最小值,不在范围内时会有错误提示
    public int odinRange = 2;
4、Title 和Header 标题提示显示
   //Unity Header
    [Header("UnityHeader")] //仅可用于字段
    public string myTitle;
    //Odin Title
    //第一个参数为一级标题;第二个参数为二级标题;第三个参数为位置,默认靠左;第四个参数为是否加下划线,默认为true;第五个参数为是否加粗一级标题,默认为True
    [Title("myTitle", "Subtitle", titleAlignment:TitleAlignments.Centered,horizontalLine:true,bold:true)]
    public string OdinTitle1;
    //同时方法也是可以添加标题,仅限于添加按钮特性的方法
    [Title("$myTitle")][Button]
    public void OdinTitleFunc(){
    
    }

2、几个好用的Odin特性

1、DictionaryDrawerSettings 字典使用
    //DictionaryDrawerSettings
    //DictionaryDrawerSettings 有4个定义参数,KeyLabel为Key的字段文本,ValueLabel为value的字段文本
    //displayMode定义字典的展开方式,KeyColumnWidth为显示的Key的宽度
    //一般来说非可序列话的元素是无法在面板展示的,如果想要在Inspector中显示字典的格式,需要添加[ShowInInspector]或者让类继承SerializedBehaviour,
    [ShowInInspector] 
    [DictionaryDrawerSettings(KeyLabel = "Key",ValueLabel = "value",DisplayMode = DictionaryDisplayOptions.OneLine,KeyColumnWidth = 100)]
    public Dictionary<string, string> odinDic;
2、[BoxGroup]、[HorizontalGroup]和[HideIfGroup] 窗口布局
    //有相同标题的字段会放到一个“盒子”里,且盒子的名字为SomeTitle,同时ShowLabel可以控制标题是否显示
    [BoxGroup("SomeTitle",ShowLabel = false)] 
    public int a;
    [BoxGroup("SomeTitle")] 
    public int b;
    //在父级标题下加子标题,形成嵌套,就是个文件夹路径
    [BoxGroup("SomeTitle/SubTitle")] 
    public int a1;
    //[HorizontalGroup] 可以把当前子标题“box“下的所有Group水平排列,
    [HorizontalGroup("box",LabelWidth =  50)]
    [BoxGroup("box/left")] 
    public int left;
    //同时HorizontalGroup也可以进行嵌套,然后在”box/right"下的所有子Group又会是水平排列
    [HorizontalGroup("box/right",LabelWidth =  50)]
    [BoxGroup("box/right/A")]
    public int right;
    //Hide if Group,就是根据传递的值来看是否隐藏或显示当前组,
    public bool toggle = true;
    [HideIfGroup("toggle")][VerticalGroup]
    public int testHideifgroup;
3、FilePath和FolderPath 筛选文件路径
     //FilePath,打开文件路径,并把路径赋值给对应变量,支持打开文件夹自选,也支持拖拽过来
     //ParentFolder可以规定打开时的初始文件夹路径,Extensions筛选扩展名的。AbsolutePath 是否需要绝对路径,RequireExistingPath 所选路径是否存在,
     [FilePath(ParentFolder = "Assets/Plugins",Extensions = "cs",AbsolutePath = true, RequireExistingPath = true)]
     public string filePath;
     //FolderPath 是打开文件夹,输入的也是文件加路径,也只能输入文件夹路径,
     [FolderPath(ParentFolder = "Assets/Plugins",AbsolutePath = true, RequireExistingPath = true)]
     public string folderPath;
4、Validition 检测输入

    //ValidatorInput 用于检测输入,condition 用于自定义检测函数,defaultmessage 默认提示消息,MessageType 提示消息类型
    [ValidateInput("CheckName","defaultMessage",MessageType = InfoMessageType.Info)] 
    public GameObject go;

    private bool CheckName(GameObject go, ref string errorMessage,ref InfoMessageType ? infoMessageType)
    {
    
    
        if (go == null)
        {
    
    
            infoMessageType = InfoMessageType.Error;
            errorMessage = "没对象";
            return false;
        }
        string name = go.gameObject.name;
        if (name == "")
        {
    
    
            infoMessageType = InfoMessageType.Warning;
            errorMessage = "给个名字呗";
            return false;
        }
        return true;
    }

三、自定义特性

1、OdinValueDrawer 自定义值特性

可以自定一值特性,模板如下,可以使用泛型,这里继承SerializedMonoBehaviour 并加上OdinSerialize标签是为了把对应值序列化,然后显示在面板上。

public class Test : SerializedMonoBehaviour
{
    
    
    [OdinSerialize]
    public TestValue<int,int> tt = new TestValue<int,int>();
}
public class TestValue<T1,T2>
{
    
    
    public T1 t1;
    public T2 t2;
}

public class TestValueDrawer : OdinValueDrawer<TestValue<int, int>>
{
    
    
    protected override void DrawPropertyLayout(GUIContent label){
    
     }
}
public class TestValueDrawer<T1,T2> : OdinValueDrawer<TestValue<T1, T2>>
{
    
    
    protected override void DrawPropertyLayout(GUIContent label){
    
     }
}
2、OdinAttributeDrawer 自定义特性绘制

绘制自定义特性,和普通特性使用方式一样


public class AttributeTest : MonoBehaviour
{
    
    
    [TestDraw(1)] public int a;
}
//定义特性运用的范围
[AttributeUsage(AttributeTargets.Field)]
public class TestDrawAttribute : Attribute
{
    
    
    public int value;

    public TestDrawAttribute(int value)
    {
    
    
        this.value = value;
    }
}

public class TestDrawAttributeDraw : OdinValueDrawer<TestDrawAttribute>
{
    
    
    protected override void DrawPropertyLayout(GUIContent label){
    
     }
}
3、OdinGroupDrawer 自定义组特性
public class GroupTest : MonoBehaviour
{
    
    
    [GroupDrap(1)] public int a;
}
[AttributeUsage(AttributeTargets.All)]
public class GroupDrap : PropertyGroupAttribute
{
    
    
    public int value;
    public GroupDrap(int value,int order = 0):base("_DefaultGroup",order)
    {
    
    
        this.value = value;
    }
}
public class GroupDrapDraw : OdinGroupDrawer<GroupDrap>
{
    
    
    protected override void DrawPropertyLayout(GUIContent label){
    
     }
}
4、AttributeValidator 自定义验证器特性

可以写一些验证器相关的


 public class ValidationExample : MonoBehaviour
{
    
    
    [NotOne] public int NotOne;
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class NotOneAttribute : Attribute
{
    
    
}
public class NotOneAttributeValidator : AttributeValidator<NotOneAttribute, int>
{
    
    
    protected override void Validate(ValidationResult result)
    {
    
    
        if (this.ValueEntry.SmartValue == 1)
        {
    
    
            result.Message = "1 is not a valid value.";
            result.ResultType = ValidationResultType.Error;
        }
    }
}

四、EditorWindow

Odin提供了两个类,OdinEditorWindow和OdinMenuEditorWindow,他们分别用来编辑通用窗口和菜单窗口,使用这俩类编写窗口时UI不必写在OnGui里了,不在需要调用EditorLayout 、Layout、OnGui等这些Unity原生接口,可以直接使用Odin丰富的特性编写,这样写不仅更简单且更容易维护。

1、OdinEditorWindow:

基础窗口,类似原生EditorWindow

//基础窗口可以继承OdinEditorWindow,创建窗口是直接调用GetWidow就够了
public class windowTest : OdinEditorWindow
{
    
    
    [MenuItem("WindowTest/OdinBaseWindow")]
    private static void OpenWindow()
    {
    
    
        var window = GetWindow<windowTest>();
        window.position = new Rect(0, 0, 1980, 1080);
    }
    //不用写在 OnGui 里了 ,可直接运用特性写入
    [LabelText("测试文字")]
    public string test;
}
2、OdinMenuEditorWindow

菜单树窗口,就是提供了一个类似Unity Project Settings的窗口,当然显示是更加丰富。

 
//继承OdinMenuEditorWindow,创建窗口是直接调用GetWidow就够了
public class windowTest : OdinMenuEditorWindow
{
    
    
    [MenuItem("WindowTest/OdinMenuEditorWindow")]
    private static void OpenWindow()
    {
    
    
        var window = GetWindow<windowTest>();
        window.position = new Rect(0, 0, 1980, 1080);
    }
    [BoxGroup("当前页面内容")]
    public string test;

    protected override OdinMenuTree BuildMenuTree()
    {
    
    
        OdinMenuTree tree = new OdinMenuTree(supportsMultiSelect: false) //为true时,可以按着ctrl多选
        {
    
    
            //path 菜单树的名,文件夹形式的,可以嵌套,  instance 需要显示在当前菜单下显示的页面,EditorIcon 图标
            {
    
    "Home", this , EditorIcons.House},
            {
    
    "OdinSetting", null , EditorIcons.SettingsCog},
            {
    
    "OdinSetting/Color paletter", ColorPaletteManager.Instance , EditorIcons.EyeDropper}
        };
        var customMenuStyle = new OdinMenuStyle
        {
    
    
            BorderPadding = 0f,
            AlignTriangleLeft = true,
            TriangleSize = 16f,
            TrianglePadding = 0f,
            Offset = 20f,
            Height = 23,
            IconPadding = 0f,
            BorderAlpha = 0.323f
        };
        //定义菜单树样式
        tree.DefaultMenuStyle = customMenuStyle;
        //是否显示搜索栏
        tree.Config.DrawSearchToolbar = true;
        //自定义搜索方式,也可以不定义
        tree.Config.SearchFunction = (obj) =>  !obj.Name.StartsWith("Odin");
        //也能这样定义,但是发现没啥用
        /*this.DrawMenuSearchBar = true;
        this.CustomSearchFunction = (obj) =>  !obj.Name.StartsWith("Odin");*/
        //AddAllAssetsAtPath 把assetFolderPath下的所有对应类型的文件插入到menuPath下
        tree.AddAllAssetsAtPath("Odin Settings/More Odin Settings", "Plugins/Sirenix", typeof(ScriptableObject), true);
        //AddassetAtPath 把支援assetPath插入到menuPath下
        tree.AddAssetAtPath("Odin Getting Started", "Plugins/Sirenix/Getting Started With Odin.asset");
        //MenuItems.Insert 在Index位置插入一个 OdinMenuItem,这个是在最外层插入
        tree.MenuItems.Insert(2, new OdinMenuItem(tree, "Menu Style", tree.DefaultMenuStyle));
        //Add添加一个菜单
        tree.Add("Menu/Items/Are/Created/As/Needed", new GUIContent());
        //默认排序 排序方式是,有子菜单的在上面,然后同级按照首字母排序
        tree.SortMenuItemsByName();
        //自定义排序
        tree.EnumerateTree(true).SortMenuItemsByName((obj1,obj2) =>  0);
        return tree;
    }
}

总结

Odin特性大概有100多个,目前只举例了一点,更多的还是需要看官网,或者下载插件,点击 Tools->Odin->Inspector->Attribute Overview.
Odin提供的众多特性可以让我们非常快速的编写代码,搭建美观且强大的界面,它也允许我们轻松的扩展和定制我们的编辑器,通过编写自定义属性绘制器、值绘制器等,我们可以快速编写我们想要的功能。
嗯·推荐使用

猜你喜欢

转载自blog.csdn.net/qq_43096697/article/details/134956023