Unity编辑器

检视面板简单扩展(Unity特性)

官方文档

相关文档

特性 (Attribute) 是可以放在脚本中的类、属性或函数上方来指示特殊行为的标记。

  • System命名空间下
特性 描述 代码描述 具体体现
Serializable 序列化一个类,作为另一个类一个子属性在监视面板上显示。与Unity的内置JSON工具运行原理相似。 alt alt
NonSerialized 反序列化一个变量,并且在监视版上隐藏。 alt alt B在检视面板上没有显示
Flags 定义标志枚举,并实现在检视面板中的多选框(注意:枚举类型的字段默认为单选且是序列化的)实现多选的枚举值应为2的次幂,需要支持位运算 alt alt
  • UnityEngine命名空间下
特性 描述 代码描述 具体体现
RequireComponent(组件类型Type) 自动添加需要的组件。若已存在则不额外添加。这样脚本就可以安全的使用该组件。自动添加的组件不能先于该脚本删除 alt alt
ContextMenuItem(在检视面板中显示的内容,回调函数) 给字段右键菜单添加一个自定义方法,不能是静态的。 alt alt
ContextMenu(在检视面板中显示的内容) 为脚本设置菜单添加回调函数,不能是静态的。 alt alt
AddComponentMenu(组件的路径,order) 在编辑器中添加组件菜单,路径中的脚本名称会对实际的脚本名称产生更改 alt alt
CreateAssetMenu(fileName = 路径包含名称, menuName = 显示内容, order = 1) 用于ScriptableObject(参考链接)的子类(Editor),在Project面板中创建并添加资源 alt alt
HideInInspector 在检视面板隐藏公有成员;不改变序列化属性。 alt alt
SerializeField 在检视面板中使私有变量可见;并且可以进行修改;公有变量默认是可序列化的 alt alt
ExecuteInEditMode 在编辑器模式下运行生命周期函数,但不同于在游戏模式那样时刻运行;运行条件:1、Update:在这个场景中任意游戏对象或项目组织发生变化了执行;2、OnGUI:在游戏视口接收到一个Event时执行;3、OnRenderObject:和其他渲染回调函数在场景视口或游戏视口重新渲染时执行。 alt
Space(需要的空行像素) 用于在检视面版的字段上加空行 alt alt
Header(标题内容) 标题特性,为检视面版中的字段加一个小标题。 alt alt
Multiline(显示的行数) 可以让string变量在检视面板上显示多行 alt alt
TextArea(最小行,最大行) string在检视面板上显示成带滚动条的文本域。 最小行:初始显示的行数;最大行:当行数超出最大行显示滚动条 alt alt
Tooltip(提示信息) 为检视面板上的字段添加提示信息。及鼠标指向字段显示的提示。 alt alt
Range(最小值, 最大值) 在检视面板限制int或float类型变量值。 alt alt

UnityEditor命名空间下

特性 描述 代码描述 具体体现
MenuItem(菜单路径包含名称,等校验函数,优先级) 添加菜单项,只能修饰静态方法。第二个参数若为true,则会先判断该方法是否返回true,若是,则可以使用,若为false,则该按钮是不可用的(灰色的)。 alt在现有组件菜单中添加方法:alt alt在现有组件菜单中添加方法:alt
ContextMenuItem(上下文菜单的名称,被回调的函数名称) 为公有的字段添加右键方法,添加的方法为非静态的 alt alt
ContextMenu(上下文菜单的名称,是否为validate(验证)函数(默认为false),priority优先级用于覆盖菜单项的顺序(默认为1000000)。数字越低,在菜单中显示的越早。) 为组件添加右键方法,添加的方法为非静态 alt alt
CustomEditor(type) 要自定义编辑器;type为需要自定义脚本编辑器的脚本类型;将type类型的脚本与相应的编辑器脚本做关联 alt alt

编辑器扩展

编辑器扩展

编辑器弹窗

  • 脚本实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

/// <summary>
/// 弹出窗口
/// </summary>
public class PopWindow : EditorWindow
{
    [MenuItem("工具/弹出编辑窗口")]
    static void PopWindowEditor()
    {
        //EditorWindow window = GetWindow(typeof(PopWindow));
        //GetWindow:获取一个窗口
        EditorWindow window = GetWindow<PopWindow>();
        window.maxSize = new Vector2(500, 800);
    }
    //脚本生命周期
    private void OnEnable()
    {
        Debug.Log("调用了OnEnable函数");
    }
 	//脚本生命周期
    private void OnDisable()
    {
        Debug.Log("调用了OnDisable函数");
    }
  	//脚本生命周期
    private void Update()
    {
        if (Selection.activeGameObject == null) return;
        GameObject gameObject = Selection.activeGameObject;
        Debug.Log(gameObject.name);
    }
  	//脚本生命周期
    private void OnGUI()
    {
        GUILayout.Label("学校");
        string text="123456";
        GUILayout.TextField(text);
        GUILayout.BeginHorizontal();
        if (GUILayout.Button("打印123"))
        {
            Debug.Log("123");
        }
        if (GUILayout.Button("打印456"))
        {
            Debug.Log("456");
        }
        GUILayout.EndHorizontal();
    }
  	//脚本生命周期
    private void OnSelectionChange()
    {
        Debug.Log("选择的游戏物体发生了改变--调用了OnSelectionChange函数");
    }
  	//脚本生命周期
    private void OnHierarchyChange()
    {
        Debug.Log("层级面板发生了改变--调用了OnHierarchyChange函数");
    }
  	//脚本生命周期
    /// <summary>
    /// 每秒调用十帧;检测检视面板的更新
    /// </summary>
    private void OnInspectorUpdate()
    {
        //Debug.Log("检视面板进行了更新--调用了OnInspectorUpdate函数");
    }
}
  • 效果展示

alt

检视器高级修改(外挂式编程)

  • 项目中建立Editor目录

  • Editor目录下建立外挂式开发脚本

  • 将编辑器脚本与原始脚本关联

  • 原始脚本 Player类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//角色职业
public enum PLAYER_PROFESSION
{
    Warrior = 0,
    Wizard = 1,
}
//角色天赋(要理解二级制或,可以实现多选)
[System.Flags]
public enum PLAYER_TALENT
{
    Strength = 1,
    Agility = 2,
    Intelligence = 4,
}
//在AddComponent菜单中添加组件 参数一:路径包括名称 ;参数二:在组件菜单中添加新项。??
[AddComponentMenu("控制器/玩家控制器", 1)]
[ExecuteInEditMode]//在编辑器下执行脚本生命周期;当对象发生变化或项目组织发生变化时运行
public class Player : MonoBehaviour
{
    public int ID;
    public string Name;
    public float Atk;
    public bool isMan;
    public Vector3 HeadDir;
    public Color Hair;
    public GameObject Weapon;
    public Texture Cloth;
    public PLAYER_PROFESSION Profession;
    public PLAYER_TALENT Talent;
    public List<string> Items;
    [ContextMenu("打印1122")]//在脚本编辑菜单添加回调函数
    private void A()
    {
        print("1122");
    }
}
  • 原始脚本显示效果

alt

  • 原始脚本的外挂式脚本 PlayerEditor
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;//步骤1:检视器的扩展开发属于编辑器范畴,所以使用 UnityEditor 命名空间


/// <summary>
/// Player的外挂脚本
/// </summary>
[CustomEditor(typeof(Player))]//步骤3:将编辑器脚本与需要编辑的脚本建立外挂的关联关系
public class PlayerEditor : Editor//步骤2:继承Editor,使用声明周期函数及成员变量,参考 MonoBehavior
{
    private Player player;
    private PopWindow popWindow;
    bool isClick;

    //步骤4:确定,什么时候显示内容在监视器面板中,即使用生命周期函数
    /// <summary>
    /// 脚本生命周期
    /// 当组件被启用(被挂载到游戏物体上)或处于激活状态或者被点击
    /// </summary>
    private void OnEnable()
    {
        player = target as Player;//target:与该编辑器脚本相关联的对象
        Debug.Log(player.gameObject.name);
        Debug.Log("调用了OnEnable");
    }
    /// <summary>
    /// 重写Editor中的函数 实现自定义的检视面板检查器;每帧都会运行,使用到类: EditorGUILayout
    /// </summary>
    public override void OnInspectorGUI()
    {
        //base.OnInspectorGUI();
        EditorGUILayout.LabelField("玩家信息");
        //使用绘制方法,重新绘制ID字段(获取原始ID,绘制后,再对原始ID赋值)
        //                             参数:属性名称,属性值
        player.ID = EditorGUILayout.IntField("玩家ID", player.ID);
        //文本
        player.Name = EditorGUILayout.TextField("玩家名称", player.Name);
        //浮点数
        player.Atk = EditorGUILayout.FloatField("玩家攻击力", player.Atk);
        #region 滑块与提示盒子
        player.Atk = EditorGUILayout.Slider("玩家攻击力滑条", player.Atk, 0, 100);
        if (player.Atk > 80)
        {
            EditorGUILayout.HelpBox(new GUIContent().text = "攻击力高", MessageType.Warning);
        }
        if (player.Atk < 20)
        {
            EditorGUILayout.HelpBox(new GUIContent().text = "攻击力低", MessageType.Error);
        }
        #endregion
        //布尔
        player.isMan = EditorGUILayout.Toggle("是否为男性", player.isMan);
        //向量
        player.HeadDir = EditorGUILayout.Vector3Field("头部方向", player.HeadDir);
        //颜色
        player.Hair = EditorGUILayout.ColorField("头发颜色", player.Hair);
        #region 按钮
        if (!isClick && GUILayout.Button("第一个按钮"))
        {
            popWindow = PopWindow.GetWindow<PopWindow>();
            isClick = true;
        }
        else if (isClick && GUILayout.Button("第一个按钮按下了"))
        {
            popWindow.Close();
            isClick = false;
        }
        GUILayout.BeginHorizontal();
        GUILayout.Button("第二个按钮");
        if (GUILayout.Button("点击打印123"))
        {
            Debug.Log("123");
        }
        GUILayout.EndHorizontal();
        #endregion
        #region 单选与多选枚举    参数:标题, 组件上的原始值
        player.Talent = (PLAYER_TALENT)EditorGUILayout.EnumFlagsField(new GUIContent().text = "角色天赋", player.Talent);
        player.Profession = (PLAYER_PROFESSION)EditorGUILayout.EnumPopup(new GUIContent().text = "角色职业", player.Profession);
        #endregion

        ////终极数据类型绘制////////////////////////////////////////////////////////
        //更新可序列化数据
        serializedObject.Update();
        //通过成员变量名找到组件上的成员变量
        SerializedProperty sp = serializedObject.FindProperty("Items");
        //可序列化数据绘制(取到的数据,标题,是否将所有获得的序列化数据显示出来)
        EditorGUILayout.PropertyField(sp, new GUIContent("道具信息"), true);
        //将修改的数据,写入到可序列化的原始数据中
        serializedObject.ApplyModifiedProperties();

        ////对象数据类型绘制////////////////////////////////////////////////////////
        //参数1:标题
        //参数2:原始组件的值
        //参数3:成员变量的类型
        //参数4:是否可以将场景中的对象拖给这个成员变量
        player.Weapon = EditorGUILayout.ObjectField("武器", player.Weapon, typeof(GameObject), true) as GameObject;
        player.Cloth = EditorGUILayout.ObjectField("纹理", player.Cloth, typeof(Texture), false) as Texture;
    }
    /// <summary>
    /// 脚本生命周期
    /// 当组件不被启用(未被挂载到游戏物体上)或未处于激活状态或者未被点击
    /// </summary>
    private void OnDisable()
    {
        Debug.Log("调用了OnDisable");
    }
}
  • 原始脚本的外挂式脚本显示效果

alt

  • 两个按钮叠加的效果
if (!isClick && GUILayout.Button("第一个按钮"))
{
    popWindow = PopWindow.GetWindow<PopWindow>();
    isClick = true;
}
else if (isClick && GUILayout.Button("第一个按钮按下了"))
{
     popWindow.Close();
     isClick = false;
}
  • 效果展示

alt

Selection类

Selection.activeGameObject:获取和设置激活并选中的游戏对象

OnValidate方法

对一些数值进行验校 参考链接

编辑器模式下OnValidate 仅在下面两种情况下被调用:

  • 脚本被加载时
  • Inspector 中的任何值被修改时 下面是脚本中如何使用它:
public string str;
private void OnValidate()
{
    str = str.ToUpper();
}//使字符串始终显示大写状态

效果:

alt