Unity编辑器功能及拓展(3) —[Attribute]特性

在 Unity 中,[Attribute]格式的特性是用于扩展编辑器功能、控制序列化行为和调整 Inspector 显示,进行编辑器拓展的核心工具。

一.基础编辑器拓展

1.基础序列化控制

1.[SerializeField] 强制显示私有变量到Inspector

2.[HideInInspector] 隐藏该字段在Inspector中的显示

3.[NonSerialized] 阻止该字段被序列化

4.[Serializable] 对类内类使用

2.Inspector布局优化

1.[Header("Title")] 为该字段添加标题

2.[Tooltip("Description")] 鼠标悬停显示提示文本

3.[Space(int)] 插入垂直距离,修改字段显示垂直间距

4.[TexArea] 动态拓展多行文本输入框

5.[Multiline(int)] 多行文本输入框

3.数值约束

1.[Range(min,max)] float类型变量使用,滑动条约束

2.[Min(minValue)] 设置最小值

4.警告提示

1.[Obsolete("已弃用的提示")]:警告该标志字段,已弃用

示例代码

cs 复制代码
 [SerializeField, Range(0, 1)] float ID;
 [HideInInspector] public int index;
 public Student student;
 [TextArea]
 [Space(5)]
 [SerializeField]
 string description;
 [Obsolete("弃用的错误的数值")]
 int wrongNum;
 int trueNum;

 private void ObsoleteTest()
 {
     wrongNum++;
 }


[Serializable]
public class Student
{
    [Header("年龄"), Tooltip("以阴历为准"), Min(0)]
    public int age;
}

二.高级编辑器拓展

1.脚本标记

1.[ExecuteInEditorMode]:该脚本在编辑器模式下运行

2.[RequireComponment(typeof(TypeName ))]:挂载本脚本时将自动添加目标组件

3.[DisallowMultipleComponent]:禁止同一物体重复挂载本脚本

4.[CustomEditor(typeof(TypeName))]:本脚本对目标类型脚本进行编辑器面板下的拓展

第四种较为特殊,在这里详细介绍

[CustomEditor(typeof(TypeName))]

使用该特性,需要编写一个实例脚本及一个对其进行拓展的继承Editor的脚本。

实例脚本Test

示例代码

cs 复制代码
using UnityEngine;

[ExecuteInEditMode]//编辑模式下任意操作引起帧运行
[DisallowMultipleComponent]//禁止重复挂载
[RequireComponent(typeof(Rigidbody))]
public class Test : MonoBehaviour
{
    public int ID;
    public string Name;
    public bool isDead;
    public GameObject weapon;
    public Texture texture;
    public E_testEnum testEnum;
    public float processSlider;
    public Player player;

    void Update()
    {
        //  Debug.Log("操作了一下");
    }

    public void TestDebug()
    {
        Debug.Log("Test!");
    }
}

public enum E_testEnum
{
    test1, test2, test3
}

编辑器脚本TestEditor

我们需要重写实现Editor中的OnInspectorGUI()方法,在脚本组件面板上绘制信息。

当OnInspectorGUI()函数中无功能代码时,发现Test脚本无法正常显示信息(见下图)

此时,可以使用DrawDefaultInspector()来进行默认字段的正常绘制。

cs 复制代码
 public override void OnInspectorGUI()
 {
     DrawDefaultInspector();
 }

接下来我们来自定义绘制一下所有字段。

示例代码

cs 复制代码
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(Test))]
public class TestEditor : Editor
{
    //获得需要编辑显示的组件
    private Test testComponent;

    //当关联组件所在对象被选中或组件被添加时调用
    private void OnEnable()
    {
        testComponent = (Test)target;//在当前的外挂脚本中获得需要被拓展的test组件对象
    }

    //当关联组件所在对象被取消选中或组件爱你被移除时调用
    private void OnDisable()
    {
        testComponent = null;
    }
    //OnInspectorGUI是用于绘制检视面板的生命周期函数
    public override void OnInspectorGUI()
    {
        //标题显示
        EditorGUILayout.LabelField("测试拓展脚本");

        //简单数据类型绘制
        testComponent.ID = EditorGUILayout.IntField("角色ID", testComponent.ID);
        testComponent.Name = EditorGUILayout.TextField("角色姓名", testComponent.Name);
        testComponent.isDead = EditorGUILayout.Toggle("是否死亡", testComponent.isDead);

        //对象数据类型绘制/
        testComponent.weapon = EditorGUILayout.ObjectField("武器", testComponent.weapon, typeof(GameObject), true) as GameObject;
        testComponent.texture = EditorGUILayout.ObjectField("贴图", testComponent.texture, typeof(Texture), false) as Texture;
        //标题,原始组件的值,成员变量的类型,是否可以将场景中对象拖给这个变量,注意纹理来源于project而非场景中物体,故用false

        //枚举数据类型绘制
        testComponent.testEnum = (E_testEnum)EditorGUILayout.EnumPopup("玩家职业", testComponent.testEnum);

        //终极数据类型绘制(适用于list等,以及自己写的类型的数据)
        OtherDataDraw("player", "玩家");

        //滑动条绘制
        testComponent.processSlider = EditorGUILayout.Slider(new GUIContent("滑动条"), testComponent.processSlider, 0, 100);
        if (testComponent.processSlider >= 80)
        {
            EditorGUILayout.HelpBox("滑动条即将到顶", MessageType.Error);
        }
        if (testComponent.processSlider <= 20)
        {
            EditorGUILayout.HelpBox("滑动条即将到底", MessageType.Warning);
        }

        //按钮绘制(默认纵向绘制,一行占一个按钮)
        if (GUILayout.Button("来个按钮"))
        {
            Debug.Log("点击了按钮");
        };

        //开启横向绘制(一行可多个按钮)
        GUILayout.BeginHorizontal();
        //关闭横向绘制
        GUILayout.EndHorizontal();

    }

    void OtherDataDraw(string originalDataName, string processedHeadName)
    {
        //更新可序列化数据
        serializedObject.Update();
        //通过成员变量名找到组件上的成员变量
        SerializedProperty sp = serializedObject.FindProperty(originalDataName);
        //可序列化数据绘制(取到的数据,标题,是否将所有获得的序列化数据显示)
        EditorGUILayout.PropertyField(sp, new GUIContent(processedHeadName), true);
        //将修改的数据写入到可序列化的原始数据中
        serializedObject.ApplyModifiedProperties();

    }
}

2.[AddComponment( )]

标记实例脚本,编辑器面板Componment菜单拓展

使用:点击拓展脚本即可为目标物体添加脚本组件。

常用重载

1.AddComponentMenu(string menuName)

编辑器面板Componment菜单拓展,标记实例脚本。

2.AddComponentMenu(string menuName, int order)

编辑器面板Componment菜单拓展,标记实例脚本。

参数order表示拓展脚本按钮在面板中的优先级,值越小,在面板中位置越靠上。

示例代码

cs 复制代码
[AddComponentMenu("MyAddC/addc1", 1)]
public class AddC1 : MonoBehaviour{
}

注意:先选中场景内游戏物体,再为其添加脚本~

3.[MenuItem( )]

编辑器面板菜单拓展,标记静态函数。点击拓展方法即可在编辑器面板下执行。

常用重载

1.[MenuItem("MenuName/FuncName")]

编辑器面板菜单拓展,标记静态方法。

cs 复制代码
//增加菜单栏选项
[MenuItem("MyTools/SendMes1 &1", false, 900)]
static void SendMes1()
{
    print("Print1");
}

2.[MenuItem("MenuName/FuncName",int priority)]

编辑器面板菜单拓展,标记静态方法。

参数priority表示拓展方法在面板中的优先级,值越小,在面板中位置越靠上。

优先级每差出10级,面板下多一条分割线(见下图)


3.[MenuItem("CONTEXT/组件名称/拓展功能名称")]

Inspector拓展脚本缩略点内功能,对当前实例脚本内非静态方法使用。

示例代码

cs 复制代码
  //给某目标类型组件添加右键菜单选项【"CONTEXT/Rigibody(组件名称)/Init(按钮名称)"】
  [MenuItem("CONTEXT/Rigidbody/Init")]
  static void RigInit()
  {
      Debug.Log("我是Rigibody");
  }

4.[ContextMenu( )]

常用重载

1.[ContextMenu("功能描述")]

Inspector拓展脚本缩略点内功能,对当前实例脚本内非静态方法使用。

2.[ContextMenuItem("功能描述", "实例方法名称")]

Inspector拓展字段右键菜单功能,对当前实例脚本内非静态方法使用。

优化:nameof( )关键字替代"实例方法" ,便于后期维护。

示例代码

cs 复制代码
public class AttributesTest : MonoBehaviour
{
    [ContextMenuItem("增加10点力量", "AddPower")]
    public int power;

    //对当前实例脚本使用,方法需非静态,打开脚本右侧缩略点可直接调用该方法
    [ContextMenu("组件目录菜单")]
    void Init()
    {
        Debug.Log("方法调用");
    }

    void AddPower()
    {
        power += 10;
    }
}

以上是我学习过程中总结的印象中大致所有的Attribute特性及其应用,可能会有瑕疵或缺漏,欢迎大家不吝赐教。

本篇完!

相关推荐
孟无岐4 小时前
【Laya】Laya 类使用说明
typescript·游戏引擎·游戏程序·laya
在路上看风景6 小时前
1.2 Unity资源分类
unity·游戏引擎
one named slash6 小时前
BMFont在Unity中生成艺术字
unity·游戏引擎
微醺的老虎7 小时前
【工具】vscode格式化json文件
ide·vscode·编辑器
乔宕一7 小时前
vscode 设置每次调试 powershell 脚本都使用临时的 powershell 终端
ide·vscode·编辑器
郝学胜-神的一滴8 小时前
图形学中的纹理映射问题:摩尔纹与毛刺的深度解析
c++·程序人生·unity·游戏引擎·图形渲染·unreal engine
在路上看风景8 小时前
10. CPU-GPU协作渲染
unity
程序员agions9 小时前
Unity 游戏开发邪修秘籍:从入门到被策划追杀的艺术
unity·cocoa·lucene
JIes__9 小时前
Unity(一)——场景切换、退出游戏、鼠标隐藏锁定...
unity·游戏引擎
山峰哥9 小时前
数据库工程与SQL调优实战:从原理到案例的深度解析
java·数据库·sql·oracle·性能优化·编辑器