Unity 编辑器扩展一 常用属性

参考
Unity 编辑器扩展总结 一:编辑器开发入门
Unity 编辑器扩展总结 二:编辑器的相关特性
Unity Editor 基础篇(一):Build-In Attribute
Unity拓展编辑器入门指南

一、简单示例(编辑器扩展是做什么的)

使用Visual studio在Assets/Editor下新建项


visual studio新建项

选Editor Script

脚本名就用默认的,visual studio会自动生成一个模板代码

using UnityEditor;
using UnityEngine;

namespace Assets.Editor
{
    public class NewEditorScript1 : ScriptableObject
    {
        [MenuItem("Tools/MyTool/Do It in C#")]
        static void DoIt()
        {
            EditorUtility.DisplayDialog("MyTool", "Do It in C# !", "OK", "");
        }
    }
}

菜单上会多出一个选项

这里把: ScriptableObject去掉也没关系,关于ScriptableObject以后再做了解。

在上面的示例中,通过指定MenuItem第二个参数为true,表示DeleteValidate为此菜单的有效函数。Selection.objects.Length == 0,此菜单才生效。

using UnityEditor;
using UnityEngine;

namespace Assets.Editor
{
    public class NewEditorScript1 : ScriptableObject
    {
        [MenuItem("Tools/MyTool/Do It in C#", true)]
        private static bool DeleteValidate()
        {
            if (Selection.objects.Length == 0)
                return true;
            else
                return false;
        }

        [MenuItem("Tools/MyTool/Do It in C#")]
        static void DoIt()
        {
            EditorUtility.DisplayDialog("MyTool", "Do It in C# !", "OK", "");
        }
    }
}

选项变灰了

unity编辑器扩展#2 GUILayout、EditorGUILayout 控件整理
unity编辑器扩展#3 《Extending Unity with Editor Scripting 》笔记

一、常用属性
[MenuItem(“MyTools/test1”,false,priority)]

前两个参数上面已经解释用处,第三个参数priority是优先级,用来表示菜单按钮的先后顺序,默认值为1000。一般菜单中的分栏,数值相差大于10。

1.实现点击菜单按钮,删除场景或者Project中选中的多个对象
[MenuItem("MyTool/DeleteAllObj", true)]
private static bool DeleteValidate()   
{
    if (Selection.objects.Length > 0)
        return true;
    else
        return false;
}

[MenuItem("MyTool/DeleteAllObj",false)]
private static void MyToolDelete()
{
    //Selection.objects 返回场景或者Project中选择的多个对象
    foreach (Object item in Selection.objects)
    {
        //记录删除操作,允许撤销
        Undo.DestroyObjectImmediate(item);
    }
} 
2.Selection 用于获取选择的游戏物体
  • Selection.activeGameObject 返回第一个选择的场景中的对象
  • Selection.gameObjects 返回场景中选择的多个对象,包含预制体等
  • Selection.objects 返回选择的多个对象
//遍历选择的对象,并立刻销毁
foreach(object obj in Selection.objects)
{
    DestroyImmediate(obj);
}

P.S. Destroy方法会将删除的对象放在缓存中,缓存满了,才完全删除,而在编辑器未运行的时候,是没有这片缓存的,所以需要用DestroyImmediate(),立刻销毁。当然,可以直接使用Undo.DestroyObjectImmediate()来销毁对象并记录销毁操作。

3.添加快捷键
  • % Ctr/Command
  • # Shift
  • & Alt
  • LEFT/Right/UP/DOWN 方向键
  • F1-F2 F功能键
  • _g 字母g

例如:[MenuItem(“MyTools/test1 %_q”)] 快捷键 Ctrl+Q

4.CONTEXT给某组件添加右键菜单选项

[MenuItem(“CONTEXT/组件名/按钮名”)]
注意CONTEXT大写

[MenuItem("CONTEXT/Rigidbody/Init")]
private static void RigidbodyInit() 
{
    //TODO
}

5.MenuCommand用于获取当前操作的组件

如下,给自定义的组件PlayerHealth添加右键Init按钮

[MenuItem("CONTEXT/PlayerHealth/Init")]
static void Init(MenuCommand cmd)
{
    PlayerHealth health = cmd.contex as PlayerHealth;
}
6.ContextMenu、ContextMenuItem

给某组件添加右边小齿轮菜单选项

[ContextMenu("FunctionName")]
public void FunctionName()
{
    //ToDo
}

给某属性添加右键菜单选项

[ContextMenuItem("Handle", "HandleHealth")]
public float health;

private void HandleHealth()
{
    //ToDo
}

P.S. 这两个特性是在UnityEngine命名空间下的,而不像其他[MenuItem]、Selection是在UnityEditor下的。

7.常用的属性特性
  • [Range(0,100)] //限制数值范围
  • [Multiline(3)] //字符串多行显示
  • [TextArea(2,4)] //文本输入框
  • [SerializeField] //序列化字段,主要用于序列化私有字段
  • [NonSerialized] //反序列化一个变量,并且在Inspector上隐藏
  • [HideInInspector] //public变量在Inspector面板隐藏
  • [FormerlySerializedAs(“Value1”)] //当变量名发生改变时,可以保存原来Value1的值
  • [ContextMenu(“TestBtn”)] //组件右键菜单按钮
  • [ContextMenuItem(“Reset Value”,“Reset”)] //定义属性的右键菜单
  • [Header(“Header Name”)] //加粗效果的标题
  • [Space(10)] //表示间隔空间,数字越大,间隔越大
  • [Tooltip(“Tips”)] //显示字段的提示信息
  • [ColorUsage(true)] //显示颜色面板
image.png
通过Space进行布局分割
8.常用的方法特性
  • [DrawGizmo] //用于Gizmos渲染,将逻辑与调试代码分离
  • [MenuItem] //添加菜单项
9.常用的类的特性
  • [Serializable] //序列化一个类,作为一个子属性显示在监视面板
  • [RequireComponent(typeof(Animator))] //挂载该类的对象,必须要有Animator组件
  • [DisallowMultipleComponent] //不允许挂载多个该类或其子类
  • [ExecuteInEditMode] //允许脚本在编辑器未运行的情况下运行
  • [CanEditMultipleObjects] //允许当选择多个挂有该脚本的对象时,统一修改值
  • [AddComponentMenu] //可以在菜单栏Component内添加组件按钮
  • [CustomEditor] //要自定义编辑器就要加这个特性
  • [CustomPropertyDrawer] //用于绘制自定义PropertyDrawer的特性
  • [SelectionBase] //选择在场景视图中使用此属性的组件对象,即不会误选中子物体

P.S. 多个特性可以用逗号隔开,例如:[SerializeField, Range(0,5)]

我们既想保证类的某个字段不被其他的类访问修改,又想在Inspector视窗中修改这个字段的值的话,就可以采用private +[SerializeField]属性的方案。

[SerializeField]
private int privateInt;
image.png
10.AddComponentMenu

AddComponentMenu 属性允许将一个脚本添加到 Component 菜单中,然后你便可以通过 Component ->(你设置的名字)为一个选中的游戏对象创建该脚本

[AddComponentMenu("Learn/Test")]
public class Test1 : MonoBehaviour
image.png
11.[RequireComponent(typeof(Animator))]

RequireComponent()属性会自动帮你添加你需要的组件,如果已经存在则不再重复添加,且不能移除

[AddComponentMenu("Learn/Test")]
[RequireComponent(typeof(Rigidbody))]
public class Test1 : MonoBehaviour
{
尝试移除失败

提示:经过测试,我发现一个问题,如果脚本已经挂在物体身上,然后再修改脚本,添加 RequireComponent 属性的话,完全不起作用,因此建议大家在用此属性的时候要注意。

12.ContextMenu

ContextMenu()属性允许添加一个命令到该组件上,你可以通过右键或者点击设置图标来调用到它(一般用于函数),且是在非运行状态下执行该函数,如下所示:

[AddComponentMenu("Learn/Test")]
[RequireComponent(typeof(Rigidbody))]
public class Test1 : MonoBehaviour
{
    public Text Text1;
    public bool myBool;
    public string myString;

    [ContextMenu("OutputInfo")]
    void OutputInfo()
    {
        Debug.Log("outputinfo");
    }
}
image.png
13.HelpURL

HelpURL()提供一个自定义的文档链接,点击组件上的文档图标既能打开到你指定的链接,如下所示:

[HelpURL("http://www.baidu.com")]
[AddComponentMenu("Learn/Test")]
[RequireComponent(typeof(Rigidbody))]
public class Test1 : MonoBehaviour
image.png
14.Range、Multiline、header

Range()属性用于将一个值指定在一定的范围内,并在Inspector面板中为其添加滑块;Multiline()属性用于给 string 类型添加多行输入;header()属性用于添加属性的标题,具体操作如下所示:

public class Test1 : MonoBehaviour
{
    public Text Text1;
    public bool myBool;

    [Header("BaseInfo")]
    [Multiline(5)]
    public string myString;

    [Range(-2, 2)]
    public int age;
image.png
15.Tooltip、Space

Tooptip()属性用于在 Inspector 面板中,当鼠标停留在设置了Tooptip()的属性添加指定的提示;Space()用于为在 Inspector 面板两属性之间添加指定的距离,如下所示:

    [Range(-2, 2)]
    public int age;

    [Space(100)]
    [Tooltip("用于设置性别")]
    public string sex;
image.png
二、编辑器相关文件夹介绍
1.Editor
  • 该文件夹可以放在项目的任何文件夹下,可以有多个"Editor"文件夹。
  • 编辑器扩展相关的脚本都要放在该文件夹内,该文件夹中的脚本只会对Unity编辑器起作用。
  • 项目打包的时候,不会被打包到项目中。如果编辑器相关脚本不放在该文件夹中,打包项目可能会出错。
  • 如果非要有些编辑器相关脚本不放在该文件夹中,需要在该类的前后加上UNITY_EDITOR的宏定义
2.Editor Default Resources
  • 该文件夹需要放在Assets根目录下,用来存储编辑器所需要的图片等资源,书写的时候需要注意中间有空格隔开。此文件夹也不会被打包,访问方法为:EditorGUIUtility.Load()
  • 当然,也可以在Editor文件夹内创建一个Resources文件夹,将相关资源放在该文件夹内,通过Resources.Load()获取资源,也是可以的
3.Gizmos
  • 该文件夹也需要放在Assets根目录下,可以用来存放Gizmos.DrawIcon()的图片资源
4.一般继承MonoBehaviour的脚本都放在Assets/Scripts文件夹里,而使用using UnityEditor;这个命名空间的脚本都放在Assets/Editor文件夹里。本系列文章后续例子中的代码,不再重复说明此点。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容