Unity 扩展编辑器

扩展编辑器: 可以在编辑器的任何地方加上自己自定义的按钮

一、扩展编辑器概述

写的类引入 UnityEditor 命名空间  

写的方法必须是static的  这样编辑器就可以通过类名调用 而不需要创建实例

在方法的顶部标记为[MenuItem()]

MenuItem() 是 UnityEditor 的 静态方法  有许多重载函数 可以看下面的源码  对应参数的解释也已经很详细了 

// Decompiled with JetBrains decompiler
// Type: UnityEditor.MenuItem
// Assembly: UnityEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 9C976F45-3B3D-47EA-8C1B-74624247F64D
// Assembly location: D:\Unity\2018.4.32f1\Editor\Data\Managed\UnityEditor.dll

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Scripting;

namespace UnityEditor
{
  /// <summary>
  ///   <para>The MenuItem attribute allows you to add menu items to the main menu and inspector context menus.</para>
  /// </summary>
  [RequiredByNativeCode]
  [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
  public sealed class MenuItem : Attribute
  {
    private static readonly string[] kMenuItemSeparators = new string[1]
    {
      "/"
    };
    public string menuItem;
    public bool validate;
    public int priority;

    /// <summary>
    ///   <para>Creates a menu item and invokes the static function following it, when the menu item is selected.</para>
    /// </summary>
    /// <param name="itemName">The itemName is the menu item represented like a pathname.
    /// For example the menu item could be "GameObject/Do Something".</param>
    /// <param name="isValidateFunction">If isValidateFunction is true, this is a validation
    /// function and will be called before invoking the menu function with the same itemName.</param>
    /// <param name="priority">The order by which the menu items are displayed.</param>
    public MenuItem(string itemName)
      : this(itemName, false)
    {
    }

    /// <summary>
    ///   <para>Creates a menu item and invokes the static function following it, when the menu item is selected.</para>
    /// </summary>
    /// <param name="itemName">The itemName is the menu item represented like a pathname.
    /// For example the menu item could be "GameObject/Do Something".</param>
    /// <param name="isValidateFunction">If isValidateFunction is true, this is a validation
    /// function and will be called before invoking the menu function with the same itemName.</param>
    /// <param name="priority">The order by which the menu items are displayed.</param>
    public MenuItem(string itemName, bool isValidateFunction)
      : this(itemName, isValidateFunction, !itemName.StartsWith("GameObject/Create Other") ? 1000 : 10)
    {
    }

    /// <summary>
    ///   <para>Creates a menu item and invokes the static function following it, when the menu item is selected.</para>
    /// </summary>
    /// <param name="itemName">The itemName is the menu item represented like a pathname.
    /// For example the menu item could be "GameObject/Do Something".</param>
    /// <param name="isValidateFunction">If isValidateFunction is true, this is a validation
    /// function and will be called before invoking the menu function with the same itemName.</param>
    /// <param name="priority">The order by which the menu items are displayed.</param>
    public MenuItem(string itemName, bool isValidateFunction, int priority)
      : this(itemName, isValidateFunction, priority, false)
    {
    }

    internal MenuItem(string itemName, bool isValidateFunction, int priority, bool internalMenu)
    {
      itemName = MenuItem.NormalizeMenuItemName(itemName);
      this.menuItem = !internalMenu ? itemName : "internal:" + itemName;
      this.validate = isValidateFunction;
      this.priority = priority;
    }

    private static string NormalizeMenuItemName(string rawName)
    {
      return string.Join(MenuItem.kMenuItemSeparators[0], ((IEnumerable<string>) rawName.Split(MenuItem.kMenuItemSeparators, StringSplitOptions.None)).Select<string, string>((Func<string, string>) (token => token.Trim())).ToArray<string>());
    }
  }
}

二、扩展编辑器分类

想要对路径进行分类  如图  多一个分割线

 

调用MenuItem() 的时候  相邻的两个函数 设置 priority的参数 相差11即可

如上图 假设 Camera 为 10   那么 CenterOnChildren 设置为大于21 就会在它们俩中间多一条分割线

三、在Hierarchy下 鼠标右键添加按钮

想要在Hierarchy下 鼠标右键添加按钮  可以将路径设置在GameObject下  priority设置在25以内 (0-24) 如图

 

 

 

 

using UnityEngine;
using UnityEditor;

public class Tools 
{
    [MenuItem("Tools/Test1", false, 0)]
    static void Test1()
    {

    }

    [MenuItem("Tools/Test2", false, 1)]
    static void Test2()
    {

    }

    [MenuItem("Tools/Test3", false, 2)]
    static void Test3()
    {

    }

    [MenuItem("GameObject/MyCustom", false, 24)] // 注意这里 如果是25 就会在新的分类
    static void MyCustom()
    {

    }


}

四、在Project下 鼠标右键添加按钮

想要在Project下 鼠标右键添加按钮  可以将路径设置在Assets下 如图

 

 

 

 

using UnityEngine;
using UnityEditor;

public class Tools 
{

    [MenuItem("Assets/MyCustom", false, 24)] 
    static void MyCustom()
    {

    }


}

 五、在Inspector的组件上添加右键按钮

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class PlayerEditor  {
    [MenuItem("CONTEXT/PlayerHealth/InitHealthAndSpeed")]// CONTEXT 组件名 按钮名
    static void InitHealthAndSpeed( MenuCommand cmd )//menucommand是当前正在操作的组件
    {
        //Debug.Log(cmd.context.GetType().FullName);
        CompleteProject.PlayerHealth health = cmd.context as CompleteProject.PlayerHealth;
        health.startingHealth = 200;
        health.flashSpeed = 10;
        Debug.Log("Init");
    }
    [MenuItem("CONTEXT/Rigidbody/Clear")]
    static void ClearMassAndGravity( MenuCommand cmd )
    {
        Rigidbody rgd = cmd.context as Rigidbody;
        rgd.mass = 0;
        rgd.useGravity = false;
    }
}

六、给扩展属性增加可用性校验

using UnityEngine;
using UnityEditor;

public class Tools 
{

    
    // 第二个参数设置为 true 表示下面这个方法是一个验证方法
    [MenuItem("GameObject/MyDelete", true, 11)]

    // 注意 : 这个验证的函数名可以随意 但是 MenuItem方法下的路径 一定要跟 按键是同一个"GameObject/MyDelete"
    // 这里的返回值是一个bool型  如果return true 则按钮可以用 否则按钮是置灰
    static bool MyDeleteValidate()
    {
        if (Selection.objects.Length > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    [MenuItem("GameObject/MyDelete", false, 11)]
    static void MyDelete()
    {
        foreach (Object o in Selection.objects)
        {
            // 使用Undo 是可以撤销的
            Undo.DestroyObjectImmediate(o);
        }
    }
}

七、给扩展的组件按钮增加快捷键

 

    //%=ctrl #=shift &=alt
    [MenuItem("Tools/test2 %q",false,100)]
    static void Test2()
    {
        Debug.Log("Test2");
    }
    [MenuItem("Tools/test3 %t",false,0)]
    static void Test3()
    {
        Debug.Log("Test3");
    }

八、撤销   Undo.DestroyObjectImmediate(o);//利用Undo进行的删除操作 是可以撤销的

九、在组件的public 属性上增加扩展

 

 

十、创建对话框

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class EnemyChange : ScriptableWizard {

    [MenuItem("Tools/CreateWizard")]
    static void CreateWizard()
    {
        ScriptableWizard.DisplayWizard<EnemyChange>("统一修改敌人","Change And Close","Change");
    }

    public int changeStartHealthValue = 10;
    public int changeSinkSpeedValue = 1;

     const string changeStartHealthValueKey = "EnemyChange.changeStartHealthValue";
     const string changeSinkSpeedValueKey = "EnemyChange.changeSinkSpeedValue";
    //当窗口被创建出来的时候调用的
    void OnEnable()
     {
         changeStartHealthValue = EditorPrefs.GetInt(changeStartHealthValueKey, changeStartHealthValue);
         changeSinkSpeedValue = EditorPrefs.GetInt(changeSinkSpeedValueKey, changeSinkSpeedValue);
    }
    //检测create按钮的点击
    void OnWizardCreate()
    {
        GameObject[] enemyPrefabs = Selection.gameObjects;
        EditorUtility.DisplayProgressBar("进度", "0/" + enemyPrefabs.Length + " 完成修改值", 0);
        int count = 0;
        foreach (GameObject go in enemyPrefabs)
        {
            CompleteProject.EnemyHealth hp = go.GetComponent<CompleteProject.EnemyHealth>();
            Undo.RecordObject(hp, "change health and speed");
            hp.startingHealth += changeStartHealthValue;
            hp.sinkSpeed += changeSinkSpeedValue;
            count++;
            EditorUtility.DisplayProgressBar("进度", count+"/" + enemyPrefabs.Length + " 完成修改值", (float)count/enemyPrefabs.Length);
        }
        EditorUtility.ClearProgressBar();
        ShowNotification(new GUIContent(Selection.gameObjects.Length + "个游戏物体的值被修改了"));
    }
    void OnWizardOtherButton()
    {
        OnWizardCreate();
    }
    //当前字段值修改的时候会被调用
    void OnWizardUpdate()
    {
        errorString = null;
        helpString = null;
        if (Selection.gameObjects.Length > 0)
        {
            helpString = "您当前选择了" + Selection.gameObjects.Length + "个敌人";
        }
        else
        {
            errorString = "请选择至少一个敌人";
        }

        EditorPrefs.SetInt(changeStartHealthValueKey, changeStartHealthValue);
        EditorPrefs.SetInt(changeSinkSpeedValueKey, changeSinkSpeedValue);
    }
    void OnSelectionChange()
    {
        OnWizardUpdate();
    }

}

十一、创建自定义窗口

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class MyWindow : EditorWindow {

    [MenuItem("Window/show mywindow")]
    static void ShowMyWindow()
    {
        MyWindow window= EditorWindow.GetWindow<MyWindow>();
        window.Show();
    }

    private string name="";
    void OnGUI()
    {
        GUILayout.Label("这是我的窗口");
        name = GUILayout.TextField(name);
        if (GUILayout.Button("创建"))
        {
            GameObject go = new GameObject(name);
            Undo.RegisterCreatedObjectUndo(go, "create gameobject");
        }
    }

}

 

posted @ 2021-04-05 06:54  一个新星的诞生  阅读(20)  评论(0)    收藏  举报