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回退效果实现1 -> 正文阅读

[游戏开发]Unity3D Editor Undo回退效果实现1

环境:Unity2021.1.14 Odin3.0.4 语言:C#

面向:Editor开发人员

文章参考:

http://49.233.81.186/undo.html

总起

因为Unity在用于中小型游戏开发的时候并不太需要对Editor进行深度扩展,而现阶段使用Unity进行大型游戏的开发比较少,所以Editor相关的资料相对匮乏。

安藤圭吾的《エディター拡張入門》作为为数不多的Unity Editor入门经典,内容丰富,并且深入讲述了很多原理性的知识,非常值得一看。

本文主要基于其书的第12章,并结合自己的开发经验,针对Undo进行讲解。

体验Undo操作

在Hierarchy视图中右键,并选择3D Object/Cube,创建一个Cube:

接下来按下Ctrl + Z或点击菜单栏中Edit/Undo Create Cube撤销操作:

可以看到Cube创建的操作被撤销了,当然我们可以按下Ctrl + Y或点击菜单栏中Edit/Redo Create Cube重做创建Cube的操作。

撤销实现的原理实际是维护了一个堆栈,当有新的更改产生之前,将之前状态存起来,等到需要重做时再恢复之前的状态。

实现Undo操作

创建一个Cube的一般代码:

using UnityEditor;
using UnityEngine;

public class Test2 : MonoBehaviour
{
    [MenuItem("Test/Test2/Create Cube")]
    static void CreateCube()
    {
        GameObject.CreatePrimitive(PrimitiveType.Cube);
    }
}

使用上述代码直接进行创建Cube是无法进行撤销的,这里最关键的原因是没有在Undo的堆栈中进行注册

让我们在创建cube之后注册一下:

using UnityEditor;
using UnityEngine;

public class Test2 : MonoBehaviour
{
    [MenuItem("Test/Test2/Create Cube")]
    static void CreateCube()
    {
        var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
        Undo.RegisterCreatedObjectUndo(cube, "Create Cube");
    }
}

这样Undo效果就顺利实现了,我们再来看一个Undo改变Transform属性的例子:

[MenuItem("Test/Test2/Random Rotate")]
static void RandomRotate()
{
    var tr = Selection.activeTransform;
    if (tr != null)
    {
        Undo.RecordObject(tr, "Rotate " + tr.name);
        tr.rotation = Random.rotation;
    }
}

注意:Undo.RecordObject是将之前的属性保存到堆栈中,必须在属性改变之前调用。(注意一下调用顺序,我已经被坑了好几次了)

Profiler中的一些细节

Profiler中的Undo截图:

PropertyDiffUndoRecorder函数执行顺序:

  1. RecordObjec;
  2. 更改值;
  3. 运行Flush,Undo.willFlushUndoRecord在Flush之前执行;
  4. Modification输出,Undo.postprocessModifications在输出之后执行。

我们在使用Undo.RecordObject时,内存中只会记录属性的差别,因此内存占用比较少,而Undo. RegisterCompleteObjectUndo会记录整个对象,在属性数量多的情况下导致内存暴增。

关于以上的内存问题,我这边做了一些测试:

public class Test2SerializedData : ScriptableObject
{
    public Test2SerializedData()
    {
        SetStrValue("");
    }

    public void SetStrValue(string lastRow)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 100; i++)
        {
            for (int j = 0; j < 10; j++)
            {
                sb.Append("a");
            }
            sb.Append("\n");
        }
        sb.Append(lastRow);
        strValue = sb.ToString();
    }

    public string strValue;
    public int intValue;
}

public class Test2 : MonoBehaviour
{
    public Test2SerializedData data = null;

    [Button]
    public void CreateValue()
    {
        data = ScriptableObject.CreateInstance<Test2SerializedData>();
    }

    [Button]
    public void ChangeValue()
    {
        Undo.RecordObject(data, "Test2SerializedData Changed");
        data.intValue++;
        Debug.LogError("change to " + data.intValue);
    }
}

Test2SerializedData是一个需要Undo功能的数据类,它保存了一个巨大的字符串变量strValue和一个intValue。

(其中Button的Attribute是Odin的功能,方便创建按钮,这边就直接使用了)

点击CreateValue,我们在Profiler中就能看到5.8KB的内存占用:

?

然后点击ChangeValue 100次,105.5KB的占用:

?

接下来我分别测试了使用RegisterCompleteObjectUndo替代RecordObject、改变strValue的最后一行内容、strValue从string替换成string[]存储每一行的内容。

结果如下:

RecordObject 105.5KB

RegisterCompleteObjectUndo 222.6KB

RecordObject 改strValue最后一行 322.8KB

RegisterCompleteObjectUndo改strValue最后一行 218.2KB

RecordObject 改strValue最后一行 strValue替换string[]类型 126.9KB

RegisterCompleteObjectUndo改strValue最后一行 strValue替换string[]类型 278.9KB

结论:

  1. 在成员变量都占用内存不大的情况下,使用RecordObject能最有效优化内存;
  2. 在成员变量占用内存巨大的情况下,RecordObject反而内存会暴增;
  3. 使用string[]可以改善内存暴增的情况;
  4. 但string[]如果数量发生变化,会回退到内存会暴增的情况,因此处理占用内存巨大的成员变量需要根据实际情况来进行定夺。

  游戏开发 最新文章
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-05 17:40:59  更:2021-08-05 17:41:54 
 
开发: 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/6 13:57:14-

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