ET ReferenceCollector
结构
-
ReferenceCollectorData
该类主要用来保存引用信息,并使其能在Inspector面板显示
[Serializable]//使其能在Inspector面板显示,并且可以被赋予相应值 public class ReferenceCollectorData { public string key; //Object并非C#基础中的Object,而是 UnityEngine.Object public Object gameObject; }
-
ReferenceCollectorDataComparer
该类主要用于对比两个
ReferenceCollectorData
是否重复。//继承IComparer对比器 public class ReferenceCollectorDataComparer : IComparer<ReferenceCollectorData> { public int Compare(ReferenceCollectorData x, ReferenceCollectorData y) { //StringComparison.Ordinal会使用序号排序规则比较字符串,因为是byte级别的比较,所以准确性和性能都不错 return string.Compare(x.key, y.key, StringComparison.Ordinal); } }
-
ReferenceCollector
-
容器
//用于序列化的List public List<ReferenceCollectorData> data = new List<ReferenceCollectorData>(); //Object并非C#基础中的Object,而是 UnityEngine.Object private readonly Dictionary<string, Object> dict = new Dictionary<string, Object>();
-
-
继承
ISerializationCallbackReceiver
并实现OnAfterDeserialize
和OnBeforeSerialize
两个回调函数,使其能在序列化后做一些操作/// <summary> /// 在反序列化后运行 /// </summary> public void OnAfterDeserialize() { dict.Clear(); foreach (ReferenceCollectorData referenceCollectorData in data) { //筛选重复项 if (!dict.ContainsKey(referenceCollectorData.key)) { //添加到字典中 dict.Add(referenceCollectorData.key, referenceCollectorData.gameObject); } } }
-
UNITY_EDITOR方法
#if UNITY_EDITOR //添加新的元素 public void Add(string key, Object obj) { //SerializedObject和SerializedProperty是提供通用的方式编辑对象属性的类 SerializedObject serializedObject = new SerializedObject(this); //根据PropertyPath读取数据 //如果不知道具体的格式,可以右键用文本编辑器打开一个prefab文件(如Bundles/UI目录中的几个) //因为这几个prefab挂载了ReferenceCollector,所以搜索data就能找到存储的数据 SerializedProperty dataProperty = serializedObject.FindProperty("data"); int i; //遍历data,看添加的数据是否存在相同key for (i = 0; i < data.Count; i++) { if (data[i].key == key) { break; } } //不等于data.Count意为已经存在于data List中,直接赋值即可 if (i != data.Count) { //根据i的值获取dataProperty,也就是data中的对应ReferenceCollectorData,不过在这里,是对Property进行的读取,有点类似json或者xml的节点 SerializedProperty element = dataProperty.GetArrayElementAtIndex(i); //对对应节点进行赋值,值为gameobject相对应的fileID //fileID独一无二,单对单关系,其他挂载在这个gameobject上的script或组件会保存相对应的fileID element.FindPropertyRelative("gameObject").objectReferenceValue = obj; } else { //等于则说明key在data中无对应元素,所以得向其插入新的元素 dataProperty.InsertArrayElementAtIndex(i); SerializedProperty element = dataProperty.GetArrayElementAtIndex(i); element.FindPropertyRelative("key").stringValue = key; element.FindPropertyRelative("gameObject").objectReferenceValue = obj; } //应用与更新 EditorUtility.SetDirty(this); serializedObject.ApplyModifiedProperties(); serializedObject.UpdateIfRequiredOrScript(); } //删除元素,知识点与上面的添加相似 public void Remove(string key) { SerializedObject serializedObject = new SerializedObject(this); SerializedProperty dataProperty = serializedObject.FindProperty("data"); int i; for (i = 0; i < data.Count; i++) { if (data[i].key == key) { break; } } if (i != data.Count) { dataProperty.DeleteArrayElementAtIndex(i); } EditorUtility.SetDirty(this); serializedObject.ApplyModifiedProperties(); serializedObject.UpdateIfRequiredOrScript(); } public void Clear() { SerializedObject serializedObject = new SerializedObject(this); //根据PropertyPath读取prefab文件中的数据 //如果不知道具体的格式,可以直接右键用文本编辑器打开,搜索data就能找到 var dataProperty = serializedObject.FindProperty("data"); dataProperty.ClearArray(); EditorUtility.SetDirty(this); serializedObject.ApplyModifiedProperties(); serializedObject.UpdateIfRequiredOrScript(); } public void Sort() { SerializedObject serializedObject = new SerializedObject(this); data.Sort(new ReferenceCollectorDataComparer()); EditorUtility.SetDirty(this); serializedObject.ApplyModifiedProperties(); serializedObject.UpdateIfRequiredOrScript(); } #endif
ReferenceCollectorEditor
主要重构了ReferenceCollector
组件的Inspector面板
在OnInspectorGUI中实现了如下几个功能
-
按钮模块(这一部分按钮事件实际上是在
ReferenceCollector
的UNITY_EDITOR方法实现的)- 添加引用
- 删除所有
- 删除空引用
- 排序
-
搜索框
-
字典模块
-
拖动功能
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
//Object并非C#基础中的Object,而是 UnityEngine.Object
using Object = UnityEngine.Object;
//自定义ReferenceCollector类在界面中的显示与功能
[CustomEditor(typeof(ReferenceCollector))]
//没有该属性的编辑器在选中多个物体时会提示“Multi-object editing not supported”
[CanEditMultipleObjects]
public class ReferenceCollectorEditor : Editor
{
private ReferenceCollector referenceCollector;
//搜索Key
private string _searchKey = "";
//搜索框对应对象
private Object heroPrefab;
//输入在textfield中的字符串
private string searchKey
{
get
{
return _searchKey;
}
set
{
if (_searchKey != value)
{
_searchKey = value;
heroPrefab = referenceCollector.Get<Object>(searchKey);
}
}
}
private void OnEnable()
{
//将被选中的gameobject所挂载的ReferenceCollector赋值给编辑器类中的ReferenceCollector,方便操作
referenceCollector = (ReferenceCollector)target;
}
//OnInspectorGUI可以自定义对Inspector面板的绘制
public override void OnInspectorGUI()
{
//使ReferenceCollector支持撤销操作,还有Redo,不过没有在这里使用
Undo.RecordObject(referenceCollector, "Changed Settings");
//获取所有引用对象
var dataProperty = serializedObject.FindProperty("data");
//-----------------------按钮栏------------------------
//开始水平布局
GUILayout.BeginHorizontal();
//下面几个if都是点击按钮就会返回true调用里面的东西
if (GUILayout.Button("添加引用"))
{
//添加新的元素,具体的函数注释
// Guid.NewGuid().GetHashCode().ToString() 就是新建后默认的key
AddReference(dataProperty, Guid.NewGuid().GetHashCode().ToString(), null);
}
if (GUILayout.Button("全部删除"))
{
referenceCollector.Clear();
//dataProperty.ClearArray();
}
if (GUILayout.Button("删除空引用"))
{
referenceCollector.DelNull();
//DelNullReference();
}
if (GUILayout.Button("排序"))
{
referenceCollector.Sort();
}
//结束水平对齐
GUILayout.EndHorizontal();
//-----------------搜索栏---------------------
//开始新的水平对齐
EditorGUILayout.BeginHorizontal();
//可以在编辑器中对searchKey进行赋值,只要输入对应的Key值,就可以点后面的删除按钮删除相对应的元素
//输入框
searchKey = EditorGUILayout.TextField(searchKey);
//添加的可以用于选中Object的框,这里的object也是(UnityEngine.Object
//第三个参数为是否只能引用scene中的Object
//对象框
EditorGUILayout.ObjectField(heroPrefab, typeof(Object), false);
//删除按键
if (GUILayout.Button("删除"))
{
referenceCollector.Remove(searchKey);
heroPrefab = null;
}
//结束水平对齐
GUILayout.EndHorizontal();
//留白空间
EditorGUILayout.Space();
//---------------字典-----------------
var delList = new List<int>();
SerializedProperty property;
//遍历ReferenceCollector中data list的所有元素,显示在编辑器中
for (int i = referenceCollector.data.Count - 1; i >= 0; i--)
{
//开始新的水平对齐
GUILayout.BeginHorizontal();
//这里的知识点在ReferenceCollector中有说
property = dataProperty.GetArrayElementAtIndex(i).FindPropertyRelative("key");
//输入文本框
property.stringValue = EditorGUILayout.TextField(property.stringValue, GUILayout.Width(150));
property = dataProperty.GetArrayElementAtIndex(i).FindPropertyRelative("gameObject");
//对象框
property.objectReferenceValue = EditorGUILayout.ObjectField(property.objectReferenceValue, typeof(Object), true);
if (GUILayout.Button("X"))
{
//将元素添加进删除list
delList.Add(i);
}
//结束水平对齐
GUILayout.EndHorizontal();
}
//----------------------拖动-----------------------
var eventType = Event.current.type;
//在Inspector 窗口上创建区域,向区域拖拽资源对象,获取到拖拽到区域的对象
if (eventType == EventType.DragUpdated || eventType == EventType.DragPerform)
{
// 在拖放上显示一个复制图标
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if (eventType == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
foreach (var o in DragAndDrop.objectReferences)
{
//将元素添加到字典中
AddReference(dataProperty, o.name, o);
}
}
Event.current.Use();
}
//遍历删除list,将其删除掉
foreach (var i in delList)
{
dataProperty.DeleteArrayElementAtIndex(i);
}
//应用修改属性,并更新脚本
serializedObject.ApplyModifiedProperties();
serializedObject.UpdateIfRequiredOrScript();
}
//添加元素,具体知识点在ReferenceCollector中说了
private void AddReference(SerializedProperty dataProperty, string key, Object obj)
{
int index = dataProperty.arraySize;
dataProperty.InsertArrayElementAtIndex(index);
var element = dataProperty.GetArrayElementAtIndex(index);
element.FindPropertyRelative("key").stringValue = key;
element.FindPropertyRelative("gameObject").objectReferenceValue = obj;
}
}
相关扩展类
GameObjectHelper
主要实现了一个对GameObject查找引用的快捷扩展
public static class GameObjectHelper
{
/// <summary>
/// 通过对象挂载的引用集脚本获取对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="gameObject"></param>
/// <param name="key"></param>
/// <returns></returns>
public static T Get<T>(this GameObject gameObject, string key) where T : class
{
try
{
return gameObject.GetComponent<ReferenceCollector>().Get<T>(key);
}
catch (Exception e)
{
throw new Exception($"获取{gameObject.name}的ReferenceCollector key失败, key: {key}", e);
}
}
}
网友评论