当前位置:网站首页>Unity-编辑器扩展(Editor)
Unity-编辑器扩展(Editor)
2022-08-02 14:11:00 【莉萝爱萝莉】
1. 基本介绍
1. MenuItem
- 使用 MenuItem 特性,让静态函数作为一个菜单栏功能
[MenuItem(“Tools/mytest”)]
- 第三个参数层级,可控制显示的顺序。不同级别的参数最小值和最大值之间最小为11
- 添加快捷键
- %-CTRL
- # -Shift
- & -Alt
[MenuItem( “Tools/New Option %#a” )] 即Alt+Shift+A
[MenuItem( “Tools/Item2 _g” )] 即G
- 特殊路径
- Assets/[] -添加到“Assets”菜单下,同时也显示在右键单击项目视图时弹出的菜单中。
- Asset/Create/[] - 添加在“Assets ->Create”子菜单中。
- CONTEXT/[任意组件名]/[] - 菜单项将出现在给定组件的右键单击菜单中。
[MenuItem("CONTEXT/Rigidbody/Do Something")]
public static void DoSomething (MenuCommand command) {
Rigidbody body= command.context as Rigidbody ;
body.mass = 5 ;
}
- GameObject/[] 菜单项会出现在层级的右键菜单中。但你需要给定一个层级 < 50
[MenuItem(“GameObject/mytest2”, false, 12)]
- 限定
通过第二个参数来限定该按钮是否启用
// 效果
[MenuItem("Assets/SetMass", false, 12)]
static void SetMass()
{
Rigidbody rigidbody = Selection.gameObjects[0].GetComponent<Rigidbody>();
rigidbody.mass = 5;
}
// 约束
[MenuItem("Assets/SetMass", true)]
static bool SetMassOption()
{
return Selection.gameObjects[0].GetComponent<Rigidbody>() != null;
}
2. AddComponentMenu
添加组件到“添加组件”菜单下。
3. ContextMenu、ContextMenuItem
在继承MonoBehaviour时:
特性 | 描述 |
---|---|
ContextMenu | 添加到自身的右键菜单中 |
ContextMenuItem | 添加到菜单列表 |
public class MyObject : MonoBehaviour
{
[ContextMenuItem("+1", "AddValue")]
public int value;
public string data;
[ContextMenu("重置")]
void ResetData()
{
value = 0;
data = "";
}
void AddValue()
{
value++;
}
}
4. ScriptableWizard
继承该类可生成一个点击后关闭并重置的面板,可进行方便的对象创建修改操作
using UnityEngine;
using UnityEditor;
public class MyObjectEditor : ScriptableWizard
{
// 参数
public string changeName = "未命名";
// 创建按钮到编辑器,并设置按钮名称以及标题
[MenuItem("Tools/CreateWizard")]
public static void CreateWizard()
{
// 一个点击后关闭并重置的面板
DisplayWizard<MyObjectEditor>("统一修改", "修改", "第二个按钮");
}
// 点击后关闭界面并执行
private void OnWizardCreate()
{
// 修改选取对象的名字
foreach (var obj in Selection.gameObjects)
{
// 操作记录到unity记录中,以便ctrl+z撤回
Undo.RecordObject(obj, "修改名字");
obj.name = changeName;
}
// 在界面显示提示信息,仅在未关闭时显示
ShowNotification(new GUIContent(Selection.gameObjects.Length + "个对象被修改了"));
}
// 点击另一个按钮,这不会关闭界面
private void OnWizardOtherButton() => OnWizardCreate();
// 仅在初始化和值改变时变化
private void OnWizardUpdate() => HelpInfor();
// 选中物体时调用
private void OnSelectionChange() => HelpInfor();
// 用于提示信息
void HelpInfor()
{
if (Selection.gameObjects.Length == 0)
{
// 错误信息
errorString = "请选择一个对象!";
helpString = "";
}
else
{
// 帮助信息
errorString = "";
helpString = "您选择了" + Selection.gameObjects.Length + "个对象";
}
}
}
5. [UnityEditor.InitializeOnLoadMethod]
每次Unity编译都会运行一次
2. 编译器绘图
1. 绘图方法
- GUILayout
方法 | 描述 |
---|---|
Label | 写入一个文本 |
BeginVertical | 开始垂直绘图 |
BeginHorizontal | 开始水平绘图 |
BeginScrollView | 开始滚动视图 |
Button | 按钮控件,放在IF语句中判断点击 |
- EditorGUILayout
方法 | 描述 |
---|---|
TextField | 文本框 |
EnumPopup | 枚举选项 |
IntField | 整数选项 |
ObjectField(object, typeof, false) | 一个Unity对象 |
PropertyField | 一个SerializedObject类中的对象 |
2. 示例:窗口形式
主面板
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class AudioWindowEditor : EditorWindow
{
static SaveData data;
string path = "Assets/Resources/MainData.asset";
string text;
string search = "";
static GUIStyle textStyle;
static AudioWindowEditor window;
[MenuItem("Tools/test")]
public static void CreateWindow()
{
// 固定大小窗口
// Rect rect = new Rect(0, 0, 800, 600);
// AudioWindowEditor window = GetWindowWithRect(typeof(AudioWindowEditor), rect) as AudioWindowEditor;
if (window == null)
{
window = GetWindow<AudioWindowEditor>("数据管理");
textStyle = new GUIStyle("HeaderLabel");
textStyle.fontSize = 24;
}
window.Show();
}
/// <summary>
/// 每秒调用十次
/// </summary>
private void OnInspectorUpdate()
{
}
private void OnGUI()
{
// 设置标题和其显示的值,将其值返回
// text = EditorGUILayout.TextField("输入文字", text);
if (data == null) data = AssetDatabase.LoadAssetAtPath<SaveData>(path);
if (data == null)
{
// new SaveData()可替换为 CreateInstance<SaveData>()
AssetDatabase.CreateAsset(new SaveData(), path);
AssetDatabase.SaveAssets();
data = AssetDatabase.LoadAssetAtPath<SaveData>(path);
}
var settings = new SerializedObject(data);
GUI.skin.label.fontSize = 16;
GUILayout.Label("基本内容");
EditorGUILayout.PropertyField(settings.FindProperty("key"), new GUIContent("名称"));
// 第一栏
// GUILayout.BeginHorizontal("Wizard Box");
search = EditorGUILayout.TextField("", search, "SearchTextField");
// GUILayout.EndHorizontal();
// 第二栏
GUILayout.BeginVertical("Wizard Box", GUILayout.MaxHeight(1));
GUILayout.BeginHorizontal(new GUIStyle("HelpBox")
{
padding = new RectOffset(10, 0, 4, 4)
});
GUILayout.Label("名称", new GUIStyle("SettingsHeader")
{
fontSize = 16,
alignment = TextAnchor.MiddleLeft,
});
GUILayout.Label("值", new GUIStyle("SettingsHeader")
{
fontSize = 16,
alignment = TextAnchor.MiddleLeft,
});
GUILayout.EndHorizontal();
KeyValue removeOne = null;
KeyValue moveUp = null;
KeyValue moveDown = null;
for (int i = 0; i < data.keyvalues.Count; i++)
{
var dataSlot = data.keyvalues[i];
if (dataSlot.key == null) dataSlot.key = "";
if (dataSlot.key.ToLower().Contains(search.ToLower()))
{
GUILayout.Space(2);
GUILayout.BeginHorizontal("HelpBox");
dataSlot.key = EditorGUILayout.TextField(dataSlot.key);
dataSlot.value = (GameObject)EditorGUILayout.ObjectField(dataSlot.value, typeof(GameObject), false);
if (i != 0)
{
if (GUILayout.Button("↑", GUILayout.MaxWidth(24), GUILayout.MaxHeight(18))) moveUp = dataSlot;
}
else GUILayout.Button("", GUILayout.MaxWidth(24), GUILayout.MaxHeight(18));
if (i != data.keyvalues.Count - 1)
{
if (GUILayout.Button("↓", GUILayout.MaxWidth(24), GUILayout.MaxHeight(18))) moveDown = dataSlot;
}
else GUILayout.Button("", GUILayout.MaxWidth(24), GUILayout.MaxHeight(18));
if (GUILayout.Button("-", GUILayout.MaxWidth(24), GUILayout.MaxHeight(18))) removeOne = dataSlot;
GUILayout.EndHorizontal();
}
}
if (removeOne != null) data.keyvalues.Remove(removeOne);
if (moveUp != null)
{
int index = -1;
for (int i = 0; i < data.keyvalues.Count; i++) if (data.keyvalues[i] == moveUp) index = i;
if (index != -1)
{
var move = moveUp;
data.keyvalues.Remove(moveUp);
data.keyvalues.Insert(index - 1, move);
}
}
if (moveDown != null)
{
int index = -1;
for (int i = 0; i < data.keyvalues.Count; i++) if (data.keyvalues[i] == moveDown) index = i;
if (index != -1)
{
var move = moveDown;
data.keyvalues.Remove(moveDown);
data.keyvalues.Insert(index + 1, move);
}
}
if (GUILayout.Button("+"))
{
data.keyvalues.Add(new KeyValue());
}
GUILayout.EndVertical();
// 应用修改
settings.ApplyModifiedPropertiesWithoutUndo();
}
}
数据类
using UnityEngine;
using System.Collections.Generic;
public class SaveData : ScriptableObject
{
public string key;
public List<KeyValue> keyvalues;
}
[System.Serializable]
public class KeyValue
{
public string key;
public GameObject value;
}
3. 示例:项目设置中添加项形式
using UnityEditor;
using UnityEngine;
public class MainDataEditor
{
static MainData data;
[SettingsProvider]
public static SettingsProvider MyCustom()
{
string path = "Assets/Resources/Data/MainData.asset";
var provider = new SettingsProvider("MainData", SettingsScope.Project)
{
// 默认情况下,如果未提供标签,则路径的最后一个标记将用作显示名称。
label = "主线资源库",
// 填充搜索关键字
// 创建SettingsProvider并将其绘图(IMGUI)功能初始化
guiHandler = (searchContext) =>
{
if (data == null) data = AssetDatabase.LoadAssetAtPath<MainData>(path);
if (data == null)
{
// new MainData()可替换为 CreateInstance<MainData>()
AssetDatabase.CreateAsset(new MainData(), path);
AssetDatabase.SaveAssets();
data = AssetDatabase.LoadAssetAtPath<MainData>(path);
}
var settings = new SerializedObject(data);
GUI.skin.label.fontSize = 16;
GUILayout.Label("基本扩展");
EditorGUILayout.PropertyField(settings.FindProperty("blood"), new GUIContent("溅血特效"));
EditorGUILayout.PropertyField(settings.FindProperty("spark"), new GUIContent("火星特效"));
EditorGUILayout.PropertyField(settings.FindProperty("sharp"), new GUIContent("锋利特效"));
// 应用修改
settings.ApplyModifiedPropertiesWithoutUndo();
}
};
return provider;
}
}
数据类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
[CreateAssetMenu(fileName = "MainData", menuName = "Data/MainData", order = 1)]
public class MainData : ScriptableObject
{
/// <summary>
/// 溅血特效
/// </summary>
public GameObject blood;
/// <summary>
/// 火星特效
/// </summary>
public GameObject spark;
/// <summary>
/// 锋利特效
/// </summary>
public GameObject sharp;
/// <summary>
/// 结束的线
/// </summary>
public GameObject endLine;
/// <summary>
/// 结束光环
/// </summary>
public GameObject endNova;
/// <summary>
/// 孔明灯
/// </summary>
public GameObject KongMingLight;
/// <summary>
/// 枪线
/// </summary>
public GameObject weaponLine;
public TileBase[] tileBases;
public AudioClip[] audioClips;
/// <summary>
/// 背包
/// </summary>
public Bag bag;
/// <summary>
/// 提示信息
/// </summary>
public Infor infor;
public Dialog dialog;
/// <summary>
/// 失败面板
/// </summary>
public Fail failPanel;
public Fail winPanel;
public LoadScene loadScenePanel;
public Sprite[] move_Sprs;
public WeaponData[] weapons;
/// <summary>
/// 保存值
/// </summary>
public SaveData saveData;
public Player player;
/// <summary>
/// 物品列表
/// </summary>
public Good[] goods;
}
边栏推荐
猜你喜欢
随机推荐
Knapsack Problem - Dynamic Programming - Theory
MATLAB drawing command fimplicit detailed introduction to drawing implicit function graphics
开心一下,9/28名场面合集
Introduction to MATLAB drawing functions ezplot explanation
Doubled and sparse tables
网络安全抓包
LeetCode 2344. 使数组可以被整除的最少删除次数 最大公约数
Problems related to prime numbers - small notes
jest test, component test
Based on the least squares linear regression equation coefficient estimation
轻量化AlphaPose
推开机电的大门《电路》(一):电压,电流,参考方向
Network Security Packet Capture
MATLAB绘制平面填充图入门详解
第三十章:普通树的存储和遍历
pygame绘制弧线
【系统设计与实现】基于flink的分心驾驶预测与数据分析系统
LeetCode 2353. 设计食物评分系统 维护哈希表+set
远程连接Ubuntu中的Mysql
二叉排序树与 set、map