当前位置:   article > 正文

【学习笔记】Unity扩展编辑器【待更新】_unity customeditor 访问集合

unity customeditor 访问集合

先贴一张学习导图

在这里插入图片描述

扩展菜单栏

菜单栏

MemuItem

MenuItem 属性用于向主菜单和检视面板上下文菜单添加菜单项。
该 MenuItem 属性能够将任何静态函数转变为菜单命令。仅静态函数可使用 MenuItem 属性。

热键内容
%在Windows和Linux上为ctrl,在macOS上为cmd
^在Windows、Linux和macOS上的ctrl
#shift
&alt
_X字母热键 _g _x _a 等等

支持一些特殊的键盘键作为热键,例如“#LEFT”将映射为向左移动。像这样支持的键有:LEFT、RIGHT、UP、DOWN、F1···F12,HOME,END,PGUP,PGDN,INS,DEL,TAB,SPACE。

[MenuItem (string itemName, bool isValidateFunction, int priority)]
  • 1
  • itemName 路径名 (特殊路径:CONTEXT/ComponentName - 菜单项将出现在给定组件的(右键单击显示的菜单)中,即复位,复制组件的那个菜单里)
  • isValidateFunction 有效验证 (当为true时,将为验证函数,为同名itemName,但是false的函数来判断是否执行,验证函数为返回值为false时,非验证函数就可以调用
[MenuItem("MyMenu/1",false,1)]
static void Test1() {
	Debug.Log("1111");
}
[MenuItem("MyMenu/1", true, 3)]
static bool Test2() {
	Debug.Log("Test");
	return false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

有效验证:相同的itemName,当验证函数返回false时,那么Test1则不会调用(无法调用)


  • priority 菜单栏排序 当满足有两个priority之间的差大于10(大于等于11)时两个菜单之间会出现一个分栏
    在这里插入图片描述在这里插入图片描述

判断的是3和14


快捷键设置

// 单一个字母
[MenuItem("MyMenu/Test _g")]
static void Test() {
	Debug.Log("Test");
}
// 热键+字母
[MenuItem("MyMenu/Test ^g")]
// shift + ←
[MenuItem("MyMenu/Test #LEFT")]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

扩展

MenuCommand

当添加一个新的菜单项到Inspector中(使用 “CONTEXT/Component”),获取一个真实组件的引用也是有必要的

[MenuItem("CONTEXT/Transform/Init")]
static void Test3(MenuCommand cmd) {
	Transform trans = cmd.context as Transform;
	Debug.Log(trans.name);
}
  • 1
  • 2
  • 3
  • 4
  • 5

和ContextMenu很像,ContextMenu给指定的组件定义默认的上下文菜单项,然而使用MeneItem只是为了扩展现有的组件菜单

扩展检视面板

特性

ContextMenu

给方法添加右键菜单

public ContextMenu (string itemName, bool isValidateFunction, int priority);
//itemName 路径名
//isValidateFunction 有效验证
//priority 菜单栏排序
  • 1
  • 2
  • 3
  • 4
[ContextMenu("Test")]
public void Test() {
	Debug.Log(name);
}
  • 1
  • 2
  • 3
  • 4
ContextMenuItem

给字段添加右键菜单

public ContextMenuItemAttribute (string name, string function);
// name 上下文名字
// function 应调用的函数名字
  • 1
  • 2
  • 3
[ContextMenuItem("Add HP", "AddHp")]
public int HP = 0;

void AddHp() {
	HP++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

自定义Inspector面板显示

告知编辑器类该编辑器所针对的运行时类型。

[CustomEditor(Type inspectedType, bool editorForChildClasses)]
  • 1
  • editorForChildClasses 如果为 true,则 inspectedType 的子类也将显示此编辑器。默认为 false。

样例

[CustomEditor(typeof(ViewScript))]	// 标记
public class ViewScriptEditor : Editor {
    // 检视面板属性绘制
	public override void OnInspectorGUI(){
        base.OnInspectorGUI();
    }
} 

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

扩展窗口

自定义对话框

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
 
public class TestScriptableWizard : ScriptableWizard
{
    public string s = "aaa";
    public int i = 500;
    public Color color = Color.red;
 
    [MenuItem("TestMenu/MyScriptableWizard")]  
    //弹出窗口调用
    static void CreateWizard()
    {
        // 展示对话框
        ScriptableWizard.DisplayWizard<TestScriptableWizard>("MyTestMenu", "Create", "other");// 设置标题,Create按钮的文本,other按钮的文本
        Debug.Log("CreateWizard");
    }
    //开启窗口或数据更新时调用
    void OnWizardUpdate()
    {
        helpString = "帮助或说明……";
        Debug.Log("OnWizardUpdate");
    }
    //当用户按下"other"时被调用,保存设置但不关闭窗口
    void OnWizardOtherButton()
    {
        Debug.Log("OnWizardOtherButton");
    }
    //点击"Create"时调用,关闭窗口并保存设置
    void OnWizardCreate()
    {
        Debug.Log("OnWizardCreate");
    }
    
    // 当选择发生改变的时候调用
	private void OnSelectionChange() {
		
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
方法内容
public void ShowNotification (GUIContent notification);在窗口上显示通知消息。与消息框或日志消息不同,通知会在一段时间后自动消失。

自定义窗口

要继承EditorWindow

public class MyWindow : EditorWindow {

	[MenuItem("Window/My_Window",false,-30)]
	static void ShowMyWindow() {
		// 获取Window,没有则创建
		MyWindow window = EditorWindow.GetWindow<MyWindow>();
		window.Show();	// 展示
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
函数内容
EditorWindow.GetWindow()返回当前屏幕上第一个 t 类型的 EditorWindow。如果没有,则创建并显示新窗口,然后返回其实例。

GUI

分为GUI,EditorGUI,GUILayout,EditorGUILayout

不加Layout的不会自带布局,要自己手动确认位置
EditorGUI只是GUI的一个扩展(但是只能用在Editor模式下)

布局

  • .BeginHorizontal().EndHorizontal();水平布局
  • .BeginVertical();.EndVertical();垂直布局(默认就是垂直)
  • BeginArea;EndArea;在一个固定的屏幕区域中开始 GUI 控件的 GUILayout 块。

大小&外观

  • Width,MinWidth,MaxWidth 绝对大小,最小,最大(高度同理)

  • GUIStyle:Box,Button,laybel

UI

  • Toolbar 创建一个工具栏
  • Label 创建一个标签
  • TextField 创建一个文本字段
  • Button 创建一个单击按钮
  • PasswordField 创建一个输入密码文本字段
  • RepeatButton 创建一个重复按钮(只有按住才一直返回true)
  • Toggle 创建一个单选按钮

数据

保存Editor数据

不建议,因为是共享的数据,保存在注册表中,而且里面还存有Unity自己的键值对,建议用PlayerPrefs

// 和PlayerPrefs差不多
EditorPrefs.SetInt(string key,int value);
...
  • 1
  • 2
  • 3

序列化

SerializedObject

SerializedObjectSerializedProperty 两个类能够以完全通用的方式编辑 Unity 对象上的可序列化字段。这些类可自动处理个别序列化字段的脏化,以便 Undo 系统处理这些字段,并在检视面板中绘制时针对预制件重载正确设置这些字段的样式

static class Example2
{
    [MenuItem("Edit/Reset Selected Objects Position")]
    static void ResetPosition()
    {
        var transforms = Selection.gameObjects.Select(go => go.transform).ToArray();
        var so = new SerializedObject(transforms);
        // you can Shift+Right Click on property names in the Inspector to see their paths
        so.FindProperty("m_LocalPosition").vector3Value = Vector3.zero;
        so.ApplyModifiedProperties();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
函数内容
ApplyModifiedProperties应用属性修改
ApplyModifiedPropertiesWithoutUndo在不注册撤销操作的情况下应用属性修改
FindProperty按名称查找序列化属性
GetIterator获取第一个序列化字段(迭代器)

SerializedProperty

这是SerializedObject中获取的属性字段

函数内容
objectReferenceValue对象属性的值(Object)
stringValue字符串属性的值。
name属性名称
isArray是否是数组
arraySize数组的大小
type属性的类型名称
Next()移至下一个属性
NextVisible()移至下一个可见属性
Reset()移至对象的第一个属性。

生成Prefab

PrefabUtility

函数内容
SaveAsPrefabAsset在给定路径上,从给定的游戏对象创建一个预制件资源
SaveAsPrefabAssetAndConnect在给定路径上,从给定的游戏对象创建一个预制件资源,同时会使游戏对象和预设体关联引用
ApplyPrefabInstance将预制件实例上的所有重载应用于其预制件资源;相当于检视面板上的Apply All

工具

神奇的函数

TypeCache

获取从 T 类型派生的类型的集合。

通过此方法可以快速访问 Unity 域程序集中派生自特定类或实现特定接口的所有类。基类或接口可为通用类型。

TypeCache.GetTypesDerivedFrom(Type parentType);
  • 1

获取用 T 属性标记的方法的集合。

通过此方法可以快速访问从 Unity 域程序集加载并用特定属性标记的所有方法。结果中还包含用指定属性的上级标记的方法。

TypeCache.GetMethodsWithAttribute(Type attrType);
  • 1

AssetDatabase

AssetDatabase 接口仅在编辑器中可用,它仅适用于放置在 Editor 文件夹中的脚本


函数内容
SaveAssets()将所有未保存的资源更改写入磁盘
.Refresh()刷新导入所有更改的资源
.FindAssets()使用搜索筛选器字符串搜索资源数据库,返回该资源的GUID(查找资源包括文件夹,文件)(后续有解释)
.GUIDToAssetPath()通过资源GUID获取资源路径,返回相对于项目文件夹的路径
.GUIDFromAssetPath获取 path下资源的 GUID

AssetDatabase.FindAssets(string filter[, string[] searchInFolders]);
  • 1
  • filter:要筛选的字符串可以包含搜索数据
  • 要从中开始搜索的文件夹

案例

AssetDatabase.FindAssets("A");	//会筛选名称带有A的;eg。AXXX XXAXX
AssetDatabase.FindAssets("A B C");	// 多值匹配(与顺序无关),名称要同时满足有A、B、C
AssetDatabase.FindAssets("l:Field XXX"); // 标签匹配,同时可以在后续添加筛选名称
AssetDatabase.FindAssets("t:texture2D XXX"); // 类型匹配,同时可以在后续添加筛选名称
  • 1
  • 2
  • 3
  • 4

Undo

让您可以针对要执行更改的特定对象注册撤销操作。
要写在要改动之前
在这里插入图片描述


函数内容
RecordObject记录执行函数调用之后对象所做的任何更改;使用这一函数可以记录几乎所有属性的更改(有解释)
DestroyObjectImmediate销毁对象并记录撤销操作,以便能够重新创建该对象。(解释)
AddComponent记录新添加的组件
RegisterCompleteObjectUndo记录创建新的游戏对象
SetTransformParent记录更改变换组件的父子化

Undo.RecordObject (Object objectToUndo, string name);
  • 1
  • objectToUndo:对要修改对象的引用
  • name:要在撤消历史记录中显示(即在撤销菜单中可见)的操作的名称

无法使用这一函数记录变换组件的父项、AddComponent 和对象销毁,应该使用专用函数来记录。


DestroyObjectImmediate (Object objectToUndo);
  • 1
名称内容
objectToUndo要销毁的对象。

Selection

访问编辑器中的选择。

列出常用的,更多看API

静态变量、函数内容
activeGameObject返回处于活动状态的游戏对象。(显示在检视面板中的对象)它还将返回可能为预制件或不可修改对象的游戏对象(当然除了获取也可以设置)
activeObject返回实际对象选择。包括预制件、不可修改的对象。
activeTransform返回处于活动状态的变换。(显示在检视面板中的变换)。此函数绝不会返回预制件或不可修改的对象。
selectionChanged当前活动/所选项发生更改时触发的委托回调。
assetGUIDs返回所选资源的 GUID。在处理资源选择时,建议使用此属性,因为它还可以处理是否在 Project Browser(处于双列模式)的第一列中选择了文件夹。

EditorUtility

EditorUtility

函数内容
.SetDirty(Object target);将目标标记为脏数据
.ClearDirty(Object target);清除脏标记
.DisplayProgressBar(string title, string info, float progress);显示或者更新一个进度条
.ClearProgressBar()移除进度条,Diaplay后不会自动关闭,需要手动调用这个

EditorApplication

主应用程序类,和Application不一样

函数内容
EnterPlaymode()将编辑器切换为运行模式。等同于将EditorApplication.isPlaying设置为 true
ExitPlaymode()将编辑器切换为编辑模式。等同于将EditorApplication.isPlaying设置为 false
OpenProject()打开某个项目,具体到项目路径(即该项目Assets前一级)

系统事件

重新加载脚本回调

using UnityEditor.Callbacks
[DidReloadScripts]
static void Callback(){
    ......
}
  • 1
  • 2
  • 3
  • 4
  • 5

案例工具

自定义属性绘制【显示】

  1. 首先需要类继承PropertyAttribute,unity自带自定义属性特性
  2. 重构特性的绘制PropertyDrawer

例子:获取构建项目的所有场景,并且以下拉框的形式出现在检视面板

SceneNameAttribute

public class SceneNameAttribute : PropertyAttribute {

}
  • 1
  • 2
  • 3

SceneNameDrawer

using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(SceneNameAttribute))]
public class SceneNameDrawer : PropertyDrawer {
	int sceneIndex = -1;
	GUIContent[] sceneNames;

	readonly string[] scenePathSplit = { "/", ".unity" };

	/// <summary>
	/// GUI的绘制
	/// </summary>
	/// <param name="position">用于绘制的位置</param>
	/// <param name="property">用来描述当前选项的容器</param>
	/// <param name="label">此属性的标签</param>
	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
		if (EditorBuildSettings.scenes.Length == 0) return; //在buildsetting中没有场景

		// 初始化【获取所有场景名字,并构建成GUIContent容器
		if (sceneIndex == -1) {
			GetSceneNamesArray(property);
		}

		
		int oldindex = sceneIndex;
		sceneIndex = EditorGUI.Popup(position,label, sceneIndex, sceneNames);
		// 如果菜单选择的和之前不一致需要更新显示的名字
		if (oldindex != sceneIndex) {
			property.stringValue = sceneNames[sceneIndex].text;
		}
	}

	/// <summary>
	/// 获取所有场景名字数组
	/// </summary>
	/// <param name="property"></param>
	private void GetSceneNamesArray(SerializedProperty property) {
		var scenes = EditorBuildSettings.scenes;	// 获取到构建设置中的所有场景
		// 初始化数组
		sceneNames = new GUIContent[scenes.Length];

		// 获取场景的名字【切割】,并构建所有的GUIContent元素
		for (int i = 0; i < scenes.Length; i++) {
			string path = scenes[i].path;

			// 规定切割的内容,移除那些空的内容
			string[] splitPath = path.Split(scenePathSplit, System.StringSplitOptions.RemoveEmptyEntries);

			string sceneName = "";

			if (splitPath.Length > 0) {
				sceneName = splitPath[splitPath.Length - 1];
			}
			else {
				sceneName = "(Delete Scene)";
			}

			sceneNames[i] = new GUIContent(sceneName);
		}

		// 没有场景
		if (scenes.Length == 0) {
			sceneNames = new[] { new GUIContent("Check Your Build Setting") };
		}

		// 如果当前属性的内容不为空的
		if (!string.IsNullOrEmpty(property.stringValue)) {
			bool nameFound = false;
			// 检测填写的内容是否存在【寻找名字】
			for (int i = 0; i < sceneNames.Length; i++) {
				if (sceneNames[i].text == property.stringValue) {
					sceneIndex = i;
					nameFound = true;
					break;
				}
			}
			if (!nameFound) sceneIndex = 0;
		}
		else {
			// 默认第一个
			sceneIndex = 0;
		}

		// 属性内容为选择的场景名字
		property.stringValue = sceneNames[sceneIndex].text;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/806530
推荐阅读
相关标签
  

闽ICP备14008679号