Unity Odin Characterization

Analysis of Odin features

I. Introduction

Odin is a very powerful editor plug-in in Unity, which provides many rich features (Attribute), which can be used to enrich the display and interaction of the editor. Some useful features are listed below.

2. Feature Encyclopedia

2.1 Restrictions related

2.1.1 [AssetsOnly] Only Project resources

  • Function:
    Only the resources under the Project can be selected, and the resources under the Scene cannot be selected
  • Example:
    [AssetsOnly]
    public GameObject SomePrefab;

2.1.2 [SceneObjectsOnly] Only Scene resources

  • Function:
    Only the resources under Scene can be selected, and the resources under Project cannot be selected
  • 示例:
    [SceneObjectsOnly]
    public GameObject SomePrefab;

2.1.3 【MinValue】&【MaxValue】minimum value & maximum value

  • Function:
    Limit the minimum and maximum values
  • Example:
    [MinValue(0)] [MaxValue(5)]
    public int val;

2.1.4 【MinMaxSlider】minimum and maximum slider (modified Vector2)

  • Function:
    It also limits the minimum and maximum values, but has a scroll bar. Used to modify Vector2
  • 示例:
    [MinMaxSlider(-10, 10)]
    public Vector2 MinMaxValueSlider = new Vector2(-7, -2);
  • Renderings:
    insert image description here

2.1.5 [Range] & [PropertyRange] field & property range

  • Function:
    Range is used to limit the range of fields;
    PropertyRange is more powerful and can also limit the range of properties; normal built-in functions of Unity cannot display fields, here we use the [ShowInInspector] feature in Odin to display Property properties

  • Example:
    [Range(0, 10)]
    public int Field = 2;

    [ShowInInspector,PropertyRange(0, 10)]
    public int Property { get; set; }

  • Renderings:
    insert image description here

  • Advanced usage:
    The minimum and maximum values ​​above are all fixed values. In fact, dynamic field values ​​can also be used as the range, as in the following example

  • Advanced example:
    [PropertyRange(0, “Max”)]
    public int Dynamic = 6;

    public int Max = 100;

2.1.6 [Required] Restricted resources cannot be empty

  • Function:
    Required can be used to limit that a resource cannot be empty

  • Syntax:
    [Required("Prompt when the resource is empty, you can leave it blank")]

  • 示例:
    [Required]
    public GameObject MyGameObject;

    [Required(“Custom error message.”)]
    public Rigidbody MyRigidbody;

  • Renderings:
    -

2.1.7 【ValidateInput】Assignment verification

  • Function:
    ValidateInput is a very practical function. When the user assigns a value to the field, the callback method will be triggered. We can then write code logic to check for user-assigned entities. If the check fails, you can give a reminder

  • Syntax:
    [ValidateInput("callback method name", "default prompt")]

  • example

[ValidateInput("HasMeshRendererDynamicMessageAndType", "Prefab must have a MeshRenderer component")]
public GameObject DynamicMessageAndType;
 
//返回类型为bool,表示是否通过检测
private bool HasMeshRendererDynamicMessageAndType(GameObject gameObject, ref string errorMessage, ref InfoMessageType? messageType)
    {
    	//没有赋值时,通过检测
        if (gameObject == null) return true;

		//如果赋值的GameObject没有MeshRenderer,return false检测不通过
        if (gameObject.GetComponentInChildren<MeshRenderer>() == null)
        {
            // 设置提示信息
            errorMessage = "\"" + gameObject.name + "\" should have a MeshRenderer component";

            // //设置警告类型
            messageType = InfoMessageType.Warning; 

            return false;
        }

        return true;
    }

2.1.8 【TypeFilter】restriction type

  • Function:
    TypeFilter can be used to restrict only the specified type collection to be selected
  • example
    [ShowInInspector]
    [TypeFilter("GetFilteredTypeList")]
    public BaseClass A, B;

    [ShowInInspector]
    [TypeFilter("GetFilteredTypeList")]
    public BaseClass[] Array = new BaseClass[3];

    public IEnumerable<Type> GetFilteredTypeList()
    {
        var q = typeof(BaseClass).Assembly.GetTypes()
            .Where(x => !x.IsAbstract)                                          // 排除 BaseClass
            .Where(x => !x.IsGenericTypeDefinition)                             // 排除 泛型,如C1<>
            .Where(x => typeof(BaseClass).IsAssignableFrom(x));                 // 排除没有继承BaseClass的类型

        // 增加特定的类
        q = q.AppendWith(typeof(C1<>).MakeGenericType(typeof(GameObject)));
        q = q.AppendWith(typeof(C1<>).MakeGenericType(typeof(AnimationCurve)));
        q = q.AppendWith(typeof(C1<>).MakeGenericType(typeof(List<float>)));

        return q;
    }

    public abstract class BaseClass
    {
        public int BaseField;
    }

    public class A1 : BaseClass { public int _A1; }
    public class A2 : A1 { public int _A2; }
    public class A3 : A2 { public int _A3; }
    public class B1 : BaseClass { public int _B1; }
    public class B2 : B1 { public int _B2; }
    public class B3 : B2 { public int _B3; }
    public class C1<T> : BaseClass { public T C; }
  • renderings
    insert image description here

2.1.9 [ReadOnly] read-only

  • Function:
    ReadOnly can make the properties displayed on the panel cannot be modified

  • 示例:
    [ReadOnly]
    public string MyString = “This is displayed as text”;

    [ReadOnly]
    public int MyInt = 9001;

    [ReadOnly]
    public int[] MyIntList = new int[] { 1, 2, 3, 4, 5, 6, 7, };

  • Renderings:
    insert image description here

2.2 General characteristics

Here are some useful but not easy to classify features

2.2.1 [ShowInInspector] displayed in the Inspector panel

  • Function:
    ShowInInspector can display attributes, static variables and other fields that cannot be displayed in the Inspector in the panel. very useful function

  • 示例:
    [ShowInInspector]
    public string DelayedProperty { get; set; }

    [ShowInInspector]
    public static string testStr;

  • Renderings:
    insert image description here

2.2.2 [OnValueChanged] Value change callback

  • Function:
    OnValueChanged can call the callback method after the value of the field changes

  • Syntax:
    [OnValueChanged("callback method name")]

  • Example:
    [OnValueChanged(“OnValueChanged”)]
    public int val;

    private void OnValueChanged()
    {
    Debug.Log(“Value changed!”);
    }

  • Renderings:
    insert image description here

2.2.3 【Delayed】&【DelayedProperty】delay

  • Function:
    Delayed and DelayedProperty can be used with the above OnValueChanged. OnValueChanged can be called back after the value changes, but it will be called back every time it changes. At this time, Delayed comes in handy. You can call back after the value change is completed, and there will not be so many callbacks. Delayed modifies common fields, and DelayedProperty can be used to modify properties

  • Example:

    [Delayed]
    [OnValueChanged(“OnValueChanged”)]
    public int DelayedField;

    [ShowInInspector, DelayedProperty]
    [OnValueChanged(“OnValueChanged”)]
    public string DelayedProperty { get; set; }

    private void OnValueChanged()
    { Debug.Log(“Value changed!”); }

  • Renderings:
    insert image description here

2.2.4 [GUIColor] color change

  • Function:
    GUIColor can change the color of fields, buttons, and color gradients. Next, let's look at the official example
  • Example:
    [GUIColor(0.3f, 0.8f, 0.8f, 1f)]
    public int ColoredInt1;

    [GUIColor(0.3f, 0.8f, 0.8f, 1f)]
    public int ColoredInt2;

    [ButtonGroup]
    [GUIColor(0, 1, 0)]
    private void Apply()
    {
    }

    [ButtonGroup]
    [GUIColor(1, 0.6f, 0.4f)]
    private void Cancel()
    {
    }

    [InfoBox("You can also reference a color member to dynamically change the color of a property.")]
    [GUIColor("GetButtonColor")]
    [Button("I Am Fabulous", ButtonSizes.Gigantic)]
    private static void IAmFabulous()
    {
    }

    [Button(ButtonSizes.Large)]
    [GUIColor("@Color.Lerp(Color.red, Color.green, Mathf.Abs(Mathf.Sin((float)EditorApplication.timeSinceStartup)))")]
    private static void Expressive()
    {
    }

    private static Color GetButtonColor()
    {
        Sirenix.Utilities.Editor.GUIHelper.RequestRepaint();
        return Color.HSVToRGB(Mathf.Cos((float)UnityEditor.EditorApplication.timeSinceStartup + 1f) * 0.225f + 0.325f, 1, 1);
    }
  • Renderings:
    insert image description here

2.2.4 [ColorPalette] color palette

Hey, when it comes to color, let's talk about the color palette, which is also a useful function. Because a game is generally formed by 3~4 main colors. Putting these colors in a palette will not make mistakes, and it will be more convenient to choose colors

  • Function:
    ColorPalette is used to modify the Color field, and a palette can be listed next to it for users to choose colors

  • Syntax:
    [ColorPalette] You can choose any palette in the drop-down box, and then you can choose the color under the palette

    [ColorPalette("Palette Name")] can specify the palette name, so that the color of the palette appears directly

  • Example:
    //Do not specify a palette name
    [ColorPalette]
    public Color ColorOptions;

    //Add some spacing, otherwise it will be too crowded, not easy to explain
    [PropertySpace(SpaceBefore = 60)]

    Specify the palette name as Underwater
    [ColorPalette(“Underwater”)]
    public Color UnderwaterColor;

  • Renderings:
    insert image description here

2.2.5 [Title] & [HideLabel] add title & hide field name

  • Function:
    Title can be used to add title, HideLabel can be used to hide field name

  • 示例:
    [Title(“Wide Colors”)]
    [HideLabel]
    [ColorPalette(“Fall”)]
    public Color WideColor1;

  • Effect picture:
    You can see that the original field name is hidden, and only the Title and palette we defined are displayed

2.2.6 [DisplayAsString] display content

  • Function:
    DisplayAsString can display the content of the string field on the Inspector panel
  • 示例:
    [HideLabel]
    [DisplayAsString]
    public string Title = “I am content”;
  • Effect picture:
    you can see that the field name is hidden, but the field content is displayed
    insert image description here

2.2.7 【PropertyOrder】field priority

  • Function:
    PropertyOrder can control the priority of the field, the smaller the priority value, the higher the ranking

  • Example:
    [PropertyOrder(1)]
    public int Second;

    [PropertyOrder(-1)]
    public int First;

  • Renderings:
    insert image description here

2.2.7 [PropertySpace] field interval

  • Function:
    PropertySpace can be used to set the field interval, and PropertySpace was used when talking about the color palette

  • Example:
    public int Space;

    [PropertySpace(SpaceBefore = 10, SpaceAfter = 20)]
    public int Space2;

    public int Space3;

  • Effect picture:
    It is clear at a glance when you see the effect picture
    insert image description here

2.2.8 【Searchable】Search

  • Function:
    Searchable can be used to mark classes and lists, and a search box will appear to search for items whose field name or content matches the input content. Let's look at the official example
  • Example:
    [Searchable]
    public ExampleClass searchableClass = new ExampleClass();
    

    [Serializable]
    public class ExampleClass
    {
        public string SomeString = "Saehrimnir is a tasty delicacy";
        public int SomeInt = 13579;

        public DataContainer DataContainerOne = new DataContainer() { Name = "Example Data Set One" };
        public DataContainer DataContainerTwo = new DataContainer() { Name = "Example Data Set Two" };
    }

    [Serializable, Searchable] // You can also apply it on a type like this, and it will become searchable wherever it appears
    public class DataContainer
    {
        public string Name;
        public List<ExampleStruct> Data = new List<ExampleStruct>(Enumerable.Range(1, 10).Select(i => new ExampleStruct(i)));
    }

    [Serializable]
    public struct FilterableBySquareStruct : ISearchFilterable
    {
        public int Number;

        [ShowInInspector, DisplayAsString, EnableGUI]
        public int Square { get { return this.Number * this.Number; } }

        public FilterableBySquareStruct(int nr)
        {
            this.Number = nr;
        }

        public bool IsMatch(string searchString)
        {
            return searchString.Contains(Square.ToString());
        }
    }

    [Serializable]
    public struct ExampleStruct
    {
        public string Name;
        public int Number;
        public ExampleEnum Enum;

        public ExampleStruct(int nr) : this()
        {
            this.Name = "Element " + nr;
            this.Number = nr;

            this.Enum = (ExampleEnum)ExampleHelper.RandomInt(0, 5);
        }
    }

    public enum ExampleEnum
    {
        One, Two, Three, Four, Five
    }

  • Renderings:
    Please add a picture description

2.2.9 [MultiLineProperty] Multi-line input box

  • Function:
    MultiLineProperty can be used to modify string fields and properties, so that the text input box becomes the specified number of lines

  • 示例:
    [Multiline(10)]
    public string UnityMultilineField = “”;

    [InfoBox(“Odin supports properties, but Unity’s own Multiline attribute only works on fields.”)]
    [ShowInInspector]
    [MultiLineProperty(10)]
    public string OdinMultilineProperty { get; set; }

  • Effect picture:
    You can see that there are 10 lines of input boxes, MultiLineProperty can modify properties, but the built-in MultiLine cannot
    insert image description here

2.2.10 [EnumPaging] Enumeration paging

  • Function:
    EnumPaging can be used to modify enumeration fields and attributes, so that the enumeration can add left and right switching buttons

  • 示例:
    [EnumPaging] public SomeEnum SomeEnumField;

    public enum SomeEnum
    {
    A,
    B,
    C
    }

  • Renderings:
    Please add a picture description

2.2.11 [EnumToggleButtons] Enumeration drop-down box to horizontal layout

  • Function:
    EnumToggleButtons can remove the original drop-down box of the enumeration, and instead list all options in the form of a horizontal layout
  • Example:
    //不加修饰的枚举
    public SomeEnum OriginSomeEnum;
    
    //去掉下拉框,把枚举选项横向排列显示出来
    [EnumToggleButtons] 
    public SomeEnum SomeEnumField;

    public enum SomeEnum
    {
        First,
        Second,
        Third,
        Fourth,
        AndSoOn
    }
  • Renderings:
    Please add a picture description

2.2.12 [SuffixLabel] Add suffix to Label

  • Function:
    SuffixLabel can add a suffix at the end of the input box, so that people who use the tool know what the current unit is

  • 示例:
    [SuffixLabel(“ms”, Overlay = false)]
    public float Speed1;

    //Overlay indicates whether the suffix is ​​in the input box
    [SuffixLabel("ms", Overlay = true)]
    public float Speed2;

  • Renderings:
    insert image description here

2.2.13 [InlineButton] add a button at the end

  • Function:
    InlineButton to modify the field, you can add a button at the end of the field
  • Syntax:
    [InlineButton("method name", "button name")]
    button name can be empty, when it is empty, name the button with the method name
  • Example:
    [InlineButton("A")]
    public int InlineButton;
    
    [InlineButton("A")]
    [InlineButton("B", "Custom Button Name")]
    public int ChainedButtons;

    private void A()
    {
        Debug.Log("A");
    }

    private void B()
    {
        Debug.Log("B");
    }
  • Renderings:
    insert image description here

2.3 More special features

What is "more special features" and what is the difference from "general features". Maybe it’s because of my own feeling that I have classified them here; maybe it’s because I’ve already listed a lot above, so I feel that I have to list them in a new group...

2.3.1 [Button] button

  • Function:
    Button is used to modify the method, a button will appear in the Inspector window, and the logic of the method will be executed after clicking

  • 示例:
    [Button(“Hello”)]
    private void DefaultSizedButton()
    {
    Debug.Log(“Hello”);
    }

    [Button(“Hello,Doraemon”,buttonSize:ButtonSizes.Large),GUIColor(0,1,0)]
    private void Button2()
    {
    Debug.Log(“Hello ~ Doraemon”);
    }

  • Renderings:
    insert image description here

2.3.2 [FilePath] select file

  • Function:
    FilePath can open a file window to select a file. It seems easy to duplicate the name. The complete namespace is this: [Sirenix.OdinInspector.FilePath], which can be specified when the name is duplicated

  • Example:
    //Return relative path by default
    [FilePath]
    public string UnityProjectPath;

    //You can specify the directory to open (the relative path to the directory will be returned), specify the file format, and specify whether to return the full path. The default is false, that is, the relative path [FilePath(ParentFolder = "Assets/OdinLearn",
    Extensions = "cs ", AbsolutePath = true)]
    public string ResourcePath;

  • Renderings:
    insert image description here
    insert image description here

2.3.3 【FolderPath】Select folder

The usage of FolderPath is similar to the above FilePath, except that the folder is selected, and the file is selected above

2.3.4 [OnInspectorInit] Inspector initialization callback

  • Function:
    OnInspectorInit can be called when the Inspector initializes the callback
  • Example:
    //在Inspector窗口初始化绘制
    [OnInspectorInit("@TimeFoldoutWasOpened = DateTime.Now.ToString()")]
    public string TimeFoldoutWasOpened;
  • Renderings:
    insert image description here

2.3.5 [OnInspectorGUI] Always call back when the Inspector is activated

  • Function:
    OnInspectorGUI can always be called back when the Inspector window is activated. Fields can be modified, and methods can also be modified. Can be used to help us add some UI
  • Example:
    //修饰字段
    [OnInspectorInit("@Texture = EditorIcons.OdinInspectorLogo")]
    [OnInspectorGUI("DrawPreview", append: true)]//append = true,表示回调方法DrawPreview在该字段绘制之后,再调用;false,则表示在原始绘制之前
    public Texture2D Texture;
	
	//把选择的图标再绘制一遍出来
    private void DrawPreview()
    {
        if (this.Texture == null) return;

        GUILayout.BeginVertical(GUI.skin.box);
        GUILayout.Label(this.Texture);
        GUILayout.EndVertical();
    }
    
    
    //修饰方法,直接绘制
    [OnInspectorGUI]
    private void OnInspectorGUI()
    {
        UnityEditor.EditorGUILayout.HelpBox("OnInspectorGUI can also be used on both methods and properties", UnityEditor.MessageType.Info);
    }
  • Renderings:
    insert image description here

2.3.6 [OnStateUpdate] callback every frame

  • Function:
    OnStateUpdate Callback every frame, modify the field, you can execute some methods
  • Example:
    public List<string> list;

    [OnStateUpdate("@#(list).State.Expanded = $value")]
    public bool ExpandList;

    [OnStateUpdate("@UnityEngine.Debug.Log(\"OnStateUpdate event invoked!\")")]
    public bool Test;
  • Effect picture:
    no

2.3.7 【CustomValueDrawer】custom drawing

  • Function:
    CustomValueDrawer decorates the field, which can change the original drawing method to a custom drawing method
  • Syntax:
    CustomValueDrawer("method name")
  • Example:
    //原始
    public float From = 2, To = 7;
    
    //自定义显示字段1
    [CustomValueDrawer("MyCustomDrawerStatic")]
    public float CustomDrawerStatic;
    
    //自定义显示方法1,使用固定值
    private static float MyCustomDrawerStatic(float value, GUIContent label)
    {
        return EditorGUILayout.Slider(label, value, 0f, 10f);
    }
    

    //自定义显示字段2
    [CustomValueDrawer("MyCustomDrawerInstance")]
    public float CustomDrawerInstance;
    
    //自定义显示方法2,使用From & To 作为变量
    private float MyCustomDrawerInstance(float value, GUIContent label)
    {
        return EditorGUILayout.Slider(label, value, this.From, this.To);
    }

    //自定义显示字段3
    [CustomValueDrawer("MyCustomDrawerAppendRange")]
    public float AppendRange;
    
    //自定义显示方法3,增加盒子和Label
    private float MyCustomDrawerAppendRange(float value, GUIContent label, Func<GUIContent, bool> callNextDrawer)
    {
        SirenixEditorGUI.BeginBox();
        callNextDrawer(label);
        var result = EditorGUILayout.Slider(value, this.From, this.To);
        SirenixEditorGUI.EndBox();
        return result;
    }
    
    //自定义显示字段4,修饰数组
    [CustomValueDrawer("MyCustomDrawerArrayNoLabel")]
    public float[] CustomDrawerArrayNoLabel = new float[] { 3f, 5f, 6f };
    
    //自定义显示方法4,去掉Label
    private float MyCustomDrawerArrayNoLabel(float value)
    {
        return EditorGUILayout.Slider(value, this.From, this.To);
    }
  • Renderings:
    insert image description here

2.4 Group group

2.4.1 [BoxGroup] box group

  • Function:
    BoxGroup can make fields of the same group in a box

  • 示例:
    // Box with a title.
    [BoxGroup(“Some Title”)]
    public string A;

    [BoxGroup(“Some Title”)]
    public string B;

    // Box with a centered title.
    [BoxGroup(“Centered Title”, centerLabel: true)]
    public string C;

    [BoxGroup(“Centered Title”)]
    public string D;

  • Renderings:
    insert image description here

2.4.1 [HorizontalGroup] horizontal layout group

  • Function:
    HorizontalGroup can arrange fields horizontally
  • Example:
// LabelWidth can be helpfull when dealing with HorizontalGroups.
    [HorizontalGroup("Group 1", LabelWidth = 20)]
    public int C;

    [HorizontalGroup("Group 1")]
    public int D;

    [HorizontalGroup("Group 1")]
    public int E;

// Having multiple properties in a column can be achived using multiple groups. Checkout the "Combining Group Attributes" example.
    [HorizontalGroup("Split", 0.5f, LabelWidth = 20)]
    [BoxGroup("Split/Left")]
    public int L;

    [BoxGroup("Split/Right")]
    public int M;

    [BoxGroup("Split/Left")]
    public int N;

    [BoxGroup("Split/Right")]
    public int O;
  • Renderings:
    insert image description here

2.4.2 【VerticalGroup】Numerical layout group

  • Function:
    VerticalGroup is similar to HorizontalGroup, and realizes vertical layout

  • 示例:
    [HorizontalGroup(“Split”)]
    [VerticalGroup(“Split/Left”)]
    public InfoMessageType First;

    [VerticalGroup(“Split/Left”)]
    public InfoMessageType Second;

    [HideLabel]
    [VerticalGroup(“Split/Right”)]
    public int A;

    [HideLabel]
    [VerticalGroup(“Split/Right”)]
    public int B;

  • Renderings:
    insert image description here

2.4.3 [ButtonGroup] button group

  • Function:
    ButtonGroup modifies methods, which will arrange the method buttons of the same group into a neat HorizontalGroup horizontal layout.
    Note: Modification is the method, which is different from HorizontalGroup
  • Example:
    //不带组名字的一个组
    [ButtonGroup]
    private void A()
    {
    }

    [ButtonGroup]
    private void B()
    {
    }

    [ButtonGroup]
    private void C()
    {
    }

    [ButtonGroup]
    private void D()
    {
    }
    

    //带组名字的一个组
    [Button(ButtonSizes.Large)]
    [ButtonGroup("Button Group1")]
    private void E()
    {
    }

    [GUIColor(0, 1, 0)]
    [ButtonGroup("Button Group1")]
    private void F()
    {
    }

  • Renderings:
    insert image description here

2.4.4 [ResponsiveButtonGroup] Collapsible group

  • Function:
    ResponsiveButtonGroup is also used to decorate the method, but it uses a fluid layout, horizontal layout from left to right, and wraps from top to bottom to continue the layout when it is full
  • Example:
    [OnInspectorGUI]
    private void Space1()
    {
        GUILayout.Space(20);
    }

    [ResponsiveButtonGroup]
    public void Foo()
    {
    }

    [ResponsiveButtonGroup]
    public void Bar()
    {
    }

    [ResponsiveButtonGroup]
    public void Baz()
    {
    }

    [OnInspectorGUI]
    private void Space2()
    {
        GUILayout.Space(20);
    }

    [ResponsiveButtonGroup("UniformGroup", UniformLayout = true)]
    public void Foo1()
    {
    }

    [ResponsiveButtonGroup("UniformGroup")]
    public void Foo2()
    {
    }

    [ResponsiveButtonGroup("UniformGroup")]
    public void LongesNameWins()
    {
    }

    [ResponsiveButtonGroup("UniformGroup")]
    public void Foo4()
    {
    }

    [ResponsiveButtonGroup("UniformGroup")]
    public void Foo5()
    {
    }

    [ResponsiveButtonGroup("UniformGroup")]
    public void Foo6()
    {
    }

    [OnInspectorGUI]
    private void Space3()
    {
        GUILayout.Space(20);
    }

    [ResponsiveButtonGroup("DefaultButtonSize", DefaultButtonSize = ButtonSizes.Small)]
    public void Bar1()
    {
    }

    [ResponsiveButtonGroup("DefaultButtonSize")]
    public void Bar2()
    {
    }

    [ResponsiveButtonGroup("DefaultButtonSize")]
    public void Bar3()
    {
    }

    [Button(ButtonSizes.Large), ResponsiveButtonGroup("DefaultButtonSize")]
    public void Bar4()
    {
    }

    [Button(ButtonSizes.Large), ResponsiveButtonGroup("DefaultButtonSize")]
    public void Bar5()
    {
    }

    [ResponsiveButtonGroup("DefaultButtonSize")]
    public void Bar6()
    {
    }
  • Renderings:
    insert image description here

2.4.5 【FoldoutGroup】foldable group

  • Function:
    FoldoutGroup can fold the fields of the same group
  • Example:
    //Group 1
    [FoldoutGroup("Group 1")]
    public int A;

    [FoldoutGroup("Group 1")]
    public int B;

    [FoldoutGroup("Group 1")]
    public int C;
    
    //Collapsed group,默认不展开
    [FoldoutGroup("Collapsed group", expanded: false)]
    public int D;

    [FoldoutGroup("Collapsed group")]
    public int E;
    
    //使用‘$GroupTitle’字段的内容作为组名字,默认展开
    [FoldoutGroup("$GroupTitle", expanded: true)]
    public int One;

    [FoldoutGroup("$GroupTitle")]
    public int Two;

    public string GroupTitle = "Dynamic group title";
  • Renderings:
    Please add a picture description

2.4.6 [TabGroup] tab group

  • Function:
    TabGroup contains 2 important fields: GroupName (group name) and TabName (page signature)
    together with the GroupName group will form a tab, which can be switched to display a large capacity in a limited space

  • Syntax:
    1 parameter, equivalent to GroupName is control "", will be assigned to the same tab group
    [TabGroup("TabName page signature")]

    2 parameters, you can specify GroupName, the same group will be assigned to the same tab group
    [TabGroup("GroupName group name", "TabName page signature")]

  • Example:

    [TabGroup("Tab A")]
    public int One;

    [TabGroup("Tab A")]
    public int Two;

    [TabGroup("Tab A")]
    public int Three;

    [TabGroup("Tab B")]
    public string MyString;

    [TabGroup("Tab B")]
    public float MyFloat;

    [TabGroup("Tab C")]
    [HideLabel]
    public MyTabObject TabC;

    [TabGroup("New Group", "Tab A")]
    public int A;

    [TabGroup("New Group", "Tab A")]
    public int B;

    [TabGroup("New Group", "Tab A")]
    public int C;

    [TabGroup("New Group", "Tab B")]
    public string D;

    [TabGroup("New Group", "Tab B")]
    public float E;

    [TabGroup("New Group", "Tab C")]
    [HideLabel]
    public MyTabObject F;

    [Serializable]
    public class MyTabObject
    {
        public int A;
        public int B;
        public int C;
    }
  • Renderings:
    Please add a picture description

2.5 Collections collector

2.5.1 [TableList] list/array renderer

  • Function:
    TableList can be used to render List and array, and can be used with properties such as TableColumnWidth and HideInTables
  • Example:
    // ShowIndexLabels是否显示序号,默认不显示
    // DrawScrollView显示滑动条
    // MaxScrollViewHeight 当高度高于多少时开始出现滚动条
    [TableList(ShowIndexLabels = true,DrawScrollView = true, MaxScrollViewHeight = 200)]
    public List<CustomItem> List = new List<CustomItem>()
    {
        new CustomItem(),
        new CustomItem(),
        new CustomItem(),
    };

    [Serializable]
    public class CustomItem
    {    
        //指定宽度
        [PreviewField(Height = 20)]
        [TableColumnWidth(30, Resizable = false)]
        public Texture2D Icon;

        //指定宽度
        [TableColumnWidth(60)]
        public int ID;
        
        //隐藏该列
        [HideInTables]
        public string Name;
        
        //图标默认值
        [OnInspectorInit]
        private void CreateData()
        {
            Icon = ExampleHelper.GetTexture();
        }
    }
  • Rendering diagram:
    We use TableColumnWidth, HideInTables and the configuration of TableList's own fields to define the rendering of the list
    insert image description here

2.5.2 [ValueDropdown] drop-down box

  • Function:
    ValueDropdown can be used to display the drop-down box
  • Syntax:
    [ValueDropdown("method name")], modify the field
  • Example:
    // 普通下路框
    [ValueDropdown("TextureSizes")] 
    public int SomeSize1;
    
    private static int[] TextureSizes = new int[] {256, 512, 1024};

    // 下拉框是string描述,值是int的下拉框
    [ValueDropdown("FriendlyTextureSizes")]
    public int SomeSize2;

    private static IEnumerable FriendlyTextureSizes = new ValueDropdownList<int>()
    {
        {"Small", 256},
        {"Medium", 512},
        {"Large", 1024},
    };

    //下拉框是某个目录下的GameObject集合
    [ValueDropdown("GetAllSirenixAssets", IsUniqueList = true)]
    public List<GameObject> UniqueGameobjectList;

    private static IEnumerable GetAllSirenixAssets()
    {
        var root = "Assets/Plugins/Sirenix/";

        return UnityEditor.AssetDatabase.GetAllAssetPaths()
            .Where(x => x.StartsWith(root))
            .Select(x => x.Substring(root.Length))
            .Select(x => new ValueDropdownItem(x, UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(root + x)));
    }

    //下拉框是树形控件
    [ValueDropdown("TreeViewOfInts", ExpandAllMenuItems = true)]
    public List<int> IntTreview = new List<int>() {1, 2, 7};

    private IEnumerable TreeViewOfInts = new ValueDropdownList<int>()
    {
        {"Node 1/Node 1.1", 1},
        {"Node 1/Node 1.2", 2},
        {"Node 2/Node 2.1", 3},
        {"Node 3/Node 3.1", 4},
        {"Node 3/Node 3.2", 5},
        {"Node 1/Node 3.1/Node 3.1.1", 6},
        {"Node 1/Node 3.1/Node 3.1.2", 7},
    };
  • Renderings:
    insert image description here

2.6 Conditionals conditions

2.6.1 DisableIf/EnableIf/ShowIf/HideIf

  • Function:
    The usage of the above features is similar, you can guess the purpose according to the name. Here we take DisableIf as an example. DisableIf modifies the field, and when the condition is met, the modified field can be disabled and cannot be modified.
  • Example:
    public UnityEngine.Object SomeObject;

    [EnumToggleButtons]
    public InfoMessageType SomeEnum;

    public bool IsToggled;
    
    //1. 根据bool值
    //当IsToggled = true;此字段Disable
    [DisableIf("IsToggled")] [ShowIf()()]
    public int DisableIfToggled;

    //2. 根据枚举值
    //当枚举 SomeEnum = InfoMessageType.Info;此字段Disable
    [DisableIf("SomeEnum", InfoMessageType.Info)]
    public Vector2 Info;

    //当枚举 SomeEnum = InfoMessageType.Error;此字段Disable
    [DisableIf("SomeEnum", InfoMessageType.Error)]
    public Vector2 Error;


    
    //3. 根据GameObject
    //当GameObject SomeObject != null; 此字段Disable
    [DisableIf("SomeObject")]
    public Vector3 EnabledWhenNull;
    
    //4. 根据表达式
    [DisableIf("@this.IsToggled && this.SomeObject != null || this.SomeEnum == InfoMessageType.Error")]
    public int DisableWithExpression;
  • Renderings:
    Please add a picture description

2.6.2 DisableInEditorMode/DisableInPlayMode

  • Function:
    DisableInEditorMode & DisableInPlayMode are to disable fields in editor and luck respectively
  • Example:
    [Title("Disabled in edit mode")]
    [DisableInEditorMode]
    public GameObject A;

    [DisableInPlayMode] 
    public Material B;
  • Rendering:
    currently in editor mode
    insert image description here

Guess you like

Origin blog.csdn.net/aaa27987/article/details/131342023