Unity编辑器
检视面板简单扩展(Unity特性)
特性 (Attribute) 是可以放在脚本中的类、属性或函数上方来指示特殊行为的标记。
- System命名空间下
特性 | 描述 | 代码描述 | 具体体现 |
---|---|---|---|
Serializable | 序列化一个类,作为另一个类一个子属性在监视面板上显示。与Unity的内置JSON工具运行原理相似。 | ||
NonSerialized | 反序列化一个变量,并且在监视版上隐藏。 | B在检视面板上没有显示 | |
Flags | 定义标志枚举,并实现在检视面板中的多选框(注意:枚举类型的字段默认为单选且是序列化的)实现多选的枚举值应为2的次幂,需要支持位运算 |
- UnityEngine命名空间下
特性 | 描述 | 代码描述 | 具体体现 |
---|---|---|---|
RequireComponent(组件类型Type) | 自动添加需要的组件。若已存在则不额外添加。这样脚本就可以安全的使用该组件。自动添加的组件不能先于该脚本删除 | ||
ContextMenuItem(在检视面板中显示的内容,回调函数) | 给字段右键菜单添加一个自定义方法,不能是静态的。 | ||
ContextMenu(在检视面板中显示的内容) | 为脚本设置菜单添加回调函数,不能是静态的。 | ||
AddComponentMenu(组件的路径,order) | 在编辑器中添加组件菜单,路径中的脚本名称会对实际的脚本名称产生更改 | ||
CreateAssetMenu(fileName = 路径包含名称, menuName = 显示内容, order = 1) | 用于ScriptableObject(参考链接)的子类(Editor),在Project面板中创建并添加资源 | ||
HideInInspector | 在检视面板隐藏公有成员;不改变序列化属性。 | ||
SerializeField | 在检视面板中使私有变量可见;并且可以进行修改;公有变量默认是可序列化的 | ||
ExecuteInEditMode | 在编辑器模式下运行生命周期函数,但不同于在游戏模式那样时刻运行;运行条件:1、Update:在这个场景中任意游戏对象或项目组织发生变化了执行;2、OnGUI:在游戏视口接收到一个Event时执行;3、OnRenderObject:和其他渲染回调函数在场景视口或游戏视口重新渲染时执行。 | ||
Space(需要的空行像素) | 用于在检视面版的字段上加空行 | ||
Header(标题内容) | 标题特性,为检视面版中的字段加一个小标题。 | ||
Multiline(显示的行数) | 可以让string变量在检视面板上显示多行 | ||
TextArea(最小行,最大行) | 让string在检视面板上显示成带滚动条的文本域。 最小行:初始显示的行数;最大行:当行数超出最大行显示滚动条 | ||
Tooltip(提示信息) | 为检视面板上的字段添加提示信息。及鼠标指向字段显示的提示。 | ||
Range(最小值, 最大值) | 在检视面板限制int或float类型变量值。 |
UnityEditor命名空间下
特性 | 描述 | 代码描述 | 具体体现 |
---|---|---|---|
MenuItem(菜单路径包含名称,等校验函数,优先级) | 添加菜单项,只能修饰静态方法。第二个参数若为true,则会先判断该方法是否返回true,若是,则可以使用,若为false,则该按钮是不可用的(灰色的)。 | 在现有组件菜单中添加方法: | 在现有组件菜单中添加方法: |
ContextMenuItem(上下文菜单的名称,被回调的函数名称) | 为公有的字段添加右键方法,添加的方法为非静态的 | ||
ContextMenu(上下文菜单的名称,是否为validate(验证)函数(默认为false),priority优先级用于覆盖菜单项的顺序(默认为1000000)。数字越低,在菜单中显示的越早。) | 为组件添加右键方法,添加的方法为非静态 | ||
CustomEditor(type) | 要自定义编辑器;type为需要自定义脚本编辑器的脚本类型;将type类型的脚本与相应的编辑器脚本做关联 |
编辑器扩展
编辑器弹窗
- 脚本实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
/// <summary>
/// 弹出窗口
/// </summary>
public class PopWindow : EditorWindow
{
[MenuItem("工具/弹出编辑窗口")]
static void PopWindowEditor()
{
//EditorWindow window = GetWindow(typeof(PopWindow));
//GetWindow:获取一个窗口
EditorWindow window = GetWindow<PopWindow>();
window.maxSize = new Vector2(500, 800);
}
//脚本生命周期
private void OnEnable()
{
Debug.Log("调用了OnEnable函数");
}
//脚本生命周期
private void OnDisable()
{
Debug.Log("调用了OnDisable函数");
}
//脚本生命周期
private void Update()
{
if (Selection.activeGameObject == null) return;
GameObject gameObject = Selection.activeGameObject;
Debug.Log(gameObject.name);
}
//脚本生命周期
private void OnGUI()
{
GUILayout.Label("学校");
string text="123456";
GUILayout.TextField(text);
GUILayout.BeginHorizontal();
if (GUILayout.Button("打印123"))
{
Debug.Log("123");
}
if (GUILayout.Button("打印456"))
{
Debug.Log("456");
}
GUILayout.EndHorizontal();
}
//脚本生命周期
private void OnSelectionChange()
{
Debug.Log("选择的游戏物体发生了改变--调用了OnSelectionChange函数");
}
//脚本生命周期
private void OnHierarchyChange()
{
Debug.Log("层级面板发生了改变--调用了OnHierarchyChange函数");
}
//脚本生命周期
/// <summary>
/// 每秒调用十帧;检测检视面板的更新
/// </summary>
private void OnInspectorUpdate()
{
//Debug.Log("检视面板进行了更新--调用了OnInspectorUpdate函数");
}
}
- 效果展示
检视器高级修改(外挂式编程)
-
项目中建立Editor目录
-
Editor目录下建立外挂式开发脚本
-
将编辑器脚本与原始脚本关联
-
原始脚本 Player类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//角色职业
public enum PLAYER_PROFESSION
{
Warrior = 0,
Wizard = 1,
}
//角色天赋(要理解二级制或,可以实现多选)
[System.Flags]
public enum PLAYER_TALENT
{
Strength = 1,
Agility = 2,
Intelligence = 4,
}
//在AddComponent菜单中添加组件 参数一:路径包括名称 ;参数二:在组件菜单中添加新项。??
[AddComponentMenu("控制器/玩家控制器", 1)]
[ExecuteInEditMode]//在编辑器下执行脚本生命周期;当对象发生变化或项目组织发生变化时运行
public class Player : MonoBehaviour
{
public int ID;
public string Name;
public float Atk;
public bool isMan;
public Vector3 HeadDir;
public Color Hair;
public GameObject Weapon;
public Texture Cloth;
public PLAYER_PROFESSION Profession;
public PLAYER_TALENT Talent;
public List<string> Items;
[ContextMenu("打印1122")]//在脚本编辑菜单添加回调函数
private void A()
{
print("1122");
}
}
- 原始脚本显示效果
- 原始脚本的外挂式脚本 PlayerEditor
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;//步骤1:检视器的扩展开发属于编辑器范畴,所以使用 UnityEditor 命名空间
/// <summary>
/// Player的外挂脚本
/// </summary>
[CustomEditor(typeof(Player))]//步骤3:将编辑器脚本与需要编辑的脚本建立外挂的关联关系
public class PlayerEditor : Editor//步骤2:继承Editor,使用声明周期函数及成员变量,参考 MonoBehavior
{
private Player player;
private PopWindow popWindow;
bool isClick;
//步骤4:确定,什么时候显示内容在监视器面板中,即使用生命周期函数
/// <summary>
/// 脚本生命周期
/// 当组件被启用(被挂载到游戏物体上)或处于激活状态或者被点击
/// </summary>
private void OnEnable()
{
player = target as Player;//target:与该编辑器脚本相关联的对象
Debug.Log(player.gameObject.name);
Debug.Log("调用了OnEnable");
}
/// <summary>
/// 重写Editor中的函数 实现自定义的检视面板检查器;每帧都会运行,使用到类: EditorGUILayout
/// </summary>
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
EditorGUILayout.LabelField("玩家信息");
//使用绘制方法,重新绘制ID字段(获取原始ID,绘制后,再对原始ID赋值)
// 参数:属性名称,属性值
player.ID = EditorGUILayout.IntField("玩家ID", player.ID);
//文本
player.Name = EditorGUILayout.TextField("玩家名称", player.Name);
//浮点数
player.Atk = EditorGUILayout.FloatField("玩家攻击力", player.Atk);
#region 滑块与提示盒子
player.Atk = EditorGUILayout.Slider("玩家攻击力滑条", player.Atk, 0, 100);
if (player.Atk > 80)
{
EditorGUILayout.HelpBox(new GUIContent().text = "攻击力高", MessageType.Warning);
}
if (player.Atk < 20)
{
EditorGUILayout.HelpBox(new GUIContent().text = "攻击力低", MessageType.Error);
}
#endregion
//布尔
player.isMan = EditorGUILayout.Toggle("是否为男性", player.isMan);
//向量
player.HeadDir = EditorGUILayout.Vector3Field("头部方向", player.HeadDir);
//颜色
player.Hair = EditorGUILayout.ColorField("头发颜色", player.Hair);
#region 按钮
if (!isClick && GUILayout.Button("第一个按钮"))
{
popWindow = PopWindow.GetWindow<PopWindow>();
isClick = true;
}
else if (isClick && GUILayout.Button("第一个按钮按下了"))
{
popWindow.Close();
isClick = false;
}
GUILayout.BeginHorizontal();
GUILayout.Button("第二个按钮");
if (GUILayout.Button("点击打印123"))
{
Debug.Log("123");
}
GUILayout.EndHorizontal();
#endregion
#region 单选与多选枚举 参数:标题, 组件上的原始值
player.Talent = (PLAYER_TALENT)EditorGUILayout.EnumFlagsField(new GUIContent().text = "角色天赋", player.Talent);
player.Profession = (PLAYER_PROFESSION)EditorGUILayout.EnumPopup(new GUIContent().text = "角色职业", player.Profession);
#endregion
////终极数据类型绘制////////////////////////////////////////////////////////
//更新可序列化数据
serializedObject.Update();
//通过成员变量名找到组件上的成员变量
SerializedProperty sp = serializedObject.FindProperty("Items");
//可序列化数据绘制(取到的数据,标题,是否将所有获得的序列化数据显示出来)
EditorGUILayout.PropertyField(sp, new GUIContent("道具信息"), true);
//将修改的数据,写入到可序列化的原始数据中
serializedObject.ApplyModifiedProperties();
////对象数据类型绘制////////////////////////////////////////////////////////
//参数1:标题
//参数2:原始组件的值
//参数3:成员变量的类型
//参数4:是否可以将场景中的对象拖给这个成员变量
player.Weapon = EditorGUILayout.ObjectField("武器", player.Weapon, typeof(GameObject), true) as GameObject;
player.Cloth = EditorGUILayout.ObjectField("纹理", player.Cloth, typeof(Texture), false) as Texture;
}
/// <summary>
/// 脚本生命周期
/// 当组件不被启用(未被挂载到游戏物体上)或未处于激活状态或者未被点击
/// </summary>
private void OnDisable()
{
Debug.Log("调用了OnDisable");
}
}
- 原始脚本的外挂式脚本显示效果
- 两个按钮叠加的效果
if (!isClick && GUILayout.Button("第一个按钮"))
{
popWindow = PopWindow.GetWindow<PopWindow>();
isClick = true;
}
else if (isClick && GUILayout.Button("第一个按钮按下了"))
{
popWindow.Close();
isClick = false;
}
- 效果展示
Selection类
Selection.activeGameObject:获取和设置激活并选中的游戏对象
OnValidate方法
对一些数值进行验校 参考链接
编辑器模式下OnValidate 仅在下面两种情况下被调用:
- 脚本被加载时
- Inspector 中的任何值被修改时 下面是脚本中如何使用它:
public string str;
private void OnValidate()
{
str = str.ToUpper();
}//使字符串始终显示大写状态
效果: