Unity Custom Editor

Unity中可以通过编写脚本实现自定义的Editor。好用Editor可以使开发事半功倍。 Asset Store上大部分第三方插件如NGUI、PlayMaker等都包含自定义Editor的部分。 自定义Editor主要包含两部分:Editor Window和Custom Editor。

Editor Window

顾名思义就是一个单独窗口的Editor。

简单的例子在官方网站上可以找到,这里不再赘述。
这里讨论几个比较重要的部分:

1, 区域划分

Unity提供了EditorGUILayout.BeginHorizontal()EditorGUILayaout.BeginVertical()这样的方法,用于声明一个元素是横向/纵向排列的新block。 其用法类似于HTML中的<table><tr><table><td>。 当然,不要忘记在结束一个区域的时候调用EditorGUILayout.EndHorizontal()EditorGUILayaout.EndVertical()。 特别是在代码中有break, return的时候要注意。 区域不闭合Unity会报错的。

2, 格式调整与样式定义

一般有两种方法:GUILayoutOption和GUIStyle

GUILayoutOption

  • Height
  • Width
  • MaxHeight
  • MaxWidth
  • MinHeight
  • MinWidth
  • ExpandHeight
  • ExpandWidth

基本上就是以上这几种。涵盖了默认长宽,最大/最小长宽,是否可以拉伸长宽等等设定。 如果对于格式要求不多的话以上基本够用。 用法如下:

EditorGUILayout.BeginVertical(GUILayout.Width(blockWidth),GUILayout.ExpandHeight(true)
);

GUIStyle

如果一定要像在HTML里一样自定义Margin, Border, Padding, OnHover, OnFocus…那么推荐使用GUIStyle。 使用方法是新建一个GUISkin,然后编辑,最后在代码中载入编辑好的GUISkin中对应的样式。 GUIStyle可以满足对于自定义样式的种种需求,但是缺点是需要很多“准备工作”,并不像GUILayoutOption一样拿来就能用。 例子如下:

GUISkin editorGUISkin = (GUISkin)Resources.Load("EditorGUISkin");
GUIStyle blockStyle = editorGUISkin.customStyles[0];
EditorGUILayout.BeginVertical(blockStyle, GUILayout.Width(stateListBlockWidth));

3, 事件处理

这里主要讨论鼠标事件处理。

Event.current.Type

当前事件的类型,典型的有

  • EventType.mouseDown
  • EventType.mouseUp
  • EventType.mouseDrag
  • EventType.scrollWheel

Event.current.button

当前按键类型,默认值为

  • 0 => 鼠标左键
  • 1 => 鼠标右键
  • 2 => 鼠标中键

Event.current.delta

这是一个Vector2的变量,可以用在处理鼠标滚轮事件上。 判断Event.current.delta.y的值即可。

以上的变量结合起来使用基本上可以编写出大部分处理鼠标事件的代码。 当然Editor Window也可以处理键盘事件,但是用的不是很广泛这里不复赘述。

重绘的时机

Editor Window的窗口绘制基本上在OnGUI()函数中进行,而OnGUI函数并不是每一帧都被调用的。 通常只有满足了重绘条件时才会被调用,比如将键盘焦点汇集到窗口上或者元素被更新等等。 也可以在脚本中通过this.Repaint()手动触发重绘操作。

因此想要使得Editor Window拥有更加流畅的体验则必须注意触发重绘函数的时机。 实际上Unity本身提供的组件如SlideBar, ScrollView等等都不需要进行特殊的设置即可满足我们的要求。 问题在于一些自定义的UI组件。 如果一个自定义的UI组件在相应各类事件时有卡顿的现象,则说明其重绘的时机可能有问题。 一般而言,是重绘没有跟上UI元素的更新,解决方法是在想要更新UI组件表现的时候使用Event.current.Use()函数。

Event.current.Use()

这个函数的作用是“吃掉”当前的事件,使得在这行代码之下的代码无法继续使用这个事件。(会看到Event.current.Type == EventType.Used) 同时,在本次OnGUI执行完成后会立即进行重绘,及时更新UI的信息。 很多自定义组件的Drag操作,如果不使用Event.current.Use()函数都会变的很不自然,就是因为更新了UI信息(比如位置)之后没有及时触发重绘操作导致的。

4,例子——可变尺寸区域

按照上述方法基本可以很快的划分好窗口的区域并将各个元素放到指定的位置。 相比于Unity的默认窗口,使用Unity提供的默认组件无法简单的构建一个可以自动调节大小的区域。 以下的例子实现了一个可以调节大小的区域。

void drawVerticalResizeBlock(ref float parameterToResize)
{float blockWidth = 0f;Rect blockRect = EditorGUILayout.BeginVertical(GUILayout.Width(blockWidth),GUILayout.ExpandHeight(true));EditorGUILayout.EndVertical();Rect resizeBlockRect = new Rect(blockRect.xMin - resizeDetectSize,blockRect.yMin,blockRect.width + 2 * resizeDetectSize,blockRect.height);EditorGUIUtility.AddCursorRect(resizeBlockRect, MouseCursor.ResizeHorizontal);if (resizeBlockRect.Contains(Event.current.mousePosition)){if (Event.current.type == EventType.mouseDown){mouseOffset = Event.current.mousePosition.x - parameterToResize;Event.current.Use();}if (Event.current.type == EventType.mouseDrag){parameterToResize = Event.current.mousePosition.x - mouseOffset;Event.current.Use();}}}void drawHorizontalResizeBlock(ref float parameterToResize)
{float blockHeight = 0f;Rect blockRect = EditorGUILayout.BeginHorizontal(GUILayout.Height(blockHeight),GUILayout.ExpandWidth(true));EditorGUILayout.EndHorizontal();Rect resizeBlockRect = new Rect(blockRect.xMin,blockRect.yMin - resizeDetectSize,blockRect.width,blockRect.height + 2 * resizeDetectSize);EditorGUIUtility.AddCursorRect(resizeBlockRect, MouseCursor.ResizeVertical);if (resizeBlockRect.Contains(Event.current.mousePosition)){if (Event.current.type == EventType.mouseDown){mouseOffset = Event.current.mousePosition.y - parameterToResize;Event.current.Use();}if (Event.current.type == EventType.mouseDrag){parameterToResize = Event.current.mousePosition.y - mouseOffset;Event.current.Use();}}
}

Custom Editor

Custom Editor包含Inspector、Scene等部分,这里主要讨论Custom Inspector的部分。

1, OnInspectorGUI与DrawDefaultInspector

Custom Editor的使用方法与Editor Window大同小异。 需要注意的是Inspector的绘制是在OnInspectorGUI函数中完成的。 此外,所继承的基类不是EditorWindow而是UnityEditor.Editor。 这个类中提供了一个DrawDefaultInspector的函数,调用即可绘制默认的Inspector。

2,serializedObject与Target

Inspector的主要用途在于编辑其所依附的MonoBehavior的数据。 编辑的方法通常有两种。

serializedObject

OnInspectorGUI中可以操作一个名为serializedObject的对象。 这个对象即为MonoBehavior序列化之后的实例。 通过调用serializedObject.FindProperty(“PropertyName”)可以得到一个类型为SerializedProperty的属性。 可以通过把该属性绑定到编辑框里(一般是PropertyField)来实现在Inspector上编辑该属性的效果。 实例如下:

// 更新serializedObject的值
serializedObject.Update();SerializedProperty sp = serializedObject.FindProperty("intField");
EditorGUILayout.PropertyField(sp, new GUIContent("IntField"), GUILayout.Width("100"));// 将更新的值写回
serializedObject.ApplyModifiedProperties();

target

一般的默认类型属性都可以通过serializedObject + PropertyField搞定。 但是对于自定义类型的属性有些时候行不通,这时候需要依靠target对象。 例子如下:

CustomClass cc = target as CustomClass;cc.IntField = 30;
cc.CustomMethod("Hello World");//提示Unity更新target信息
EditorUtility.SetDirty(target);

自定义Editor的其他玩法

除了可以自定义Editor Window和Inspector,还可以在UnityEditor.Editor类里实现OnSceneGUI函数自定义SceneView中的表现。 篇幅所限,这里不加赘述,有兴趣的读者可以参考文档。

Unity自定义Editor简明教程相关推荐

  1. Unity 自定义Editor 地图编辑工具_使用说明

    Unity 自定义Editor 地图编辑工具: 自定义六边形网格数据并生成平面地图 1.打开自定义编辑编辑器 2.设置参数生成网格: 设置参数,点击Create Grid 2.编辑网格类型: !!!S ...

  2. Unity DOTS简明教程

    什么是DOTS 首先,先来了解下什么是DOTS? DOTS是Data-Oriented-Tech-Stack,官方中文翻译是:多线程式数据导向型技术堆栈. 它主要由三部分组成: 任务系统 实体组件系统 ...

  3. GCC编译器简明教程(Linux下C语言开发环境的搭建)

    GCC编译器简明教程(Linux下C语言开发环境的搭建) 市面上常见的Linux都是发行版本,典型的Linux发行版包含了Linux内核.桌面环境(例如GNOME.KDE.Unity等)和各种常用的必 ...

  4. UNITY 2D入门基础教程 (一)

    如果用以前版本的Unity做2D游戏,虽然能做,但是要费很多周折. 比如你可以将一张纹理赋予一个"面片"网格,然后用脚本控制它的动画调整它的位移.如果你要使用物理引擎,那么还要将这 ...

  5. JabRef文献管理软件简明教程

    本文转载自:https://www.cnblogs.com/tsingke/p/4523908.html JabRef 文献管理软件简明教程 大多只有使用LaTeX撰写科技论文的研究人员才能完全领略到 ...

  6. JabRef 文献管理软件简明教程

    JabRef 文献管理软件简明教程 大多只有使用LaTeX撰写科技论文的研究人员才能完全领略到JabRef的妙不可言,但随着对Word写作平台上BibTeX4Word插件的开发和便利应用,使用Word ...

  7. Unity 2D入门基础教程

    作者:Christopher LaPollo 翻译:Xiaoke 写在前面的前面的话:首先感谢原作者和译者,这是一篇非常棒的文章! 写在前面的话:转载肯定会留原文链接,作者的署名,这是毋庸置疑的.而我 ...

  8. sqlalchemy mysql_SQLAlchemy简明教程

    原文可见:SQLAlchemy简明教程 - Jiajun的编程随想 SQLAlchemy是Python中常用的一个ORM,SQLAlchemy分成三部分: ORM,就是我们用类来表示数据库schema ...

  9. 简明python教程在线-Python简明教程

    Python简明教程在线阅读地址: https://bop.molun.net/ DocStrings 该文档字符串所约定的是一串多行字符串,其中第一行以某一大写字母开始,以句号结束.第二行为空行,后 ...

  10. Unity 4.3 2D 教程:新手上路

    这篇文章译自 Christopher LaPollo 先生的 Unity 4.3 2D 教程的第一部分 Unity 4.3 2D Tutorial: Getting Started 感谢这套优秀教程的 ...

最新文章

  1. 2022-2028年中国三网融合产业深度调研及投资前景预测报告
  2. 被公司圈养的年轻人,如何避免被市场淘汰?
  3. 含有自增字段的插入问题
  4. [POJ] 3687 Labeling Balls(拓扑排序)
  5. python getattr_Python中的getattr()函数详解
  6. 【Linux】Linux中Vim基础
  7. c语言程序设计的一般错误的是,《C语言程序设计》第十章 程序常见错误分析.pdf...
  8. Vue+Vue Router+Vuex页面演示
  9. 时隔5年 三星要重回苹果闪存零部件供应商名单了
  10. 【转】WPF 窗体淡入淡出动画
  11. ios开发-教程选择
  12. Collaborative Evolutionary Reinforcement Learning
  13. [sudo rm -rf /bin/] Linux误删除/bin目录恢复
  14. 软件工程——理论与实践(第二版)课后习题整理
  15. Unity游戏开发客户端面经——算法(初级)
  16. nuget 的生成、发布、使用和更新
  17. 嵌入式主板开发设计需要考虑哪些因素?
  18. R 语言 4.2.2安装 WGCNA
  19. 嵌入式系统开发笔记79:电磁波频谱的划分
  20. 旅行app(游记、攻略、私人定制) | 顺便游旅行H5移动端实例

热门文章

  1. (转)200亿美元比特币找不到主人,这个邪恶职业一夜爆火
  2. 一文了解 Serverless 2021 大事件
  3. 如何解决大规模高性能存储可靠性问题?
  4. oracle设置禁用外键,oracle禁用表外键
  5. 【故障分析】基于matlab轴承故障仿真信号时域波形图+幅度谱图【含Matlab源码 123期】
  6. 【图像融合】基于matlab CBF算法图像融合【含Matlab源码 083期】
  7. mysql计算两个月份之间的差值_MySQL如何获取两个年份月份信息中相差的月份(PERIOD_DIFF函数)呢?...
  8. mysql 回滚_【133期】面试官:你说熟悉MySQL事务,那来谈谈事务的实现原理吧!...
  9. 面试问到处理过什么棘手问题_为什么调节人工智能如此棘手?
  10. centos里安装mysql有34_centos7安装mysql5.6.34遇到中的问题