视频:

正文

特性效果预览

新版本Odin提供一个AttributeOverView EditorWindow,专门用于预览各个特性的效果

通过Tools-Odin Inspector-Attribute Overview即可打开:

image-20210916152203690

PropertyTree

一个超级厉害的属性树,可以支持任意类型(如果一些字段类型无法绘制,则需要添加ShowOdinSerializedPropertiesInInspector字段到根class上)在任意位置上的序列化显示,例如

1
2
3
TargetData targetData = new TargetData();
PropertyTree tree = PropertyTree.Create(targetData);
tree.Draw(false);

另外也可以参考xNode的EditorWindow中的节点,也是这样实现的:

image-20210916151544738

需要注意的是,这个PropertyTree并不会帮我们永久化数据,虽然他内部使用了SO实现,但是仅仅是内存中的,并不会保存到文件,所以临时的绘制可以用任意类,要求持久化的就必须是继承自SO的类。

如果想要针对每个字段的修改进行处理,则需要这样绘制

1
2
3
4
5
6
7
8
9
10
tree.BeginDraw(false);
foreach (var property in propertyTree.EnumerateTree(false, true))
{
EditorGUI.BeginChangeCheck();
property.Draw();
if (EditorGUI.EndChangeCheck())
// do something
}

tree.EndDraw();

OdinEditor

在处理自定义Inspector面板的时候十分方便,base.OnInspectorGUI();就是使用Odin原本的绘制方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[CustomEditor(typeof(XNode.Node), true)]
public class GlobalNodeEditor : OdinEditor
{
public override void OnInspectorGUI()
{
if (GUILayout.Button("Edit graph", GUILayout.Height(40)))
{
SerializedProperty graphProp = serializedObject.FindProperty("graph");
NodeEditorWindow w = NodeEditorWindow.Open(graphProp.objectReferenceValue as XNode.NodeGraph);
w.Home(); // Focus selected node
}
base.OnInspectorGUI();
}
}

GenericSelector

可以通过GenericSelctor来在任意地方绘制一个Odin样式的高级下拉框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 泛型参数为最后的选择结果的类型
GenericSelector<int> CustomGenericSelector;
// 可以通过一个字段来记录选择的结果
int CustomSelection;
// Data,要绘制的数据
Dictionary<string, int> DataForDraw = new Dictionary<string, int>(){};
// 构建下拉框的TreeData
IEnumerable<GenericSelectorItem<int>> customCollection = DataForDraw.Keys.Select(itemName =>
new GenericSelectorItem<int>($"{itemName}", DataForDraw[itemName]));

CustomGenericSelector = new GenericSelector<int>("自定义下拉框", false, customCollection);
// 设置单选
CustomGenericSelector.EnableSingleClickToSelect();
// 注意这个回调会在一次选择后触发两次,第一次数据为空,第二次数据正常,所以需要自行处理
CustomGenericSelector.SelectionChanged += ints =>
{
int result = ints.FirstOrDefault();
if (result != 0)
{
Debug.Log(result.ToString());
}
};
// 正式执行绘制,有多种绘制方式,可自行选择
CustomGenericSelector.ShowInPopup();

image-20210916151901542

DragAndDropUtilities

可以通过DragAndDropUtilities来快速绘制一个可拖动/放置的区域,支持任意类型的数据

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
public class TestDrag
{
public int TestInt;
}
public class drag : EditorWindow
{
[MenuItem("Tools/Drag")]
private static void ShowWindow()
{
var window = GetWindow<drag>();
window.titleContent = new GUIContent("TITLE");
window.Show();
}
public TestDrag TDrag1 = new TestDrag();
public TestDrag TDrag2;
private void OnGUI()
{
DragAndDropUtilities.DrawDropZone(new Rect(100, 100, 100, 100), TDrag1, null, 1);
TDrag1 = DragAndDropUtilities.DragAndDropZone(new Rect(100, 100, 100, 100), TDrag1, typeof(TestDrag), true,
true) as TestDrag;
DragAndDropUtilities.DrawDropZone(new Rect(200, 200, 100, 100), TDrag2, null, 2);
TDrag2 = DragAndDropUtilities.DragAndDropZone(new Rect(200, 200, 100, 100), TDrag2, typeof(TestDrag), true,
true) as TestDrag;
if (TDrag1 != null)
{
Debug.Log("1");
}
if (TDrag2 != null)
{
Debug.Log("2");
}
}
}

OdinAttributeDrawer

可以在DrawPropertyLayout函数中调用this.CallNextDrawer(label),即可复用Odin样式的属性绘制

此外,自带了一个属性树,可以反向查找到包含此Attribute的实例

1
ValueEntry.Property.Parent.ValueEntry.WeakSmartValue

借助OdinAttributeDrawer和GenericSelector可以轻松实现将配置规范化的功能,比如一个int值,我们可以为其重写成一个下拉框的形式,数据从配置表拉取,保证选择的正确性

OdinValueDrawer

被用于绘制某一特定类型作为字段时的样式

需要注意的是,如果类型为ScriptableObject或者其子类,那么在获取Property.Children时将会返回空值,这种情况下如果尝试获取子Property进行处理并绘制就会失败了

BindTypeNameToTypeAttribute

看似平平无奇的类型映射特性,本意是用来在不需要删除旧类型的情况下将数据反序列化到新类型上。

但是我的ET6.0采用了全热更的机制,会把Unity.Model, Unity.ModelView, Unity.Hotfix, Unity.HotfixView这四个程序集里的脚本文件全部构建成一个Hotfix.dll,这也就会导致序列化文件里的程序集与运行时的程序集信息对不上,利用这个特性即可完美处理这种情况

举例:现有TestC类

1
2
3
4
5
6
7
8
9
10
11
12
using ET;
using OdinSerializer;


[assembly: OdinSerializer.BindTypeNameToType("ET.TestC, Unity.Model", typeof(TestC))]
namespace ET
{
public class TestC
{
public int C;
}
}

对其在编辑器模式下进行序列化

1
2
byte[] bytes = OdinSerializer.SerializationUtility.SerializeValue(new TestC(){C = 99}, DataFormat.JSON);
File.WriteAllBytes($"Assets/Test.bytes", bytes);

并在运行时进行反序列化

1
2
3
byte[] fileBytes = File.ReadAllBytes("Assets/Test.bytes");
TestC testC = OdinSerializer.SerializationUtility.DeserializeValue<TestC>(fileBytes, OdinSerializer.DataFormat.JSON);
Log.Info(testC.C.ToString());

测试通过

ListDrawSetting

可以通过这个特性自定义List元素的绘制

其他

  • 注意,组与组之间的嵌套用“/”分割