IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity3D Editor Undo回退效果实现3 Odin相关 -> 正文阅读

[游戏开发]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;
}

总结一下:

  1. Unity能序列化的字段,Odin不会主动序列化;
  2. 使用OdinSerialize和NonSerialized两个标签强制使用Odin序列化;
  3. 当Unity和Odin都能序列化的时候,则会产生两份序列化数据,所以要想好使用哪种序列化,可以使用Serialization Debugger进行确认。

然后根据官网整理了两个重点:

  1. 当子对象中有需要Odin的序列化时,需要从父对象一路下来都要指定为Odin序列化,否则会使用Unity的序列化;
  2. 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中?我还没有做更加深入的研究,或许等哪天需要吧,这边暂时只提一个思路。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-24 15:52:55  更:2021-08-24 15:53:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/5 1:09:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码