当前位置: 首页 > news >正文

Unity UndoRedo(撤销重做)功能

需求

撤销与重做功能

思考

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

  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.lryc.cn/news/460327.html

相关文章:

  • 28条有关人工智能的名言
  • 搞机器视觉项目看不起搞机器视觉培训的,实际上怎么样
  • 使用Jenkins部署项目
  • 【机器学习与神经网络荣获诺贝尔奖】科学边界的扩展及技术革新
  • Javascript扩展符号(...)使用说明
  • giugughk
  • 【微服务】网关 - Gateway(下)(day8)
  • 【C#】创建一个控制台应用程序来管理学生成绩
  • 鸿蒙开发之ArkUI 界面篇 三十四 容器组件Tabs 自定义TabBar
  • AI核身-金融场景凭证篡改检测YOLO原理
  • 鹅厂JS面试题——0.1+0.2=0.3吗?
  • 软件功能测试重点和流程有哪些?专业软件测评服务公司推荐
  • 【数据结构】AVL树(C++实现)
  • AMD新推EPYC与MI325X,挑战英伟达AI市场地位
  • 电脑桌面文件不见了怎么恢复?8个方法帮你解决问题
  • 如果想转行AI领域却不知如何开始?可以试试这五步,超详细_ai行业怎么入行
  • 个人博客搭建 | Hexo框架
  • [Gtk] layout.ui
  • Spring MVC:精通JSON数据返回的几种高效方式
  • [LeetCode 题3] 没有重复字符的最长的子字符串
  • YoloDotNet 在工业检测中的应用详解
  • DataFrame增删改数据
  • 一站式解决App下载量统计,Xinstall引领新潮流
  • ijkMediaPlayer+ TextureView 等比全屏播放视频(避免拉伸)
  • 【RS】GEE(Python):数据处理
  • 非线性磁链观测器推导
  • 什么时机用mysql,什么时机用redis,什么时机用本地内存
  • Redis八股
  • vue3--通用 popover 气泡卡片组件实现
  • Bluetooth Channel Sounding中关于CS Step及Phase Based Ranging相应Mode介绍