Unity编辑器拓展参考代码:
Editor下的脚本资源都不会打包。
优先级相隔11就有有中线分割,每一个菜单栏的priority优先级默认为1000
具有一些参考价值的代码,如果读者需要详细信息,就到官方文档:
https://docs.unity3d.com/Manual/30_search.html?q=MenuItem
1.自定义窗口:
自定义一个跟Unity原生窗口一样的窗口:EditorWindow
file:///D:/Program64/Unity20190304f1/Unity/Editor/Data/Documentation/en/ScriptReference/EditorWindow.html
using UnityEditor;
using UnityEngine;
public class MyEditorWindow : EditorWindow
{
[MenuItem("Window/MyEditorWindow")]
static void ShowMyEditorWindow()
{
MyEditorWindow window = EditorWindow.GetWindow<MyEditorWindow>();
window.Show();
}
private string name = "";
private string myString = "My String";
private bool groupEnabled;
private bool myBool = true;
private float myFloat = 1.23f;
private void OnGUI()
{
GUILayout.Label("这是我的窗口");
GUILayout.Label("Base Settings", EditorStyles.boldLabel);
myString = EditorGUILayout.TextField("Text Field", myString);
groupEnabled = EditorGUILayout.BeginToggleGroup("Optional Settings", groupEnabled);
myBool = EditorGUILayout.Toggle("Toggle", myBool);
myFloat = EditorGUILayout.Slider("Slider", myFloat, -3, 3);
if (GUILayout.Button("创建"))
{
GameObject go = new GameObject(name);
Undo.RegisterCreatedObjectUndo(go, "create a gameObject");
}
EditorGUILayout.EndToggleGroup();
if (GUILayout.Button("博客地址"))
{
System.Diagnostics.Process.Start(blogURL);
}
}
}
2.MenuItem的各类用法:
using UnityEditor;
using UnityEngine;
namespace KervenTest
{
// MenuItem之间相差11就会加一条横线区分
// %=ctrl #=shift &=alt
public class MenuTest
{
[MenuItem("Assets/KervenTest #s", false, 1101)]
private static void KervenTestFunc()
{
Debug.Log("KervenTest Start: ");
}
[MenuItem("MyMenu/Log Selected Transform Nam")]
private static void LogSelectedTransformName()
{
Debug.Log("Selected Transform is on " + Selection.activeTransform.gameObject.name + ".");
}
// 如果此函数返回false,则菜单栏将被禁用
[MenuItem("MyMenu/Log Selected Transform Nam", true)]
private static bool ValidateLogSelectedTransformName()
{
return Selection.activeTransform != null;
}
[MenuItem("MyMenu/Do Something with a Shortcut key %g")]
private static void DoSomethingWithAShortcutKey()
{
Debug.Log("Doing something with a Shortcut Key ...");
}
// 给组件右键菜单栏添加按钮
// MenuCommand当前正在操作的组件
[MenuItem("CONTEXT/Rigidbody/Double Mass %r")]
private static void DoubleMass(MenuCommand command)
{
Rigidbody body = (Rigidbody)command.context;
body.mass *= 2;
Debug.Log("Doubled Rigidbody's Mass to " + body.mass + " from Context Menu");
}
// (Rigidbody)command.context
// menuCommand.context as GameObject
// 括号和as转类型的区别:()是强制、转不成功会报错,as更为温和、转不成功不会报错
[MenuItem("GameObject/MyCategory/Custom Game Object", false, 10)]
private static void CreateCustomGameObject(MenuCommand menuCommand)
{
GameObject go = new GameObject("Custom Game Object");
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
Undo.RegisterCompleteObjectUndo(go, "Create " + go.name);
Selection.activeObject = go;
}
[MenuItem("GameObject/my delete", false, 11)]
private static void MyDelete()
{
foreach (var obj in Selection.objects)
{
// GameObject.DestroyImmediate(obj);
// 利用Undo进行可撤销的删除操作
// 需要把删除操作注册到操作记录里面
Undo.DestroyObjectImmediate(obj);
}
}
}
}
3.生成一个对话框,与1有区别的对话框:ScriptableWizard
using UnityEngine;
using UnityEditor;
public class BatchChangeTestComponent : ScriptableWizard
{
[MenuItem("MyMenu/BatchChangeTestComponent")]
static void OpenBatchChangeWizard()
{
DisplayWizard<BatchChangeTestComponent>("批量修改选中的TestComponent", "ChangeAndClose", "Change");
}
public int startHealthAddValue = 0;
public int currentHealthAddValue = 0;
public Color lightColorChangeValue = Color.yellow;
private const string StartHealthAddValueKey = "BatchChangeTestComponent.startHealthAddValue";
private const string CurrentHealthAddValueKey = "BatchChangeTestComponent.currentHealthAddValue";
private const string LightColorChangeValueKeyR = "BatchChangeTestComponent.lightColorChangeValue_R";
private const string LightColorChangeValueKeyG = "BatchChangeTestComponent.lightColorChangeValue_G";
private const string LightColorChangeValueKeyB = "BatchChangeTestComponent.lightColorChangeValue_B";
private const string LightColorChangeValueKeyA = "BatchChangeTestComponent.lightColorChangeValue_A";
private void OnEnable()
{
startHealthAddValue = EditorPrefs.GetInt(StartHealthAddValueKey);
currentHealthAddValue = EditorPrefs.GetInt(CurrentHealthAddValueKey);
lightColorChangeValue = GetLightColorPrefs();
}
private void OnWizardCreate()
{
GameObject[] testCompsGo = Selection.gameObjects;
// 显示进度条
EditorUtility.DisplayProgressBar("修改进度", "已完成数量:" + "0/" + testCompsGo.Length, 0);
int count = 0;
foreach (var go in testCompsGo)
{
TestComponent tComp = go.GetComponent<TestComponent>();
Undo.RecordObject(tComp, "batch change tComps");
tComp.staringHealth += startHealthAddValue;
tComp.currentHealth += currentHealthAddValue;
tComp.lightColor = lightColorChangeValue;
count++;
EditorUtility.DisplayProgressBar("修改进度", "已完成数量:" + count + "/" + testCompsGo.Length, 0);
}
EditorUtility.ClearProgressBar();
// 使用ShowNotification显示提示信息
ShowNotification(new GUIContent(Selection.gameObjects.Length + "个游戏物体的值被修改了"));
}
private void OnWizardOtherButton()
{
OnWizardCreate();
}
// 当字段值修改时会被调用
private void OnWizardUpdate()
{
errorString = null;
helpString = null;
if (Selection.gameObjects.Length > 0)
helpString = "当前选择了" + Selection.gameObjects.Length + "个物体";
else
errorString = "请至少选择一个物体";
// 使用EditorPrefs保存数据
EditorPrefs.SetInt(StartHealthAddValueKey, startHealthAddValue);
EditorPrefs.SetInt(CurrentHealthAddValueKey, currentHealthAddValue);
SetLightColorPrefs(lightColorChangeValue);
}
private void SetLightColorPrefs(Color color)
{
EditorPrefs.SetFloat(LightColorChangeValueKeyR, color.r);
EditorPrefs.SetFloat(LightColorChangeValueKeyG, color.g);
EditorPrefs.SetFloat(LightColorChangeValueKeyB, color.b);
EditorPrefs.SetFloat(LightColorChangeValueKeyA, color.a);
}
private Color GetLightColorPrefs()
{
Color color = new Color();
color.r = EditorPrefs.GetFloat(LightColorChangeValueKeyR);
color.g = EditorPrefs.GetFloat(LightColorChangeValueKeyG);
color.b = EditorPrefs.GetFloat(LightColorChangeValueKeyB);
color.a = EditorPrefs.GetFloat(LightColorChangeValueKeyA);
return color;
}
private void OnSelectionChange()
{
OnWizardUpdate();
}
}
4.创建窗口,自动生成Service文件夹及下面的文件:
using UnityEngine;
using UnityEditor;
using System.IO;
public class CreateService : ScriptableWizard
{
[MenuItem("Assets/AutoCreateService #c", false, 1102)]
static void OpenCreateServiceWizard()
{
DisplayWizard<CreateService>("生成Service", "生成Service");
}
public string serviceName = "Test";
// "Create" 按钮点击执行的事件
private void OnWizardCreate()
{
CreateAService();
}
private void CreateAService()
{
string folderPath = Application.dataPath + "/Scripts/ToLua/Services/" + serviceName;
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
AssetDatabase.Refresh();
}
string filePathWithFileName = folderPath + "/" + serviceName + "Service.lua";
CreateAndWriteFile(filePathWithFileName, "KingOfStrategyService");
}
private void CreateAndWriteFile(string filePathWithFileName, string msg)
{
var fileWriter = File.CreateText(filePathWithFileName);
fileWriter.WriteLine(msg);
fileWriter.Close();
AssetDatabase.Refresh();
}
}
5.对应组件添加右键菜单项:ContextMenuItem
using UnityEngine;
public class TestComponent : MonoBehaviour
{
[ContextMenuItem("开始血量增加10", "AddStartHp_10")]
public int staringHealth = 100;
public int currentHealth;
[ContextMenuItem("重置颜色为yellow", "SetLightColor2Yellow")]
[ContextMenuItem("重置颜色为green", "SetLightColor2Green")]
public Color lightColor = Color.yellow;
void AddStartHp_10()
{
staringHealth += 10;
}
void SetLightColor2Yellow()
{
lightColor = Color.yellow;
}
[ContextMenu("SetColor2Green")]
void SetLightColor2Green()
{
lightColor = Color.green;
}
}
6.扩展在脚本上点击右键执行修改脚本属性的方法
[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");
}
7.学习使用selection获取选择的游戏物体,制作删除功能(可以撤销的)删除操作
[MenuItem("GameObject/my delete", false, 11)]
static void Mydelete()
{
foreach (Object o in Selection.objects)
{
//GameObject.DestroyImmediate(o);
Undo.DestroyObjectImmediate(o);//利用Undo进行的删除操作 是可以撤销的
}
//需要把删除操作注册到 操作记录里面
}
8.给菜单项添加快捷键
//%=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");
}
9.控制菜单项是否启用的功能
[MenuItem("GameObject/my delete", true, 11)]
static bool MyDeleteValidate()
{
if (Selection.objects.Length > 0)
return true;
else
return false;
}
[MenuItem("GameObject/my delete", false, 11)]
static void Mydelete()
{
foreach (Object o in Selection.objects)
{
//GameObject.DestroyImmediate(o);
Undo.DestroyObjectImmediate(o);//利用Undo进行的删除操作 是可以撤销的
}
//需要把删除操作注册到 操作记录里面
}
10.Demo
[MenuItem("Assets/PackageTools/SetCastShadowsOff", priority = 506)]
static void SetCastShadowsOff()
{
Debug.Log("SetCastShadowsOff");
var select = Selection.activeObject;
if (!select)
{
return;
}
var folderPath = AssetDatabase.GetAssetPath(select);
if (Directory.Exists(folderPath))
{
var fbxs = Directory.GetFiles(folderPath, "*.FBX", SearchOption.AllDirectories);
foreach (var item in fbxs)
{
Transform fbx = AssetDatabase.LoadAssetAtPath<Transform>(item);
///下面代码有点鬼畜,.On正常,.Off就只能在本级目录
var renderers = fbx.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var one in renderers)
{
string lowerChildName = one.transform.name.ToLower();
if (lowerChildName.Contains("head") || lowerChildName.Contains("face") || lowerChildName.Contains("lian"))
{
Debug.Log(fbx.name);
Debug.Log(lowerChildName);
one.shadowCastingMode = ShadowCastingMode.Off;
Debug.Log(one.shadowCastingMode);
}
}
AssetDatabase.SaveAssets();
}
var prefabs = Directory.GetFiles(folderPath, "*.prefab", SearchOption.AllDirectories);
foreach (var item in prefabs)
{
Transform prefab = AssetDatabase.LoadAssetAtPath<Transform>(item);
var renderers = prefab.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var one in renderers)
{
string lowerChildName = one.transform.name.ToLower();
if (lowerChildName.Contains("head") || lowerChildName.Contains("face") || lowerChildName.Contains("lian"))
{
one.shadowCastingMode = ShadowCastingMode.Off;
}
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
[MenuItem("Assets/PackageTools/FindIncorrectShader", priority = 507)]
static void FindIncorrectShader()
{
Debug.Log("FindIncorrectShader");
var select = Selection.activeObject;
if (!select)
{
return;
}
var folderPath = AssetDatabase.GetAssetPath(select);
if (Directory.Exists(folderPath))
{
var mats = Directory.GetFiles(folderPath, "*.mat", SearchOption.AllDirectories);
foreach (var item in mats)
{
Material material = AssetDatabase.LoadAssetAtPath<Material>(item);
if (material.shader.ToString().IndexOf("PJAQ/Character/ToonV5")==-1)
{
Debug.Log(item.ToString());//
}
}
}
}
11.把Wizard写成一个有取消确认的Tip
using UnityEngine;
using UnityEditor;
public class LocalizationTip : ScriptableWizard
{
private static bool exporting = false;
private static bool translating = false;
private static string content;
private static bool isInitPos = false;
private static Rect initPos;
private static LocalizationTip window = null;
public static void OpenLocalizationTipWizard(bool export, bool translate, string value, Rect pos)
{
isInitPos = false;
initPos = pos;
exporting = export;
translating = translate;
content = value;
if (window == null)
window = DisplayWizard<LocalizationTip>("提示框");
window.Focus();
}
private void OnGUI()
{
if (!isInitPos)
{
isInitPos = true;
float width = 320f;
float heigth = 150f;
var xOffset = (initPos.width - width) / 2;
var yOffset = (initPos.height - heigth) / 2;
position = new Rect(initPos.x + xOffset, initPos.y + yOffset, width, heigth);
}
GUILayout.Label(content + "?", EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
if (GUILayout.Button("取消"))
OnCancel();
if (GUILayout.Button("确定"))
OnConfirm();
GUILayout.EndHorizontal();
}
private void OnConfirm()
{
if (exporting)
{
// LocalizationEditor.Instance.Export();
}
if (translating)
{
// LocalizationEditor.Instance.Translate();
}
}
private void OnCancel()
{
this.Close();
GUIUtility.ExitGUI();
}
}