Unity UndoRedo(撤销重做)功能
需求
撤销与重做功能
思考
关于记录的数据的两点思考:
- 记录操作
- 记录影响显示和逻辑的所有数据
很显然这里就要考虑取舍了:
记录操作 这种方案只需要记录每一步的操作,具体这个操作要怎么渲染和实现出来完全需要自己去实现,这种方式的好处是记录的数据量比较小,节省内存,但业务逻辑复杂,适合复杂庞大的数据记录
记录影响显示的所有数据 这种方案要记录所有影响显示和逻辑的所有数据,无疑,这种数据量肯定是比第一种大的,内存占用会比较大,但是业务逻辑简单
这里由于我的数据量不大所以选择了第二种方式来进行处理
首先肯定要有一个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();}}
}
相关文章:
Unity UndoRedo(撤销重做)功能
需求 撤销与重做功能 思考 关于记录的数据的两点思考: 记录操作记录影响显示和逻辑的所有数据 很显然这里就要考虑取舍了: 记录操作 这种方案只需要记录每一步的操作,具体这个操作要怎么渲染和实现出来完全需要自己去实现,这…...
28条有关人工智能的名言
当谈到人工智能(AI)的潜力和潜在风险,以及无人类干预的机器学习和推理过程时,目前尚存在许多不同的观点。 只有时间会告诉我们,这些语录中哪一条是最接近未来的真实情况的。在我们尚未到达目的地之前,想一想…...
搞机器视觉项目看不起搞机器视觉培训的,实际上怎么样
搞机器视觉项目第一要务就是验收回款,往往欠款的非常严重,多数还要打通人际关系需要大量的成本。大多数机器视觉检测项目具有一定的风险,客户要求不明确,技术评估不充分,往往伴随着失败的可能性。所以做项目又累又担风…...
使用Jenkins部署项目
部署中的痛点 为什么要用Jenkins?我说下我以前开发的痛点,在一些中小型企业,每次开发一个项目完成后,需要打包部署,可能没有专门的运维人员,只能开发人员去把项目打成一个exe包,可能这个项目已…...
【机器学习与神经网络荣获诺贝尔奖】科学边界的扩展及技术革新
【机器学习与神经网络荣获诺贝尔奖】科学边界的扩展及技术革新 1)科学交叉融合的体现2)方法论的创新3)社会影响的考量 一、机器学习与神经网络的发展前景1)生产制造领域2)金融领域3)医疗领域 二、机器学习和…...
Javascript扩展符号(...)使用说明
在 ES6 中,扩展运算符(spread operator)... 可以用于在函数调用、数组字面量或对象字面量中展开数组或对象。以下是扩展运算符的一些常见用法: 1. 在函数调用中使用扩展运算符 扩展运算符可以在函数调用时展开数组或对象&#x…...
giugughk
c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话: 知不足而奋进,望远山而前行&am…...
【微服务】网关 - Gateway(下)(day8)
网关过滤工厂 在上一篇文章中,主要是对网关进行了一个总体的介绍,然后对网关中的断言进行了一个描述。在这篇文章中,主要是对网关中的最后一大核心——过滤进行介绍。 当客户端发送过来的请求经过断言之后,如果还想在请求前后添…...
【C#】创建一个控制台应用程序来管理学生成绩
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 在C#中创建一个控制台应用程序来管理学生成绩编写程序程序解释 在C#中创建一个控制台应用程序来管理学生成绩 在这篇文章中,我将向你展示如何使用C#创建…...
鸿蒙开发之ArkUI 界面篇 三十四 容器组件Tabs 自定义TabBar
如果需要修改Tabs的图标和文字之间的距离我们该怎么办呢?好在tabBar是联合类型,提供了自定义tabBar,这里就可以显示特殊图标或者是文字图片,如下图: 这里定义了myBuilder的函数,用了 来修饰,没有…...
AI核身-金融场景凭证篡改检测YOLO原理
引言 YOLO (You Only Look Once) 模型是一种先进的实时目标检测算法,它在计算机视觉领域具有重要的地位。YOLO以其速度和准确性而闻名,能够快速识别图像和视频中的各种物体。这使得它在自动驾驶、安全监控、机器人技术、医学影像分析等众多领域都有着广…...
鹅厂JS面试题——0.1+0.2=0.3吗?
首先公布答案:在JavaScript 中,0.1 0.2 ≠ 0.3 为什么? JavaScript 中的数字使用 IEEE 754 标准的双精度浮点数(64 位)进行表示。这种表示方式在处理十进制小数时,不能精确地表示某些数字。比如0.1 和 0.2 这样的十进…...
软件功能测试重点和流程有哪些?专业软件测评服务公司推荐
软件功能测试就是对产品的各功能进行验证,根据功能测试用例,逐项测试,检查产品是否达到用户要求的功能。功能测试也叫黑盒测试或数据驱动测试,只需考虑需要测试的各个功能,不需要考虑整个软件的内部结构及代码.一般从软…...
【数据结构】AVL树(C++实现)
文章目录 前言AVL树节点的定义AVL树的插入AVL树的旋转AVL树的验证AVL树的删除AVL树的性能与源码 前言 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此&…...
AMD新推EPYC与MI325X,挑战英伟达AI市场地位
在人工智能(AI)加速器领域,AMD近日于美国旧金山举办的“推进人工智能”(Advancing AI Event)活动中,宣布了一系列新产品的发布,直接对标英伟达,意图在AI芯片市场占据更大份额。 AMD新…...
电脑桌面文件不见了怎么恢复?8个方法帮你解决问题
电脑桌面文件突然不见了凭空消失了怎么恢复?电脑桌面文件日常使用电脑时,很多用户喜欢将重要文件、快捷方式存放在桌面上,以方便快速访问。然而,有时我们会突然发现桌面上的文件不见了。桌面文件消失可能有多种原因,例…...
如果想转行AI领域却不知如何开始?可以试试这五步,超详细_ai行业怎么入行
我看了计算机科学家大卫格维茨写的一篇博客,里面介绍了如果想从事AI行业,却不知道如何开始的话,可以走下面五步,从而达到转行的目的。因为这是个国外作家写的,跟我们国内的情况有一些出入,但是大思路是没有…...
个人博客搭建 | Hexo框架
文章目录 1.Hexo安装2.创建博客3.将博客通过GitHub来部署4.更换主题 1.Hexo安装 Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他标记语言)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。搭建Hexo首先要…...
[Gtk] layout.ui
播放器layout: # <?xml version"1.0" encoding"UTF-8"?> <!-- Generated with glade 3.38.2 --> <interface> <requires lib"gtk" version"3.20"/> <object class"GtkWindow"…...
Spring MVC:精通JSON数据返回的几种高效方式
前言 在实际开发中,我们在前后端传送数据通常使用Json格式,而在Spring MVC中返回Json格式的方式有多种,接下来我将介绍其中一些。 准备工作 为了演示Json格式的数据,我们准备一个实体类,例如User,这些可以测…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
