Unity-撤销操作,返回上一步(拖拽,撤回)

一、基础撤销实现:

参考内容:unity推箱子合成类游戏的回退撤销操作_哔哩哔哩_bilibili

首先创建一个Gamemanger类,用于控制所有的移动物品,挂载到一个空物体上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;
    //List存储所有可移动的物体
    List<GameObject> Moveobjs = new List<GameObject>();
    //栈存放每一步移动的数据
    Stack<List<GameObject>> stack = new Stack<List<GameObject>>();

    //单例模式
    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<GameObject> temp = new List<GameObject>();
        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<GameObject> 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;

/// <summary>
/// 拖拽移动、角度旋转回撤测试
/// </summary>
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;
/// <summary>
/// 声明栈,定义撤销前存储、撤销功能
/// </summary>
public class UndoRedoManager : MonoBehaviour
{
    public static UndoRedoManager Instance { get; private set; }

    private Stack<List<TransformUndoData>> undoStack = new Stack<List<TransformUndoData>>();

    void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    private void Update()
    {
        if(Input.GetKeyUp(KeyCode.Z))
        {
            Undo();
        }
    }
    private void OnDestroy()
    {
        ClearStack();
    }
    // 执行可撤销的操作
    public void RecordUndo(params GameObject[] gameObjects)
    {
        List<TransformUndoData> dataList = new List<TransformUndoData>();
        foreach (GameObject obj in gameObjects)
        {
            dataList.Add(new TransformUndoData(obj));
        }

        undoStack.Push(dataList);
    }

    // 撤销操作
    public void Undo()
    {
        if (undoStack.Count > 0)
        {
            List<TransformUndoData> dataList = undoStack.Pop();
            foreach (TransformUndoData data in dataList)
            {
                data.RestoreOriginal();
            }
        }
    }

    //清空栈
    public void ClearStack()
    {
        undoStack.Clear();
    }
}

脚本挂载到camera或者自定义空物体上,我在里面直接使用了,方法调用,如果需要可以自己再封装。

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值