赞
踩
正所谓:人靠衣装,佛靠金装。学会插件开发,成就大神人生。
1-1本课程目标
1-2插件开发的起因
1-3插件的作用
2-1菜单项(MenuItem)
2-2组件菜单(AddComponentMenu)
2-3上下文菜单(ContextMenu)
2-4必要组件菜单(RequireComponent)
3-1属性相关标识(HideInInspector,SerializeField,NonSerialized,Serializable)
3-2自定义界面属性 (CustomEditor)
3-3自定序列化属性字段 (PropertyField)
4-1Windows窗口
4-2界面的生命周期(事件)
4-3编辑器下的UI控件
文本颜色字段,标签字段,数字输入字段,滑动条,位置大小输入字段,选择字段,标签、层、对象选择字段,单选字段,分组字段,其他字段
学会自定义编辑器,开发一款属于自己的编辑器插件
如果一个模型师想要在Unity中查看效果,但是她又是程序小白,作为公司一员应该给到她非常好帮助,比如制作一套工具集合(插件),让她根本离不开你。
帮助企业建立技术护城河,提高开发效率,减少沟通成本,减少工作。
Asset目录下新建一个Editor文件夹,新建MyEditor脚本
using UnityEngine; using UnityEditor; // 引用命名空间 public class MyEditor : Editor // 继承Editor类 { // 添加工具栏并设置创建方块方法 (false)显示出来 [MenuItem("工具/创建方块, &_f",false)] // % Ctrl & Alt # Shift public static void CreatCube() { GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.name = "方块"; } // 用来处理编辑器的问题(Update) private void OnSceneGUI() { if (Input.GetKeyDown(KeyCode.F) && Input.GetKeyDown(KeyCode.LeftAlt)) { CreatCube(); } }
效果图:
注意
OnSceneGUI方法中主要使用Handles系列来绘制可视化调试控件,比如线、盒子、球体、文本等(可以自行操作)
private void OnSceneGUI()
{
Handles.color = Color.green;
}
using UnityEngine; using UnityEditor; // 引用命名空间 public class MyEditor : Editor // 继承Editor类 { public static GameObject cube; // 添加工具栏并设置创建方块方法 (false)显示出来 [MenuItem("工具/创建方块, &_f",false)] // # Ctrl & Alt public static void CreatCube() { cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.name = "方块"; } // 添加工具栏并设置创建方块方法 (false)显示出来 [MenuItem("工具/销毁方块, &_G", false)] // # 表示Ctrl & 表示Alt public static void DestroyCube() { if (cube == null) { cube = GameObject.Find("方块"); } Editor.Destroy(cube); } // 用来处理编辑器的问题(Scene绘制图形方法类似Update) private void OnSceneGUI() { Handles.color = Color.green; if (Input.GetKeyDown(KeyCode.F) && Input.GetKeyDown(KeyCode.LeftAlt)) { CreatCube(); } if (Input.GetKeyDown(KeyCode.G) && Input.GetKeyDown(KeyCode.LeftAlt)) { DestroyCube(); } } }
using UnityEngine;
//给Component菜单下增加One子菜单,并将TestOne类添加到对象上
[AddComponentMenu("One",1)]
public class TestOne : MonoBehaviour
{
}
在面板中右键出现的菜单
public class TestOne : MonoBehaviour
{
[ContextMenu("测试")]
public void Test()
{
Debug.Log("123");
}
}
using UnityEngine;
[RequireComponent(typeof(Rigidbody))] // 添加组件必须要加上刚体
//给Component菜单下增加One子菜单,并将TestOne类添加到对象上
[AddComponentMenu("One",1)]
public class TestOne : MonoBehaviour
{
}
- 隐藏属性显示 (HideInInspector)
- 不显示的属性(SerializeField)
- 关键字序列化(Serializable) (针对类,结构体,枚举,委托有效)
- 不会被序列(NonSerialized)
using System; using UnityEngine; [Serializable] // 可以使类显示在视图面板 public enum MyColor { red, green, blue } public class TestOne : MonoBehaviour { [HideInInspector] public int age; // 将显示的属性隐藏 [SerializeField] private float height; // 显示隐藏的属性 [NonSerialized] public Vector3 v = new Vector3(10, 0, 0); // 防止变量序列化 public MyColor c; }
我们添加系统组件的时候,发现面板很好看,而脚本做不到这种效果。这里就需要自定义显示面板,如下图所示:
using UnityEngine; using UnityEditor;// 引用命名空间 // CustomEditor是对应标识的组件脚本 [CustomEditor(typeof(BoxCollider))] // 设置自定义BoxCollider的内容 public class MyInsPectorTest : Editor // 继承Editor类 { private bool b; private string s = ""; private string m; // 重写Editor 绘制面板图形界面方法 public override void OnInspectorGUI() { // 当我们使用自动布局技术的时候,就不要混合使用GUI.Lable,以免产生不必要的约束问题 GUILayout.Label("雷潮"); b = GUILayout.Toggle(b,"关闭音效"); GUILayout.Label("请输入账号"); s = GUILayout.TextField(s); GUILayout.Label("请输入密码"); m = GUILayout.PasswordField(m,'*'); // 后面是占位符,将密码字符更替为* if (GUILayout.Button("登录")) { if (s == "leichao" && m == "123456") { Debug.Log("登录成功"); } else { Debug.Log("账号或者密码错误"); } } } }
命名空间在UnityEditor
SerializedObject 序列化对象 请点击查看序列化对象类
SerializedProperty 序列化属性 请点击查看序列化属性类
using System; using UnityEngine; [Serializable] // 可以使类显示在视图面板 public enum MyColor { red, green, blue } public class TestOne : MonoBehaviour { public int age; public float height; public Vector3 v = new Vector3(10, 0, 0); public MyColor c; }
using UnityEditor; [CustomEditor(typeof(TestOne))] public class Test1 : Editor { public SerializedObject mObj; public SerializedProperty mAge; public SerializedProperty mHeight; public SerializedProperty mVector; public SerializedProperty mColor; private void OnEnable() { mObj = new SerializedObject(target); // 创建一个目标,即挂载脚本物体 mAge = this.mObj.FindProperty("age"); mHeight = this.mObj.FindProperty("height"); mVector = this.mObj.FindProperty("v"); mColor = this.mObj.FindProperty("c"); } public override void OnInspectorGUI() { this.mObj.Update(); EditorGUILayout.PropertyField(this.mAge); EditorGUILayout.PropertyField(this.mHeight); EditorGUILayout.PropertyField(this.mVector); EditorGUILayout.PropertyField(this.mColor, true); } }
using UnityEngine; using UnityEditor; public class MyWindow :EditorWindow { private bool b; private string s; private string m; private Color c = Color.white; private string t; [MenuItem("Tools/Window")] public static void ShowW() { EditorWindow.GetWindow<MyWindow>().Show(); } private void OnGUI() { // 编辑MyWindow EditorGUILayout.LabelField("雷潮邀请你输入"); EditorGUILayout.Space(); // 隔开一行 EditorGUILayout.LabelField("请输入账号"); s = EditorGUILayout.TextField(s); EditorGUILayout.SelectableLabel("请输入密码"); s = EditorGUILayout.PasswordField(s); EditorGUILayout.Space(); // 隔开一行 EditorGUILayout.LabelField("请选择颜色"); this.c = EditorGUILayout.ColorField(c); EditorGUILayout.Space(); // 隔开一行 EditorGUILayout.LabelField("请输入你要提交的信息"); this.t = EditorGUILayout.TextArea(this.t); // 文本区域可以空格换行输入 b = GUILayout.Toggle(b, "记住账号"); b = GUILayout.Toggle(b, "记住密码"); } }
[MenuItem("Tools/Window")]
public static void ShowW()
{
// 无法关闭
MyWindow.CreateInstance<MyWindow>().ShowPopup();
}
public class WindowEditorLife : EditorWindow { [MenuItem("Window/MyWindow")] public static void CreatWindow() { EditorWindow.GetWindow<WIndowEditorLife>().Show(); } private void OnEnable() // 当窗口可用时 { Debug.Log("OnEnable ==== 1"); } private void OnFocus() // 当鼠标在窗口中,获得焦点时,调用一次 { Debug.Log("OnFocus ==== 2"); } private void Update() { Debug.Log("所有可见窗口上每帧调用"); } private void OnLostFocus() // 失去焦点时调用 { Debug.Log("OnLostFocus"); } private void OnGUI() // 绘制图形 { Debug.Log("绘制图形"); if (GUILayout.Button("关闭窗口")) { this.Close(); } } private void OnDisable() // 窗口不可用的时候调用 { Debug.Log("OnDisable"); } private void OnDestroy() // 销毁的时候调用 { Debug.Log("OnDestroy"); } private void OnHierarchyChange() // 当场景层次面板发生改变的时候调用 { Debug.Log("OnHierarchyChange"); } private void OnValidate() // 当脚本被加载或者检视面板值备修改的时候调用 { Debug.Log("OnValidate"); } private void OnProjectChange() // 当工程目录更新时调用(有资源变动) { Debug.Log("OnProjectChange"); } private void OnSelectionChange() // 当在窗口选中某个物体时 { Debug.Log("OnSelectionChange"); } private void OnInspectorUpdate() // 窗口的更新调用,每秒10次 { Debug.Log("OnInspectorUpdate"); }
EditorGUILayout.Slider(…) 浮点数滚动条
EditorGUILayout.IntSlider(…)整数滚动条
EditorGUILayout.MinMaxSlider(…) 最小最大滚动条
number = EditorGUILayout.Slider(number, 0, 100);
number1 = EditorGUILayout.IntSlider(number1, 0, 100);
EditorGUILayout.MinMaxSlider(ref number2, ref number3, 0, 100);
EditorGUILayout.TextField(string) 文本输入框
EditorGUILayout.TextArea(string); 可换行的文本输入框
EditorGUILayout.PasswordField(string); 文本输入框
EditorGUILayout.ColorField(Color); 颜色输入框
EditorGUILayout.IntField(int); 整数输入
EditorGUILayout.FloatField(float); 浮点数输入
EditorGUILayout.CurveField(string,objec) // 动画曲线输入
EditorGUILayout.ObjectField(objec,typeof()) // 获取选择的物体,后面可以限制选择的物体类型
EditorGUILayout.InspectorTitlebar(bool,object) // 选择的物体放在面板
s1 = EditorGUILayout.TextField(s1); // 文本输入
s1 = EditorGUILayout.TextArea(s1); // 换行输入
s1 = EditorGUILayout.PasswordField(s1); // 密码文本输入
c1 = EditorGUILayout.ColorField(c1);
a1 = EditorGUILayout.CurveField("动画",a1);
// 选择选中的物体,类型就是后面的参数typeof(Transform)
o1 = EditorGUILayout.ObjectField(o1, typeof(Transform));
if (o1 != null)
{
b = EditorGUILayout.InspectorTitlebar(b, o1); // 选择的物体放在面板
Debug.Log(b);
}
EditorGUILayout.RectField(Rect); 矩阵
EditorGUILayout.BoundsField(Bounds);XYZ距离
EditorGUILayout.Vector2Field(string, Vector2) 二维坐标
EditorGUILayout.Vector3Field(string,Vector3); 三维坐标
EditorGuILayout.Vector4Field(string,Vector4); 三维坐标(表示旋转)(增加了W)
(
r = EditorGUILayout.RectField(r); // X,Y,width,height
b = EditorGUILayout.BoundsField(b); // 中心,大小
v2 = EditorGUILayout.Vector2Field("平面位置", v2);
v3 = EditorGUILayout.Vector3Field("3D位置", v3);
v4 = EditorGUILayout.Vector4Field("四元数", v4);
EditorGUILayout.Popup(int, string[]); 字符串选择,返回字符串数组下标
EditorGUILayout.IntPopup(int,int[],string[]) 字符串选择,返回字符串对应下标的整形数组值
EditorGUILayout.EnumPopup(Enum) 枚举选择,返回枚举
Enum e = People.man;
enum People
{
man,
woman
}
// 下拉菜单
index = EditorGUILayout.Popup(index, strs);
index = EditorGUILayout.IntPopup(index, strs, numbers);
e = EditorGUILayout.EnumPopup(e);
Debug.Log(e);
EditorGUILayout.TagField(this.t);
EditorGUILayout.LayerField(this.layer);
EditorGUILayout.ObjectField(this.mObj, typeof(BoxCollider));
EditorGUILayout.Foldout(bool,string)
s1 = "Player";
s1 = EditorGUILayout.TagField(s1);
// Layer
number1 = EditorGUILayout.LayerField(number1);
b1 = EditorGUILayout.Foldout(b1, "点击打开"); // 展开控件
if (b1)
{
number = EditorGUILayout.Slider(number, 0, 100);
o1 = EditorGUILayout.ObjectField(o1 as UnityEngine.Object, typeof(GameObject)); // 选中的游戏物体菜单
}
else
{
Debug.Log("没有任何物体");
}
- 水平分组
EditorGUILayout.BeginHorizontal();
EditorGUILayout.EndHorizontal();- 垂直分组
EditorGUILayout.BeginVertical();
EditorGUILayout.EndVertical();- Toggle分组
EditorGUILayout.BeginToggleGroup(“选择分组”,this.mbool);
EditorGUILayout.EndToggleGroup();- 滚动分组
EditorGUILayout.BeginScrollView(this.mPos);
EditorGUILayout.EndScrollView();
b1 = EditorGUILayout.BeginToggleGroup("灵敏度设置", b1);
b2 = EditorGUILayout.Toggle("鼠标设置",b2);
b3 = EditorGUILayout.Toggle("鼠标设置", b3);
b4 = EditorGUILayout.Toggle("鼠标设置", b4);
EditorGUILayout.EndToggleGroup();
####5总结:
顶部编辑器菜单:引用using UnityEditor; 使用[MenuItem()];
Inspector面板的处理:继承Editor,重写虚方法:OnInspectorGUI(),并调用(父类的此方法)base.OnInspectorGUI()
窗口的面板:继承:EditorWindow, 使用EditorWindow.GetWindow(); 调用窗口的Show()方法。
腾讯课堂
网易云课堂
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。