Unity编辑器扩展是对现有的Unity进行自定义功能扩展的一种方法,可以简化一些重复的开发动作,也可以用于制作插件
unity的编辑器扩展绝大部分是用于unity的编辑器模式下,我们扩展的方法都是为了提高开发效率,但是这些方法在游戏运行时并没有作用。不需要将他们打包进工程,所以我们需要把这些不需要打包的资源和脚本放入名字是Editor的文件夹下,这个文件夹可以是跟目录也可以是子目录只要名字不写错就可以。
这里先介绍MenuItem特性(以下仅是我知道的)
在Unity菜单栏上添加自定义方法按钮
第一步、在Editor文件夹下新建一个脚本,写上下面的代码
using UnityEngine;
using UnityEditor;
//因为这个脚本不需要作为组件存在所以不需要继承自MonoBehaviour
public class NewButton
{
//MenuItem(string itemName,bool isValidateFunction,int priority);
[MenuItem("NewButton/Test1",false,1)]
static void Test1()
{
Debug.Log("Test1");
}
}
菜单栏上就会显示,我们点击Test1就会运行debug方法
再加上几行代码
[MenuItem("NewButton/Test2",false,2)]
static void Test2()
{
Debug.Log("Test2");
}
[MenuItem("NewButton/Test3", false, 13)]
static void Test3()
{
Debug.Log("Test3");
}
这里我们发现 int priority 参数越小,在菜单列表的位置越靠上,当两个相邻的int priority值的差 >=11时就会分组,这一规律也适用于系统自带的Edit GameObeject列表
假设我们现在需要一个批量删除的方法,这个方法只有在选中对象的情况下可以调用,没选中对象就无法调用
bool isValidateFunction 参数解释
[MenuItem("NewButton/MyDelete",false,14)]
static void MyDelete()
{
// 获取选中的所有对象
foreach (Object item in Selection.objects)
{
//这里不能使用GameObject.Destory,因为在编辑模式下不能使用
//可以使用GameObject.DestroyImmediate 但是建议使用下面方法
//Undo类里面的删除方法可以撤销(ctrl z)
Undo.DestroyObjectImmediate(item);
}
}
[MenuItem("NewButton/MyDelete", true, 14)]
static bool MyDeleteTest()
{
//判断是否选中对象
if(Selection.objects.Length>0)
{
return true; //有返回true 执行相同路径方法
}
else
{
return false; //没有选中返回false 不执行相同路径方法
}
}
没有选中对象
选中对象
给自定以方法添加快捷键
在路径名后面+空格+下划线+快捷键(%代表ctrl #代表shift &代表alt)
[MenuItem("NewButton/Test1 _%e",false,1)]
static void Test1()
{
Debug.Log("Test1");
}
给脚本组件添加右键扩展方法按钮
首先在场景中添加一个Cube,然后再Inspector面板上右键Box Colider组件,只有这几个选项,这时我们添加自定义方法按钮,来控制IsTrigger开关(听起来很傻,明明在Inspector就可以调整,但是这个方法可以用于所有组件包括我们自定义的脚本,并且可以修改脚本里的字段。其实最合适的用处我也没遇到,作为一个知识先学)
添加下面代码
注意,如果有一个自定义脚本挂在多个对象上,修改只作用于当前脚本挂在的对象
// CONTEXT(固定写)/组件名/按钮名
[MenuItem("CONTEXT/BoxCollider/ChangeTrigger")]
static void ChangeCollider(MenuCommand cmd)//MenuCommand是当前正在操作的组件对象(这个参数不用传递,系统会帮传递)
{
//拿到对应的组件
//这里使用(BoxCollider)转型如果失败会直接报错,使用as转型如果失败会返回Null,并且本行不会报错
BoxCollider collider = cmd.context as BoxCollider;
//isTrigger属性取反
collider.isTrigger = !collider.isTrigger;
}
效果
对字段添加右键扩展方法
现在我们给Cube添加一个CharacterPlayer的脚本,在这个脚本里我们定义了一个health字段,
然后我们想给这个字段在Inspector面板上添加一个右键加血量的方法
using UnityEngine;
public class CharacterPlayer : MonoBehaviour
{
//ContextMenuItem特性(“按钮名”,“方法名”)
[ContextMenuItem("AddHealth", "AddHp")]
public int playerHealth = 100;
private void AddHp()
{
playerHealth += 10;
}
}
现在我们自定义一个对话框,对我们选中的对象进行批量修改,首先把上面的cube做成一个预制体,然后把他复制100份,然后我们统一修改这些预制体,给他们的playerHealth字段+10,并且可以看到实时进度。
然后新创立一个脚本,NewWindow
介绍一下代码中用到的类
ScriptableWizard类 继承自EditorWindow
EditorWindow类
EditorUtility类
using UnityEditor;
using UnityEngine;
//创建对话框类需要继承自:ScriptableWizard
public class NewWindow:ScriptableWizard
{
//对话框内显示的字段
public int Hp = 10;
[MenuItem("NewButton/NewWindow",false,30)]
static void CreatWizard()
{
//创造对话框
//DisplayWizard是ScriptableWizard的一个静态方法 (对话框名称,默认按钮名称(不写默认名是Create),新按钮名称(不写就不显示这个按钮))
NewWindow window = ScriptableWizard.DisplayWizard("修改血量","ChangeHp","NewButton");
}
//监听默认按钮按下 此按钮按下 面板会关闭
private void OnWizardCreate()
{
//先拿到所有选中预制体
GameObject[] playerfabs = Selection.gameObjects;
//计数器
int count = 0;
//创建进度条窗口 (进度条名称,提示信息, 完成百分比进度条数值 0~1,(float类型))
EditorUtility.DisplayProgressBar("进度", count + "/" + playerfabs.Length + "完成修改", 0);
foreach (GameObject item in Selection.gameObjects)
{
//拿到组件
CharacterPlayer player = item.GetComponent();
//记录指定Object的操作,用于撤销操作(对象,操作名)
Undo.RecordObject(player, "change health");
//修改血量
player.playerHealth += Hp;
//操作时间长点让进度条可见(废操作)
while (Hp<10000000)
{
Hp += 1;
}
Hp= EditorPrefs.GetInt("NewWindow.Health", 10);
//计数器自增
count++;
EditorUtility.DisplayProgressBar("进度", count + "/" + playerfabs.Length + "完成修改", (float)count / playerfabs.Length);
}
//关闭进度条
EditorUtility.ClearProgressBar();
//提示信息(EditorWindow里的方法)
ShowNotification(new GUIContent(playerfabs.Length + "个对象值被修改"));
}
//监听新按钮按下 此按钮按下 面板不会关闭
private void OnWizardOtherButton()
{
OnWizardCreate();
}
//监听面板打开 和修改面板字段
private void OnWizardUpdate()
{
errorString = null;
helpString = null;
if (Selection.gameObjects.Length>0)
{
helpString = "当前选中了" + Selection.gameObjects.Length + "个对象";
}
else
{
errorString = "请至少选中一个对象";
}
//编辑器模式下保存数据和PlayerPrefs用法相同
//当作字段改变,保存改变数据
EditorPrefs.SetInt("NewWindow.Health", Hp);
}
//打开窗口时读取字段数据
private void OnEnable()
{
Hp = EditorPrefs.GetInt("NewWindow.Health", 10);
}
//因为OnWizardUpdate只会在面板打开 和修改面板字段时调用,所有提示信息不能及时显示
//OnSelectionChange是继承自EditorWindow,监听 当选中对象发生改变时调用,
//因为ScriptableWizard是继承自EditoeWindow 所以这里可以使用
private void OnSelectionChange()
{
//当选中对象发生改变后调用
OnWizardUpdate();
}
}
注意这类窗口可以同时打开多个
效果
分享让知识变得有意义!后续会继续更新一些学习中问题。
OJMars
InTheMars