Unity编辑器扩展教程(二)
本文提供全流程,中文翻译。
Chinar坚持将简单的生活方式,带给世人!
(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例)
- 一
- Brief Introduction —— 简介
- 二
- ScriptableWizard —— 脚本化向导
- 1- - DisplayWizard —— 显示器向导
- 2- - ScriptableWizard Messages Sent —— 脚本化向导的信息传递
- 3- - DisplayProgressBar —— 进度条
- ScriptableWizard —— 脚本化向导
- 三
- EditorWindow —— 编辑器窗口
此教程是在教程(一)基础上进行扩展:如果不知道如何创建编辑器按钮,推荐先看教程(一)
一
Brief Introduction —— 简介
我们在做工程的时候,需要对数据进行操作。
为节省时间,会使用一些快捷键,菜单栏上的功能、或是右键菜单
这些便捷的功能,都是Unity官方为了方便我们对所需数据进行操作。
对Unity编辑进行了一些封装处理,简化数据操作流程,封装为一个按钮/一个窗口/窗口功能。
这些诸如此类的功能就是编辑器的扩展,和封装
功能键、Inspector面板、Game视窗等等都是编辑器的功能
注意:编辑器类脚本,必须放在 Assets/Editor 资源目录中
此文件夹下的脚本只对编辑器进行操作。最后资源打包,Editor文件夹下的所有资源都不会被打包到工程中
如果没有此文件夹,需自行创建:在Project视窗下,右键Create - - Folder



二
ScriptableWizard —— 脚本化向导
ScriptableWizard 是一个编辑器类,继承自 EditorWindow
从这个类派生来创建一个编辑器向导
1
- - DisplayWizard —— 显示器向导
创建一个显示器向导,调用静态方法
DisplayWizard <类型>(“标题”, “第一个按钮”, “第二个按钮”);
即可创建一个 显示器向导

1using UnityEditor; //引用Unity编辑器命名空间 2using UnityEngine; //引用Unity引擎命名空间 3 4 5/// <summary> 6/// 改变所有敌人脚本 7/// </summary> 8public class ChangeAll : ScriptableWizard 9{ 10 public int AddHp = 10; //每次增加血量值 11 public int Num = 0;//一个数字为了测试 isValue 12 13 /// <summary> 14 /// 菜单栏 我的工具 中,创建一个按钮 15 /// </summary> 16 [MenuItem("我的工具/修改所有敌人属性")] 17 static void 修改所有敌人属性() 18 { 19 //显示器向导 <所管控的类>( 窗口的名字,窗口中按钮的名字,第二个按钮的名字 ); 20 DisplayWizard<ChangeAll>("修改所有敌人血量", "确认修改血量", "第二个按钮"); 21 } 22}

2
- - ScriptableWizard Messages Sent —— 脚本化向导的信息传递
其中内置了一些固定函数,用来传递信息
OnEnable() —————————————————– 脚本有效时就会执行
OnWizardCreate() ————————————— 在 创建 / 第一个 按钮上点击时调用
OnWizardOtherButton() ————————— 在 其他 按钮上点击时调用(允许一个动作)
OnWizardUpdate() ————————————– 在向导被打开 / 向导中数据发生变化时被调用
OnSelectionChange() ——————————— 当选择发生改变,调用此函数
(此函数在EditorWindow中,而ScriptableWizard继承自EditorWindow,所以可以在其中直接调用)

1using UnityEditor; //引用Unity编辑器命名空间 2using UnityEngine; //引用Unity引擎命名空间 3 4 5/// <summary> 6/// 改变所有敌人脚本 7/// </summary> 8public class ChangeAll : ScriptableWizard 9{ 10 public int AddHp = 10; //每次增加血量值 11 public int Num = 0; //一个数字为了测试 isValue 12 13 14 /// <summary> 15 /// 菜单栏 我的工具 中,创建一个按钮 16 /// </summary> 17 [MenuItem("我的工具/修改所有敌人属性")] 18 static void 修改所有敌人属性() 19 { 20 //显示器向导 <所管控的类>( 窗口的名字,窗口中按钮的名字,第二个按钮的名字 ); 21 DisplayWizard<ChangeAll>("修改所有敌人血量", "确认修改血量", "第二个按钮"); 22 } 23 24 25 /// <summary> 26 /// 固定函数名:窗口开始时执行 27 /// </summary> 28 void OnEnable() 29 { 30 AddHp = EditorPrefs.GetInt("ChangeAll_health", AddHp); //取值,默认为初始值 并赋值给AddHp 31 } 32 33 34 /// <summary> 35 /// 固定函数名:对应现实向导中的第一个按钮 —— 确认修改血量 36 /// </summary> 37 void OnWizardCreate() 38 { 39 //进度条 40 EditorUtility.DisplayProgressBar("修改进度", "0/" + Selection.gameObjects.Length + "完成修改", 1); 41 int count = 0; 42 43 foreach (var gameObject in Selection.gameObjects) //遍历选中的物体 44 { 45 CompleteProject.EnemyHealth health = gameObject.GetComponent<CompleteProject.EnemyHealth>(); //获取其身上脚本组件 46 Undo.RecordObject(health, "Change health"); //记录变量 health 之后做的更改 47 health.startingHealth += AddHp; //自增 设置的值 //需要修改其他属性,自己往下写 48 49 count++; 50 //进度条 51 EditorUtility.DisplayProgressBar("修改进度", count + "/" + Selection.gameObjects.Length + "完成修改", progress: count / Selection.gameObjects.Length); 52 } 53 EditorUtility.ClearProgressBar();//清除进度条 54 } 55 56 57 /// <summary> 58 /// 固定函数名:对应现实向导中的第二按钮 —— 第二个按钮 59 /// </summary> 60 void OnWizardOtherButton() 61 { 62 //弹出通知(新建一个 GUI内容(被选中物体的个数)+“字符” ) 63 ShowNotification(new GUIContent(Selection.gameObjects.Length + "个元素被选中")); 64 } 65 66 67 /// <summary> 68 /// 当属性值被修改时,每帧调用。 / 当界面开启时,会调用一次 69 /// </summary> 70 void OnWizardUpdate() 71 { 72 helpString = null; //每次调用就归零一次,否则会出现字体不消除的情况 73 errorString = null; 74 75 if (Selection.gameObjects.Length > 0) 76 { 77 helpString = "当前选择了" + Selection.gameObjects.Length + "个敌人"; //及时更新选择的数量 78 } 79 else 80 { 81 errorString = "最少选择一个啊"; //错误提示 82 } 83 84 EditorPrefs.SetInt("ChangeAll_health", AddHp); //修改后存入一个值,就是记录一下。 85 } 86 87 88 /// <summary> 89 /// 当选择的物体发生改变,调用此函数 90 /// </summary> 91 void OnSelectionChange() 92 { 93 OnWizardUpdate(); 94 } 95}

3
- - DisplayProgressBar —— 进度条
在显示器向导中创建一个进度条
EditorUtility.DisplayProgressBar( 进度条标题,进度信息,进度比例 )
EditorUtility.ClearProgressBar() 调用此函数,进度条才会被删除

1using UnityEditor; //引用Unity编辑器命名空间 2using UnityEngine; //引用Unity引擎命名空间 3 4 5/// <summary> 6/// 改变所有敌人脚本 7/// </summary> 8public class ChangeAll : ScriptableWizard 9{ 10 public int AddHp = 10; //每次增加血量值 11 public int Num = 0; //一个数字为了测试 isValue 12 13 14 /// <summary> 15 /// 菜单栏 我的工具 中,创建一个按钮 16 /// </summary> 17 [MenuItem("我的工具/修改所有敌人属性")] 18 static void 修改所有敌人属性() 19 { 20 //显示器向导 <所管控的类>( 窗口的名字,窗口中按钮的名字,第二个按钮的名字 ); 21 DisplayWizard<ChangeAll>("修改所有敌人血量", "确认修改血量", "第二个按钮"); 22 } 23 24 25 /// <summary> 26 /// 固定函数名:对应现实向导中的第二按钮 —— 第二个按钮 27 /// </summary> 28 void OnWizardOtherButton() 29 { 30 EditorUtility.DisplayProgressBar("修改进度", "0/" + Selection.gameObjects.Length + "完成修改", 1); //进度条 31 int count = 0; //计数 32 foreach (var gameObject in Selection.gameObjects) //遍历选中的物体 33 { 34 CompleteProject.EnemyHealth health = gameObject.GetComponent<CompleteProject.EnemyHealth>(); //获取其身上脚本组件 35 Undo.RecordObject(health, "Change health"); //记录变量 health 之后做的更改 36 health.startingHealth += AddHp; //自增 设置的值 //需要修改其他属性,自己往下写 37 count++; //计数自增1 38 EditorUtility.DisplayProgressBar("修改进度", count + "/" + Selection.gameObjects.Length + "完成修改", count / Selection.gameObjects.Length); //进度条 39 } 40 41 //EditorUtility.ClearProgressBar(); //清除进度条(只有调用此方法,进度条才会删除) 42 } 43}

三
EditorWindow —— 编辑器窗口
创建一个编辑器窗口,在我们的Unity编辑器中
GetWindow(bool, 窗口名字)
bool 值为: false 窗口可以与其他窗口合并
bool 值为: true 窗口独立,不可合并
以下是我写的一个简单的梨子,用于批量修改物体的名称(选择物体内数组,并没有进行内部处理)

1using UnityEditor; //引用Unity编辑器命名空间 2using UnityEngine; //引用Unity引擎命名空间 3 4 5/// <summary> 6/// 创建一个窗口类 7/// </summary> 8public class ChinarWindow : EditorWindow//继承自 EditorWindow 9{ 10 string CustomName = "CustomName"; //自定义名字 11 private int ChinarNum = 0; //空物体数量 12 13 14 /// <summary> 15 /// 创建一个菜单项 16 /// </summary> 17 [MenuItem("我的工具/显示新窗口")] 18 static void 显示新窗口() 19 { 20 ChinarWindow chinar = GetWindow<ChinarWindow>(false, "Chinar窗口"); //获取到一个窗口,赋值给当前编辑器类的对象 21 chinar.Show(); //显示对象的窗口 22 } 23 24 25 /// <summary> 26 /// 此函数中实现编辑器界面的定义/绘制 27 /// </summary> 28 void OnGUI() 29 { 30 GUILayout.Label("这是Chianr窗口"); //标题 31 CustomName = GUILayout.TextField(CustomName); //文本框 32 ChinarNum = int.Parse(GUILayout.TextField(ChinarNum.ToString())); //数字框 33 34 //可以直接判断按钮的点击 35 if (GUILayout.Button("修改所有物体名字")) 36 { 37 for (int i = 0; i < Selection.transforms.Length; i++) //遍历选中的元素 38 { 39 Undo.RecordObjects(Selection.gameObjects as GameObject[], "ChinarWindow_GameObject[]"); //Selection.gameObjects 返回一个GameObject[] ,并记录键(用于回退) 40 Selection.transforms[i].SetSiblingIndex(i); //为选择的物体设置下标 41 Selection.transforms[i].name = CustomName + i; //设置名字+i 42 } 43 } 44 45 //判断按钮的点击 46 if (GUILayout.Button("创建多个空物体")) 47 { 48 if (ChinarNum <= 0) //如果数字 小于等于 0 49 { 50 ShowNotification(new GUIContent("请写入创建空物体数量")); //提示 输入 51 } 52 else //不为0 53 { 54 for (int i = 0; i < ChinarNum; i++) //遍历个数 55 { 56 Undo.RegisterCreatedObjectUndo(new GameObject(CustomName), "Chinar Create gameobject"); //创建一个名为 CustomName 的空物体 57 } 58 } 59 } 60 } 61}

END
本博客为非营利性个人原创,除部分有明确署名的作品外,所刊登的所有作品的著作权均为本人所拥有,本人保留所有法定权利。违者必究
对于需要复制、转载、链接和传播博客文章或内容的,请及时和本博主进行联系,留言,Email: ichinar@icloud.com
对于经本博主明确授权和许可使用文章及内容的,使用时请注明文章或内容出处并注明网址