Unity UndoRedo(撤销重做)功能

news2024/11/25 5:39:23

需求

撤销与重做功能

思考

关于记录的数据的两点思考:

  1. 记录操作
  2. 记录影响显示和逻辑的所有数据

很显然这里就要考虑取舍了:
记录操作 这种方案只需要记录每一步的操作,具体这个操作要怎么渲染和实现出来完全需要自己去实现,这种方式的好处是记录的数据量比较小,节省内存,但业务逻辑复杂,适合复杂庞大的数据记录
记录影响显示的所有数据 这种方案要记录所有影响显示和逻辑的所有数据,无疑,这种数据量肯定是比第一种大的,内存占用会比较大,但是业务逻辑简单

这里由于我的数据量不大所以选择了第二种方式来进行处理

首先肯定要有一个Manager来负责管理数据并实现撤销和重做的分发(我命名为UndoManager),还要有一个数据对象来记录每一步的数据,由于需要记录的信息未知,可能是一个json也可能是一个对象,所以这里用最抽象的接口来表示每一步的数据信息,后续怎么处理这份数据完全根据项目需求自己定制(我命名为IRecordData)

Redo Undo的功能都是先进后出的数据结构,这种数据结构对应到程序世界的话刚好对应的是Stack,这里就先用Stack来进行思考
定义有两个Stack 分别是UndoStack 和 RedoStack 分别来存储待撤销记录和待重做记录,假设这两个Stack的当前状态如下:
在这里插入图片描述
假设这个时候来个一个新的步骤Step4 ,这个时候Step4需要记录,记录的过程如下:

在这里插入图片描述
假设我现在需要撤销一步回到Step3,具体过程如下:
在这里插入图片描述

假设现在我又想重做上一步,想回到Step4,具体过程如下:
在这里插入图片描述
至此我们的记录、撤销、重做的核心逻辑就已经基本梳理清楚了,但是发现Record还有一个小问题,问题如下:
在这里插入图片描述
当我们RedoStack中存在数据时,如果记录新的步骤Step4,成功之后我们会发现,现在Undo会回到Step1,Redo会回到Step2,这样流程就乱了,所以,修改一个Record的逻辑,修改后如下:
在这里插入图片描述
有两种处理方案,方案一为每次记录新数步骤时清空RedoStack,方案二为每次记录新数据时将RedoStack中的数据尽数移动到UndoStack中,可根据需求选择。

由于我们选择的数据记录是记录影响显示和逻辑的所有数据 本身内存占用就比记录操作要大,如果再对这两个Stack的容量不做限制,就可能会浪费很多的内存,导致程序内存占用过高。所以我们需要对UndoStack和RedoStack做容量限制,超过容量时最早进入Stack的数据直接丢弃,但Stack本身是先进后出的数据结构,显然不能支持这种操作,所以权衡利弊之后,我选择将Stack改为LinkedList,具体的操作流程是一样的。

实现

下面直接展示源码:

using System.Collections.Generic;

namespace S
{
    public interface IRecordData
    {
    }

    public class UndoManager
    {
        private LinkedList<IRecordData> undoLinkedList;
        private LinkedList<IRecordData> redoLinkedList;
        private IRecordData currentData;

        /// <summary>
        /// Undo数量
        /// </summary>
        public int undoCount => undoLinkedList == null ? 0 : undoLinkedList.Count;

        /// <summary>
        /// Redo数量
        /// </summary>
        public int redoCount => redoLinkedList == null ? 0 : redoLinkedList.Count;

        /// <summary>
        /// 记录的最大容量  <=0时 代表无限容量
        /// </summary>
        public int maxCount=0;

        public UndoManager()
        {
            undoLinkedList = new LinkedList<IRecordData>();
            redoLinkedList = new LinkedList<IRecordData>();
            currentData = null;
        }

        /// <summary>
        /// 重做一步
        /// </summary>
        /// <returns></returns>
        public IRecordData Redo()
        {
            if (currentData != null)
            {
                undoLinkedList.AddLast(currentData);
            }

            if (redoLinkedList.Count == 0)
            {
                currentData = null;
            }
            else
            {
                currentData = redoLinkedList.Last.Value;
                redoLinkedList.RemoveLast();
            }

            return currentData;
        }

        /// <summary>
        /// 撤销一步
        /// </summary>
        /// <returns></returns>
        public IRecordData Undo()
        {
            if (currentData != null)
            {
                redoLinkedList.AddLast(currentData);
            }

            if (undoLinkedList.Count == 0)
            {
                currentData = null;
            }
            else
            {
                currentData = undoLinkedList.Last.Value;
                undoLinkedList.RemoveLast();
            }

            return currentData;
        }

        /// <summary>
        /// 记录
        /// </summary>
        /// <param name="recordData">记录对象</param>
        /// <param name="overrideRedo">是否覆盖Redo链表</param>
        public void Record(IRecordData recordData,bool overrideRedo=true)
        {
            if (currentData != null)
            {
                undoLinkedList.AddLast(currentData);
            }

            if (overrideRedo)
            {
                redoLinkedList.Clear();
            }
            else
            {
                while (redoLinkedList.Last != null)
                {
                    IRecordData value = redoLinkedList.Last.Value;
                    redoLinkedList.RemoveLast();
                    undoLinkedList.AddLast(value);
                }
            }

            currentData = recordData;

            if (maxCount>0&&undoCount > maxCount) //容量维护
            {
                undoLinkedList.RemoveFirst();
            }
        }

        /// <summary>
        /// 清空undo&redo
        /// </summary>
        public void Clear()
        {
            undoLinkedList.Clear();
            redoLinkedList.Clear();
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2205353.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎么下载安装yarn

安装 npm install --global yarn 是否安装成功 yarn -v Yarn 淘宝源安装&#xff0c;分别复制粘贴以下代码行到黑窗口运行即可 yarn config set registry https://registry.npm.taobao.org -g yarn config set sass_binary_site http://cdn.npm.taobao.org/di…

免杀对抗—python分离免杀无文件落地图片隐写SOCK管道

前言 之前就基本把所有语言都讲了一遍了&#xff0c;C/C&#xff0c;Java&#xff0c;python&#xff0c;golang&#xff0c;汇编。今天就开始讲免杀的技巧以及手法&#xff0c;分离免杀之前讲过一点&#xff0c;就是通过http或者参数获取shellcode&#xff0c;今天把其他的分…

ppt压缩文件怎么压缩?压缩PPT文件的多种压缩方法

ppt压缩文件怎么压缩&#xff1f;当文件体积过大时&#xff0c;分享和传输就会变得困难。许多电子邮件服务对附件的大小有限制&#xff0c;而在网络环境不佳时&#xff0c;上传和下载大文件可能耗时较长。此外&#xff0c;在不同设备上播放时&#xff0c;较大的PPT文件还可能导…

Chromium HTML attribute与c++接口对应关系分析

<a href"https://www.w3school.com.cn" target"_blank">访问 W3School</a>前端这些属性定义在html_attribute_names.json5文件中&#xff1a; third_party\blink\renderer\core\html\html_attribute_names.json5 html_attribute_names.json5…

【前端碎片记录】大文件分片上传

大文件分片上传&#xff0c;主要是为了提高上传效率&#xff0c;避免网络问题或者其他原因导致整个上传失败。 HTML部分没什么特殊代码&#xff0c;这里只写js代码。用原生js实现&#xff0c;框架中可参考实现 // 获取上传文件的 input框 const ipt document.querySelector(…

Richtek立锜科技线性稳压器 (LDO) 选型

一、什么是LDO? LDO也可称为低压差线性稳压器&#xff0c;适合从较高的输入电压转换成较低输出电压的应用&#xff0c;这种应用的功率消耗通常不是很大&#xff0c;尤其适用于要求低杂讯、低电流和输入、输出电压差很小的应用环境。 二、LDO的特性 LDO透过控制线性区调整管…

【每日一坑】pcb出的光绘文件导入到cam350有两个警告

pcb出的光绘文件导入到cam350有两个警告&#xff1a; 1 Warning - Zero radius arc detected. Assuming linear interpolation. 2 Warning - Apertures are used which have a size of 0. 这个 应该检查到处光绘文件时候&#xff0c;默认的线宽是否为0&#xff1b; 通过负片…

面试八股文对校招的用处有多大?C/C++语言篇

前言 1.本系列面试八股文的题目及答案均来自于网络平台的内容整理&#xff0c;对其进行了归类整理&#xff0c;在格式和内容上或许会存在一定错误&#xff0c;大家自行理解。内容涵盖部分若有侵权部分&#xff0c;请后台联系&#xff0c;及时删除。 2.本系列发布内容分为12篇…

单通道 LVDS 差分线路接收器MS21112S

MS21112S 是一款单通道低压差分信号 (LVDS) 线 路接收器。在输入共模电压范围内&#xff0c;差分接收器可以 将 100mV 的差分输入电压转换成有效的逻辑输出。 该芯片可应用于 100Ω 的受控阻抗介质上&#xff0c;进行点对 点基带数据传输。传输介质可以是印刷电路板、…

图像处理(二)——MDPI特刊推荐

特刊征稿 01 期刊名称&#xff1a; Computer Vision and Image Processing, 2nd Edition 截止时间&#xff1a; 投稿截止日期&#xff1a;2024年12月31日 目标及范围&#xff1a; 感兴趣的主题包括但不限于&#xff1a; 用于图像分类和识别的深度学习 对象检测和跟…

EdgeNAT: 高效边缘检测的 Transformer

EdgeNAT: Transformer for Efficient Edge Detection 介绍了一种名为EdgeNAT的基于Transformer的边缘检测方法。 1. 背景与动机 EdgeNAT预测结果示例。(a, b):来自BSDS500的数据集的输入图像。(c, d):对应的真实标签。(e, f):由EdgeNAT检测到的边缘。(e)显示了由于颜色变化…

QT元对象系统特性详细介绍(信号槽、类型信息、动态设置属性)(注释)

目 录 一、元对象系统简介 二、信号和槽 三、类型信息 四、动态设置属性 一、元对象系统简介 QT中的元对象系统Q_OBJECT并不是C标准代码&#xff0c;因此在使用时需要QT的MOC&#xff08;元对象编译器&#xff09;进行预处理&#xff0c;MOC会在编译时期读取C代码中的特定…

暗语源码 复现佛禅翻译系统v2升级版源码

与佛论禅翻译系统 一个翻译佛论的娱乐系统&#xff0c;类似于核心价值观加密 此为升级版&#xff0c;每次加密得到的结果不一样&#xff0c;配合箴言功能&#xff0c;更加安全 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89874751 更多资源下载&a…

现代易货交易:重塑价值,引领未来交易新风尚

在当今经济蓬勃发展的背景下&#xff0c;一种新颖的交易模式——现代易货交易&#xff0c;正逐渐崭露头角并赢得市场的认可。这一模式不仅对传统物品交换方式进行了革新&#xff0c;更在物品价值的评估与交换手段上展现出创新性。那么&#xff0c;现代易货交易究竟是何方神圣&a…

基于SSM的旅游网站【附源码】

基于SSM的旅游网站&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概要设计 4.2 系统功能结构设计 4.3 数据库设计 4.3.1 数据库E-R图设计 4.3.2 数据库表结构设计 5 系统实现 5.1 管理员功能介绍 5.1.1 用户管理 5.1.2 …

比较模拟数据

模拟数据检查器可以比较来自工作区、文件或模拟中的运行和单个信号的数据和元数据。可以使用公差来分析比较结果&#xff0c;并可以通过指定信号属性和比较约束来配置比较行为。此示例使用从模型slexAircraftExample的模拟中记录的数据&#xff0c;演示了以下内容&#xff1a; …

云栖实录 | MaxCompute 迈向下一代的智能云数仓

本文根据2024云栖大会实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a; 张治国 | 阿里云智能集团研究员、阿里云 MaxCompute 负责人 谢德军&#xff5c;阿里云智能集团资深技术专家 于得水&#xff5c;阿里云智能集团资深技术专家 谌鹏飞&#xff5c…

SpringMVC源码-@ControllerAdvice和 @InitBinder注解源码讲解

1.ControllerAdvice修饰的类何时被加载扫描 被ControllerAdvice修饰的类是作用于全局的 initStrategies 初始化springmvc的9大组件 initStrategies:531, DispatcherServlet (org.springframework.web.servlet) onRefresh:514, DispatcherServlet (org.springframework.web.se…

在线答题系统怎么做?一文为您揭秘!

在线答题系统是一种利用网络技术实现的答题平台&#xff0c;具有高效、便捷、灵活等特点&#xff0c;被广泛应用于教育、培训、考试、竞赛等场景。以下是其详细介绍&#xff1a; 一、 基本功能&#xff1a; 题目录入&#xff1a;支持多种方式录入题目&#xff0c;如手动输入、…

Android15之解决:Dex checksum does not match for dex:services.jar问题(二百三十五)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…