一、基础撤销实现:
参考内容:unity推箱子合成类游戏的回退撤销操作_哔哩哔哩_bilibili
首先创建一个Gamemanger类,用于控制所有的移动物品,挂载到一个空物体上
using System.Collections;using System.Collections.Generic;using UnityEngine;public class GameManager : MonoBehaviour{ public static GameManager Instance; //List存储所有可移动的物体 List Moveobjs = new List(); //栈存放每一步移动的数据 Stack stack = new Stack(); //单例模式 private void Awake() { if (Instance) { Destroy(gameObject); } else { Instance = this; } } private void Update() { //测试返回上一步 if(Input.GetKeyUp(KeyCode.O)) { UnDo(); } } #region Undo //将移动物体添加到List public void Rigist(GameObject obj) { Moveobjs.Add(obj); } //删除物体 public void Remove(GameObject obj) { Moveobjs.Remove(obj); } //保存这一步数据状态 public void Save() { List temp = new List(); foreach (GameObject item in Moveobjs) { //实例化这一步的状态备用 GameObject tp = Instantiate(item.gameObject,item.transform.position,Quaternion.identity); tp.SetActive(false); //加入临时List temp.Add(tp); } //将liist压入栈内 stack.Push(temp); } //撤销操作,返回上一步 public void UnDo() { //判断栈是否是空的 if (stack.Count == 0) return; //删除当前物体 foreach(var item in Moveobjs) { Destroy(item); } //取出栈里的最新一步存储,启用 List temp = stack.Pop(); foreach(var item in temp) { item.SetActive(true); } } #endregion}再创建一个UnDo脚本,挂载到每一个需要移动的物体上,脚本内容为:
void Start(){ //开始时将可移动物体加入list GameManager.Instance.Rigist(gameObject);}private void OnDestroy(){ //移除list中的物体 GameManager.Instance.Remove(gameObject);}在每次对物体移动等操作之前,先调用Gamemanager里面的存储方法
GameManager.Instance.Save();保存之后再操作,想要撤销时,调用Gamemanager里面的UnDo方法即可。
二、优化版本:
由于基础功能版本不断地实例化和销毁物体,性能消耗过大,所以优化后的情况如下
首先新建拖拽脚本 Drag
using System.Collections;using System.Collections.Generic;using UnityEngine;/// /// 拖拽移动、角度旋转回撤测试/// public class Drag : MonoBehaviour{ //拖拽测试 private IEnumerator OnMouseDown() { //将三维物体坐标转换成屏幕坐标 Vector3 screenPosition = Camera.main.WorldToScreenPoint(transform.position); //鼠标屏幕坐标 Vector3 currentScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z); //将鼠标屏幕坐标转换成三维坐标 Vector3 mouseWorldPosition = Camera.main.ScreenToWorldPoint(currentScreenSpace); //计算物体位置与鼠标之间的距离 Vector3 offset = transform.position - mouseWorldPosition; //提前定义好返回值 var cs = new WaitForFixedUpdate(); //当按下鼠标左键时 if (Input.GetMouseButtonDown(0)) { UndoRedoManager.Instance.RecordUndo(gameObject); } while (Input.GetMouseButton(0)) { //更新鼠标屏幕坐标 currentScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z); //将鼠标屏幕坐标转换成三维坐标 mouseWorldPosition = Camera.main.ScreenToWorldPoint(currentScreenSpace); //移动物体坐标 Vector3 currentPosition = mouseWorldPosition + offset; //将物体坐标设置成移动后的坐标 transform.position = currentPosition; //返回 (只有当下一次fixedUpdate开始时再执行后续代码) yield return cs; } } //角度旋转测试 private void Update() { if(Input.GetKeyDown(KeyCode.P)) { UndoRedoManager.Instance.RecordUndo(gameObject); transform.RotateAround(transform.position,transform.right,30); } }}将 Drag 脚本放置在需要拖拽的物体身上,创建脚本 TransformUndoData
using UnityEngine;using System.Collections.Generic;// 用于存储物体变换信息的类public class TransformUndoData{ public GameObject GameObject { get; private set; } public Vector3 OriginalPosition { get; private set; } public Quaternion OriginalRotation { get; private set; } public Vector3 OriginalScale { get; private set; } public TransformUndoData(GameObject gameObject) { GameObject = gameObject; OriginalPosition = gameObject.transform.position; OriginalRotation = gameObject.transform.rotation; OriginalScale = gameObject.transform.localScale; } // 恢复物体的原始变换 public void RestoreOriginal() { GameObject.transform.position = OriginalPosition; GameObject.transform.rotation = OriginalRotation; GameObject.transform.localScale = OriginalScale; }}然后再创建一个脚本 UndoRedoManager
using System.Collections;using System.Collections.Generic;using UnityEngine;/// ///