什么是编辑器开发:
- 对编辑器实现功能扩展,一般会使用它开发项目工具或实现Unity插件。
C#中的特性:
- 用于在C#运行时,传递程序中各种元素(类、结构体、变量、方法、枚举、组件)的行为信息的声明标签。一个声明标签是通过放置在它所在应用元素的前面的方括号 [] 中来描述。
特殊目录:
- Plugins:需要跨语言调用的逻辑代码存储目录,手机SDK接入
- Resources:存储跟随游戏包的资源目录
- StreamingAssets:只读,存储跟随游戏包的资源目录
- Editor:编辑器目录,项目中建立的Editor目录,编辑器相关的逻辑和资源会放在其内部,相关内容在打包生成时不会一起生成到项目中,玩家也不会使用到编辑器相关的内容。Editor目录下的脚本,无法挂载在场景对象下。
命名空间:
- Unity代码逻辑命名空间:UnityEngine
- Unity编辑器命名空间:UnityEditor,此命名空间不要出现在游戏被发布的逻辑代码中,会导致项目打包失败!。
检视器属性控制
对检视器原有属性的控制,通过在代码中给变量加特性实现。
- [HideInInspector]
- 可以隐藏公共成员变量,防止Inspector的值影响到他,同时保证脚本中变量的可访问度;
- [SerializeField]
- 将私有变量设置为检视面板可见可修改,Unity会将对象进行序列化存储,即使是私有的,标记为可序列化后也会显示,公有变量默认是可序列化的;
- [Serializable]
- 对象序列化,对象如果不标记为可序列化,则Unity在存储的时候,会认为他不可被序列化,那么也就无法被显示
- [Space(50)]
- 在当前成员变量上方留 50 像素空白区域
- [Header("标题")]
- 在当前成员变量上方加入一个标题文字
- [Tooltip("鼠标移入后的提示")]
- 添加变量悬浮提示,当鼠标放入后会有提示
- [Range(0, 150)]
- 给数值设定范围
- [Multiline(5)]
- 指定输入行字符,参数为行数
- [TextArea(5, 10)]
- 设置默认显示 5 行,最多显示 10 行内容,再多用滚动条控制显示
- ---------------------分割线,下方特性用于方法-------------------
- [ContextMenu("输出攻防比")]
- 在小齿轮中添加一个回调函数,参数为函数名称,用于调用该特性标记的方法
- [ContextMenuItem("执行我的方法", "MyFunc")]
- 给一个变量添加右键菜单,第一个参数是菜单名称,第二个参数是回调函数
- ---------------------分割线,下方特性用于类-------------------
- [AddComponentMenu("自定义控制器/玩家控制器", 1)]
- 将脚本类如 Player 作为组件添加到AddComponent上,第一个参数:分类名/组件名,第二个参数:列表中显示的顺序
- [ExecuteInEditMode]
- 使生命周期函数,在编辑器状态下可以执行,游戏中也可以正常使用,Update()在场景中对象发生变化或项目组织发生变化时会在编辑器下执行
- [RequireComponent(typeof(BoxCollider))]
- 当前组件依赖于盒子碰撞体,当前组件挂载在对象上时,盒子碰撞体会一起被添加上去,当Player组件没有被移除时,盒子碰撞体不能被删除
检视器高级修改(外挂式编程)
高级控制可以将所关联组件检视面板的属性隐藏,然后重新绘制,Editor目录下建立外挂式开发脚本,将编辑器脚本与原始脚本关联
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//步骤1:引入编辑器的命名空间,检视器属于编辑器开发范畴
using UnityEditor;
[CustomEditor(typeof(Player))]//步骤3:将编辑器开发脚本与需要编辑的组件脚本建立外挂关联关系
//外挂脚本因为存储在Editor目录下,所以不会被打入最终的游戏包
//不继承自Mono,而是继承自Editor
public class PlayerEditor : Editor //步骤2:继承Editor类,使用编辑器相关的成员变量和生命周期函数
{
//存储获得的需要编辑显示的组件
private Player _Component;
//步骤4:需要在当前的外挂脚本中,获得需要被扩展的Player组件对象
//当关联组件所在对象被选中或组件被添加时,自动调用
private void OnEnable()
{
//Debug.Log("enable");
//步骤5:获取Player组件对象
_Component = target as Player;
}
//当关联组件所在对象被取消或组件被移除时,调用
private void OnDisable()
{
//Debug.Log("disable");
_Component = null;
}
//用于绘制检视面板的生命周期函数,决定检视面板内容的关键
public override void OnInspectorGUI()
{
//标题显示
EditorGUILayout.LabelField("人物相关属性");
_Component.ID = EditorGUILayout.IntField("玩家ID", _Component.ID);
//文本
_Component.Name = EditorGUILayout.TextField("玩家名称", _Component.Name);
//浮点数
_Component.Atk = EditorGUILayout.FloatField("玩家攻击力", _Component.Atk);
//布尔
_Component.isMan = EditorGUILayout.Toggle("是否为男性", _Component.isMan);
//向量
_Component.HeadDir = EditorGUILayout.Vector3Field("头部方向", _Component.HeadDir);
//颜色
_Component.Hair = EditorGUILayout.ColorField("头发颜色", _Component.Hair);
对象数据类型绘制
//参数1:标题
//参数2:原始组件的值
//参数3:成员变量的类型
//参数4:是否可以将场景中的对象拖给这个成员变量
_Component.Weapon = EditorGUILayout.ObjectField("持有武器", _Component.Weapon, typeof(GameObject), true) as GameObject;
//纹理
_Component.Cloth = EditorGUILayout.ObjectField("衣服材质贴图", _Component.Cloth, typeof(Texture), false) as Texture;
枚举数据类型绘制
//整数转枚举
//int id = 0;
//PLAYER_PROFESSION p = (PLAYER_PROFESSION)id;
//单选枚举(标题, 组件上的原始值)
_Component.Pro = (PlayerProfression)EditorGUILayout.EnumPopup("玩家职业", _Component.Pro);
//多选枚举(标题, 组件上的原始值)
_Component.LoveColor = (PlayerLoveColor)EditorGUILayout.EnumFlagsField("玩家喜欢的颜色", _Component.LoveColor);
终极数据类型绘制
//更新可序列化数据
serializedObject.Update();
//通过成员变量名找到组件上的成员变量
SerializedProperty sp = serializedObject.FindProperty("Items");
//可序列化数据绘制(取到的数据,标题,是否将所有获得的序列化数据显示出来)
EditorGUILayout.PropertyField(sp, new GUIContent("道具信息"), true);
//将修改的数据,写入到可序列化的原始数据中
serializedObject.ApplyModifiedProperties();
滑动条绘制
//滑动条显示(1.标题,2.原始变量,最小值,最大值)
_Component.Atk = EditorGUILayout.Slider(new GUIContent("玩家攻击力"), _Component.Atk, 0, 100);
if (_Component.Atk > 80)
{
//显示消息框(红色)
EditorGUILayout.HelpBox("攻击力太高了", MessageType.Error);
}
if (_Component.Atk < 20)
{
//显示消息框(黄色)
EditorGUILayout.HelpBox("攻击力太低了", MessageType.Warning);
}
//按钮显示和元素排列
//(按钮是否被按下)显示按钮(按钮名称)
GUILayout.Button("来个按钮");
GUILayout.Button("来个按钮");
if (GUILayout.Button("测试点击"))
{
Debug.Log("测试点击");
}
//开始横向排列绘制
EditorGUILayout.BeginHorizontal();
GUILayout.Button("再来个按钮");
GUILayout.Button("再来个按钮");
//结束横向排列绘制
EditorGUILayout.EndHorizontal();
}
}
创建顶部菜单
用于在顶部菜单添加一些常用的固定功能,比如一键导出AB包
//顶部菜单类
public class Menu
{
//在顶部显示"工具"菜单,下方有"导出AB资源包",点击执行函数
[MenuItem("工具/导出AB资源包")]
static void BuildAB()
{
//Debug.Log("导出AB资源包存放路径"),也可以写具体导包逻辑;
Debug.Log(Application.persistentDataPath);
}
}
创建窗口
可以弹出一个自定义窗口
public class PopWindow : EditorWindow
{
[MenuItem("工具/创建窗口")]
static void OpenWindow()
{
//泛型T 窗口类型。必须派生自 EditorWindow。
//第一个参数设置为 true 可创建浮动实用程序窗口,设置为 false 可创建正常窗口。
//第三个参数设置是否为窗口提供焦点(如果已存在)。
PopWindow window = GetWindow<PopWindow>(false, "弹窗标题", true);
window.minSize = new Vector2(40, 30);
window.minSize = new Vector2(80, 60);
}
//开窗口调用
private void OnEnable()
{
Debug.Log("enable");
}
//关窗口调用
private void OnDisable()
{
Debug.Log("disable");
}
//窗口开启就调用
private void Update()
{
Debug.Log("update");
}
//用于绘制窗口内容
private void OnGUI()
{
if (GUILayout.Button("测试点击"))
{
Debug.Log("测试点击");
}
}
//场景结构发生变化,执行回调函数
private void OnHierarchyChange()
{
Debug.Log("hierarchy");
}
//项目结构发生变化,执行回调函数
private void OnProjectChange()
{
Debug.Log("project");
}
//选中物体发生变化,执行回调函数
private void OnSelectionChange()
{
//获取当前选中的物体的名称
Debug.Log(Selection.activeGameObject.name);
}
}