如何使用 Unity3D 在运行时设置 undo/redo 系统
How to setup undo/redo system during runtime with Unity3D
我试图在运行时设置一个 undo/redo 系统,但我的代码无法正常工作。我创建了一个撤消按钮,但是当我移动游戏对象并按下撤消按钮时,游戏对象不会返回到其原始状态。
我只是想让最终用户撤消/重做他最近的操作在运行时并且不在编辑器模式 .我做了几次尝试,尝试了许多不同的脚本,但没有取得任何进展。有什么建议么?
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>();
}
}
首先你需要一些可以存储相关数据的东西,例如
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);
}
}
接下来我将使用包装器 class 来存储可撤消的更改,例如
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();
}
}
}
然后对于控制器,我将使用两个 Stack
。 Stack
是 "last in - first out",这正是您所需要的。您使用 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));
注意:在智能手机上打字,但我希望思路清晰
我试图在运行时设置一个 undo/redo 系统,但我的代码无法正常工作。我创建了一个撤消按钮,但是当我移动游戏对象并按下撤消按钮时,游戏对象不会返回到其原始状态。
我只是想让最终用户撤消/重做他最近的操作在运行时并且不在编辑器模式 .我做了几次尝试,尝试了许多不同的脚本,但没有取得任何进展。有什么建议么?
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>();
}
}
首先你需要一些可以存储相关数据的东西,例如
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);
}
}
接下来我将使用包装器 class 来存储可撤消的更改,例如
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();
}
}
}
然后对于控制器,我将使用两个 Stack
。 Stack
是 "last in - first out",这正是您所需要的。您使用 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));
注意:在智能手机上打字,但我希望思路清晰