【问题标题】:How to setup undo/redo system during runtime with Unity3D如何在运行时使用 Unity3D 设置撤消/重做系统
【发布时间】:2020-02-13 14:55:15
【问题描述】:

我正在尝试在运行时设置撤消/重做系统,但无法让我的代码正常工作。我创建了一个撤消按钮,但是当我移动我的游戏对象并按下撤消按钮时,游戏对象并没有回到它的原始状态。

我只是想让最终用户在运行时而不是在编辑器模式期间撤消/重做他的最新操作。我进行了几次尝试并尝试了许多不同的脚本,但没有取得任何进展。有什么建议么?

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

public class undoit : MonoBehaviour
{
    public class setting
    {
        public GameObject Obj;
        public Vector3 Pos;
        public Quaternion Rot;
        public bool Deleted;

        public void Restore()
        {
            Obj.transform.position = Pos;
            Obj.transform.rotation = Rot;
            Obj.SetActive(Deleted);
        }
        public setting(GameObject g)
        {
            Obj = g;
            Pos = g.transform.position;
            Rot = g.transform.rotation;
            Deleted = g.activeSelf;
        }
    }
    public List<setting> UndoList;

    public void AddUndo(GameObject g)
    {
        setting s = new setting(g);
        UndoList.Add(s);
    }
    public void Undo()
    {
        if (UndoList.Count > 0)
        {
            UndoList[UndoList.Count - 1].Restore();
            UndoList.RemoveAt(UndoList.Count - 1);
        }
    }
    void Start()
    {
        UndoList = new List<setting>();
    }
}

【问题讨论】:

  • 你确定你每一步都记住了所有改变的东西吗?
  • 我现在只想改一步,看看代码能不能用。
  • 好吧,1 个或多个,如果您不记得更改之前的所有状态,以及要放回什么... 1 个或多个无关紧要
  • 我假设我的代码太简单而无法使用撤消...
  • 我对此表示怀疑,无论它多么复杂或简单,您都需要记住所有之前的内容,然后返回它.. 仅存储某些信息,您并不清楚您更改了什么,可能还有更多……你也没有解释它是如何不起作用的

标签: c# unity3d system undo-redo


【解决方案1】:

首先,您需要一些可以存储相关数据的东西,例如

public struct ObjectState
{
    // The transform this data belongs to
    private Transform transform;

    private Vector3 localPosition;
    private Quaternion localRotation;
    private Vector3 localScale;

    private bool active;

    public ObjectState (GameObject obj)
    {
        transform = obj.transform;
        localPosition = transform.localPosition;
        localRotation = transform.localRotation;
        localScale = transform.localScale;

        active = obj.activeSelf;
    }

    public void Apply()
    {
        transform.localPosition = localPosition;
        transform.localRotation = localRotation;
        transform.localScale = localScale;

        transform.gameObject.SetActive(active);
    }
}

接下来我将使用包装类来存储可撤消的更改,例如

public struct UndoableChange
{
    private ObjectState _before;
    private ObjectState _after;

    public UndoableChange (ObjectState before, ObjectState after)
    {
        _before = before;
        _after = after;
    }

    public void Undo()
    {
        _before.Apply();
    }   

    public void Redo()
    {
        _after.Apply();
    }     
}

或者,如果您需要它用于我的对象,您可以使用列表,例如像

public struct UndoableChange
{
    private List<ObjectState> _before;
    private List<ObjectState> _after;

    public UndoableChange (List<ObjectState> before, List<ObjectState> after)
    {
        _before = before;
        _after = after;
    }

    public void Undo()
    {
        foreach(var state in _before)
        {
            state.Apply();
        }
    }   

    public void Redo()
    {
        foreach(var state in _after)
        {
            state.Apply();
        }
    }     
}

然后对于控制器,我将使用两个StackStack 是“后进先出”,这正是您所需要的。使用Push 添加新条目,使用Pop 检索最后添加的元素,同时将其从堆栈中删除。所以它看起来像

public static class UndoRedo
{
    private static Stack<UndoableChange> undoStack = new Stack<UndoableChange>();
    private static Stack<UndoableChange> redoStack = new Stack<UndoableChange>();

    public static void Undo()
    {
        if(undoStack.Count == 0) return;

        var lastAction = undoStack.Pop();

        lastAction.Undo();

        redoStack.Push(lastAction);
    }

    public static void Redo()
    {
        if(redoStack.Count == 0) return;

        var lastAction = redoStack.Pop();

        lastAction.Redo();

        undoStack.Push(lastAction);
    }

    public static void AddAction(UndoableChange action)
    {
        redoStack.Clear();

        undoStack.Push(action);
    }
}

我不知道你的用户是如何与对象交互的,但你现在总是会做类似的事情

// at some point store the initial values
var before = new ObjectState (selectedGameObject);

//TODO Apply all your changes to the selected object 

// get the new values
var after = new ObjectState (selectedGameObject);

// Log to the history
UndoRedo.AddAction(new UndoableChange(before, after));

或相应地使用所有选定对象的列表,例如只需使用Linq

using System.Linq;

...

var before = selectedGameObjects.Select(obj => new ObjectState(obj)).ToList();

//TODO Apply all your changes to all the selected objects 

var after = selectedGameObjects.Select(obj => new ObjectState(obj)).ToList();

UndoRedo.AddAction(new UndoableChange(before, after));

注意:在智能手机上输入,但我希望思路清晰

【讨论】:

  • 哇!谢谢您的答复。我非常感谢您的解释和编码。我会看看我是否可以在我的脚本中实现你的编码。
  • 我在代码中有一些错误。一个是用于 var lastAction.*Undo*();。另一个是 public *UndoableChange*(ObjectState before, ObjectState after)。最后是 UndoRedo.AddAction(new UndoableChange(before, after));.
  • 刚刚更新了答案.. pubic class ObjectState 缺少 class 并且我的字段必须以 _ 开头的方法 ;) 正如我所说的我只在 pH 值上有时我有一些错别字:'D
  • 由于某种原因,我仍然收到 UndoRedo.AddAction(new UndoableChange(before, after)); 错误
  • 它说什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-06
  • 2012-06-29
  • 2021-05-03
  • 1970-01-01
  • 1970-01-01
  • 2011-04-01
相关资源
最近更新 更多