简单演示Undo
Undo的原理 LIFO
简单实现
创建物体Undo.RegisterCreatedObjectUndo
[MenuItem("Example/Create Cube")]
static void CreateCube()
{
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Undo.RegisterCreatedObjectUndo(cube, "Create Cube");
}
属性变化RecordObject/RegisterCompleteObjectUndo
[MenuItem("Example/Random Rotate")]
static void RandomRotate ()
{
var transform = Selection.activeTransform;
if (transform) {
Undo.RecordObject (transform, "Rotate " + transform.name);
transform.rotation = Random.rotation;
}
}
Unity又是怎么处理的呢?PropertyDiffUndoRecorder
执行顺序
- RecordObject
- 处理属性
- Flush
- 输出差异
差异的代码查看
[MenuItem("Example/Random Rotate")]
static void RandomRotate()
{
var transform = Selection.activeTransform;
if (transform)
{
//Undo.RecordObject(transform,"Rotate " + transform.name);
//Undo.RegisterCompleteObjectUndo(transform, "Rotate " + transform.name);
Undo.willFlushUndoRecord += () => Debug.Log("flush");
Undo.postprocessModifications += (modifications) =>
{
Debug.Log("modifications");
return modifications;
};
Undo.RecordObject(transform, "Rotate" + transform.name);
Debug.Log("record");
transform.rotation = Random.rotation;
Debug.Log("changed");
}
}
查看Debug
Redo的实现
Undo处理的对象
Undo定位的是一个可序列化的对象,继承自UnityEngine.Object
Undo支持的对象
GameObject
Component (MonoBehaviour)
ScriptableObject
System.Serializable序列化后可以被Undo
// 序列化
[System.Serializable]
public class PlayerInfo
{
public string name;
public int hp;
}
public class PlayerRedo : MonoBehaviour
{
[SerializeField]
public PlayerInfo info;
}
// 调用
public class ExampleRedo {
[MenuItem("Example/Change PlayerInfo")]
static void ChangePlayerInfo()
{
var player = Selection.activeGameObject.GetComponent<PlayerRedo>();
if (player)
{
Undo.RecordObject(player, "Change PlayerInfo");
player.info = new PlayerInfo
{
name = "New PlayerName",
hp = Random.Range(0, 10)
};
}
}
}
Undo处理的类型
- 对象属性
- 对象本身的操作
属性撤销[可以实现绝大部分功能]
[MenuItem("Undo/RecordObject测试")]
static void RecordObject()
{
Transform transform = Selection.activeTransform;
Undo.RecordObject(transform, "position 指定变更为 Vector3(0,0,0)");
transform.position = new Vector3(0,0,0);
}
一些自带的Undo
[MenuItem("Undo/AddComponent")]
static void AddComponent()
{
GameObject go = Selection.activeGameObject;
Rigidbody rigidbody = Undo.AddComponent<Rigidbody>(go);
}
[MenuItem("Undo/RegisterCreatedObjectUndo")]
static void RegisterCreatedObjectUndo()
{
GameObject go = new GameObject();
Undo.RegisterCreatedObjectUndo(go, "GameObject 生成");
// 组的概念
Undo.IncrementCurrentGroup();
ScriptableRedo scriptableRedo = ScriptableObject.CreateInstance<ScriptableRedo>();
Undo.RegisterCreatedObjectUndo(scriptableRedo, "ScriptableObject 生成");
//EditorApplication.update += () => Debug.Log(scriptableRedo);
}
[MenuItem("Undo/DestoryObjectImmediate")]
static void DestoryObjectImmediate()
{
GameObject go = Selection.activeGameObject;
Undo.DestroyObjectImmediate(go);
}
[MenuItem("Undo/SetTransformParent")]
static void SetTransformParent()
{
Transform root = GameObject.Find("Main Camera").transform;
Transform transform = Selection.activeTransform;
transform.SetParent(root);
//Undo.SetTransformParent(transform, root, "摄像机子物体");
}
Revert 表示不想要恢复
Undo.RevertAllInCurrentGroup Revert当前组
[MenuItem("Undo/RevertAllInCurrentGroup")]
static void RevertAllInCurrentGroup()
{
GameObject ticket = new GameObject("Ticket");
Undo.RegisterCreatedObjectUndo(ticket,"Ticket");
int number = ticket.GetInstanceID();
int winningNumber = 1234;
if (number != winningNumber)
{
Undo.RevertAllInCurrentGroup();
}
}
Undo.RevertAllDownToGroup Revert到指定的组索引
//RevertAllDownToGroup
public class ExampleRevertUndoWindow : EditorWindow
{
[MenuItem("Undo/Window/RevertUndoWindow")]
static void Open()
{
GetWindow<ExampleRevertUndoWindow>();
}
private GameObject go;
private int group1 = 0;
private int group2 = 0;
private int group3 = 0;
void OnEnable()
{
go = GameObject.Find("New Game Object");
}
void OnGUI()
{
if (Event.current.type == EventType.MouseDown)
{
group1 = Undo.GetCurrentGroup();
Undo.AddComponent<Rigidbody>(go);
Undo.IncrementCurrentGroup();
group2 = Undo.GetCurrentGroup();
Undo.AddComponent<BoxCollider>(go);
Undo.IncrementCurrentGroup();
group3 = Undo.GetCurrentGroup();
Undo.AddComponent<ConstantForce>(go);
}
if (Event.current.type == EventType.MouseUp)
{
Undo.RevertAllDownToGroup(group2);
EditorGUIUtility.ExitGUI();
}
}
}
组的概念
一般写到一段处理代码里面的操作,被划为一组
Undo.IncrementCurrentGroup 单独执行每个撤消
[MenuItem("Undo/RegisterCreatedObjectUndo")]
static void RegisterCreatedObjectUndo()
{
GameObject go = new GameObject();
Undo.RegisterCreatedObjectUndo(go, "GameObject 生成");
// 组的概念
Undo.IncrementCurrentGroup();
ScriptableRedo scriptableRedo = ScriptableObject.CreateInstance<ScriptableRedo>();
Undo.RegisterCreatedObjectUndo(scriptableRedo, "ScriptableObject 生成");
//EditorApplication.update += () => Debug.Log(scriptableRedo);
}
CollapseUndoOperations 组合多个Undo
//CollapseUndo
public class ExampleCollapseUndo : EditorWindow
{
private int groupID = 0;
[MenuItem("Undo/Window/CollapseUndoWindow")]
static void Open()
{
GetWindow<ExampleCollapseUndo>();
}
void OnEnbable()
{
groupID = Undo.GetCurrentGroup();
}
void OnDisable()
{
Undo.CollapseUndoOperations(groupID);
}
void OnGUI()
{
if (GUILayout.Button("cube"))
{
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Undo.RegisterCreatedObjectUndo(cube, "Create cube");
}
if (GUILayout.Button("plane"))
{
var plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
Undo.RegisterCreatedObjectUndo(plane, "Create plane");
}
if (GUILayout.Button("Cylinder"))
{
var cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
Undo.RegisterCreatedObjectUndo(cylinder, "Create cylinder");
}
}
}