Unity3D Editor Undo回退效果实现3 Odin相关
Posted 暗光之痕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D Editor Undo回退效果实现3 Odin相关相关的知识,希望对你有一定的参考价值。
环境:Unity2021.1.14 Odin3.0.4 语言:C#
面向:UnityEditor/Odin进阶开发人员
总起
我们在前两篇文章中讲解了Unity中Undo原理并进行了实现,这次我们来看看Odin是如何接入Undo。
实际上Odin本身是基于Unity的IMGUI,所以它Undo的底层实现就是使用的Unity中的Undo。
Odin实现Undo
先来看看以下实现:
using System;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEngine;
public class TestOdinUndo : MonoBehaviour
{
private TestOdinUndoData testOdinUndoData = new TestOdinUndoData();
private PropertyTree _propertyTree;
public PropertyTree propertyTree
{
get
{
if (_propertyTree == null)
{
_propertyTree = PropertyTree.Create(testOdinUndoData);
}
return _propertyTree;
}
}
[OnInspectorGUI]
public void OnInspectorGUI()
{
propertyTree.Draw(false);
}
}
[Serializable]
public class TestOdinUndoData
{
public int iValue;
}
效果如下,iValue能够正确显示出来,不过Undo功能因为Draw中传的是false所以没有生效。
接下来我们尝试在外面包裹一层ScriptableObject转成Unity的Object,再包裹一层SerializedObject进行传入:
using System;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
public class TestOdinUndo : MonoBehaviour
{
// 包裹的第二层,SerializedObject内部是序列化的数据,方便Unity处理
private SerializedObject _serializedObject;
public SerializedObject serializedObject
{
get
{
if (_serializedObject == null)
{
_serializedObject = new SerializedObject(testOdinUndoWrapData);
}
return _serializedObject;
}
}
// 包裹的第一层,是个ScriptableObject
private TestOdinUndoWrapData _testOdinUndoWrapData;
private TestOdinUndoWrapData testOdinUndoWrapData
{
get
{
if (_testOdinUndoWrapData == null)
{
_testOdinUndoWrapData = ScriptableObject.CreateInstance<TestOdinUndoWrapData>();
_testOdinUndoWrapData.data = testOdinUndoData;
}
return _testOdinUndoWrapData;
}
}
private TestOdinUndoData testOdinUndoData = new TestOdinUndoData();
private PropertyTree _propertyTree;
public PropertyTree propertyTree
{
get
{
if (_propertyTree == null)
{
// 创建PropertyTree使用SerializedObject
_propertyTree = PropertyTree.Create(serializedObject);
}
return _propertyTree;
}
}
[OnInspectorGUI]
public void OnInspectorGUI()
{
// 调用Draw,传入true
propertyTree.Draw(true);
}
}
public class TestOdinUndoWrapData : ScriptableObject
{
public TestOdinUndoData data;
}
[Serializable]
public class TestOdinUndoData
{
public int iValue;
}
按照以上即可实现Undo的功能。
事实上Unity Inspector处理各个字段绘制时也使用的是SerializedObject,我们在使用IMGUI写Editor时Unity也是建议使用该对象的。
Odin的序列化
我们先来看以下代码:
using Sirenix.OdinInspector;
public class TestOdinSerialize : SerializedMonoBehaviour
{
public int iValue;
public float fValue;
}
把它做成Prefab保存,我们可以看到iValue和fValue都是由Unity本身进行序列化的:
使用OdinSerialize标签:
using Sirenix.OdinInspector;
using Sirenix.Serialization;
public class TestOdinSerialize : SerializedMonoBehaviour
{
[OdinSerialize]
public int iValue;
public float fValue;
}
保存后结果:
可以看到Odin序列化中也出现了iValue字段,同时Unity也会序列化该字段。
我们通过Serialization Debugger也可以看到该结果。
想要禁用Unity的序列化只需要打上NonSerialized,这样就只会有Odin的序列化:
using System;
using Sirenix.OdinInspector;
using Sirenix.Serialization;
public class TestOdinSerialize : SerializedMonoBehaviour
{
[NonSerialized]
[OdinSerialize]
public int iValue;
public float fValue;
}
总结一下:
- Unity能序列化的字段,Odin不会主动序列化;
- 使用OdinSerialize和NonSerialized两个标签强制使用Odin序列化;
- 当Unity和Odin都能序列化的时候,则会产生两份序列化数据,所以要想好使用哪种序列化,可以使用Serialization Debugger进行确认。
然后根据官网整理了两个重点:
- 当子对象中有需要Odin的序列化时,需要从父对象一路下来都要指定为Odin序列化,否则会使用Unity的序列化;
- Odin序列化的是最佳实践是:尽量使用Unity的序列化,因为Odin的序列化会产生两次序列化过程比较消耗性能。
Odin序列化的一些细节
Odin在处理序列化时实际上是把数据转存到SerializationData上。
在默认情况下会有一个List<SerializationNode>记录想要序列化的数据,然后再由Unity进行序列化,这是之前说的Odin会产生两次序列化过程的本质。
针对这套流程我做了一个简单的小实验:
List<SerializationNode> nodes = new List<SerializationNode>();
nodes.Add(new SerializationNode() {Name = "iValue", Entry = EntryType.Integer, Data = "3"});
using (var context = Cache<DeserializationContext>.Claim())
using (var reader = new SerializationNodeDataReader(context))
using (var resolver = Cache<UnityReferenceResolver>.Claim())
{
reader.Nodes = nodes;
context.Value.IndexReferenceResolver = resolver.Value;
UnitySerializationUtility.DeserializeUnityObject(this, reader);
}
执行这套流程,我们可以看到iValue变成3。
通过这样的实现我们主动构建了这套Node的List,然后再进行反序列化到对象上。
或许通过这套流程我们能实现json转成Node保存到Unity asset中?我还没有做更加深入的研究,或许等哪天需要吧,这边暂时只提一个思路。
以上是关于Unity3D Editor Undo回退效果实现3 Odin相关的主要内容,如果未能解决你的问题,请参考以下文章
Unity3D Editor Undo回退效果实现3 Odin相关