Unity开发数字孪生编辑器操作与Undo设计

Undo的程序设计基于以下想法:

一、所有对象从根本上来讲都是以由基本的数据类型组成的,包括int、float、bool、string等等。

二、undo从根本上是将上述基本类型的值设置回操作之前的值。

三、每次一操作都是对某些特定对象的属性修改,undo的作用是将这些属性设置为修改之前的值。

四、对象的每个属性都通过事件驱动具体的内容。

下面是一个UndoManager类,该类里面包含了一个undo操作List,而List的每一项都是一个UnityAction堆栈。

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

public class UndoManager : MonoBehaviour
{
	static public UndoManager instance;

	void Awake()
	{
		instance = this;
	}

	void Update()
	{
		if(Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
		{
			if (Input.GetKeyDown(KeyCode.Z))
			{
				Undo();
			}
		}

		if(Input.GetMouseButtonUp(2)) Undo();
	}

	int undoLimit = 5;
	public void SetUndoLimit(int undoLimit) { this.undoLimit = undoLimit; }

	List> listUndoOperation = new List>();
	Stack curUndoOperation = null;
	public void FinishCurrentOperation()
	{
		if (curUndoOperation == null) return;
		listUndoOperation.Add(curUndoOperation);
		while (listUndoOperation.Count > undoLimit)
		{
			listUndoOperation.RemoveAt(0);
		}
		curUndoOperation = null;
	}

	public void AddUndoAct(UnityAction act)
	{
		if (curUndoOperation == null)
		{
			curUndoOperation = new Stack();
		}
		curUndoOperation.Push(act);
	}

	UnityAction onUndoEmpty;
	public void AddActUndoEmpty(UnityAction act) { onUndoEmpty -= act; onUndoEmpty += act; }
	public void RemoveActUndoEmpty(UnityAction act) { onUndoEmpty -= act; }
	public void Undo()
	{
		if (listUndoOperation.Count > 0)
		{
			int index = listUndoOperation.Count - 1;
			Stack op = listUndoOperation[index];
			if (op != null)
			{
				while(op.Count > 0)
				{
					op.Pop()?.Invoke();
				}
			}
			listUndoOperation.RemoveAt(index);
		}
		else
		{
			onUndoEmpty?.Invoke();
		}
	}
}

一个Vector3驱动对象,这个对象用于驱动类似Tranform的position之类的内容。为了编程方面,里面添加了一些隐式转换,加减法操作等内容。

using UnityEngine;
using UnityEngine.Events;

public class ActVector3
{
	public ActVector3(Vector3 val, UnityAction onChangeVal)
	{
		v = val;
		this.onChangeVal = onChangeVal;
	}

	UnityAction onChangeVal;
	public void AddActChangeVal(UnityAction act) { onChangeVal -= act; onChangeVal += act; }
	public void RemoveActChangeVal(UnityAction act) { onChangeVal -= act; }

	bool addUndoEnable = true;

	Vector3 v;
	public Vector3 val
	{
		get
		{
			return v;
		}
		set
		{
			Vector3 vPre = v;
			v = value;
			if (v != vPre) onChangeVal(v);
			//
			if (addUndoEnable)
			{
				UndoManager.instance.AddUndoAct(() =>
				{
					addUndoEnable = false;
					val = vPre;
					addUndoEnable = true;
				});
			}
		}
	}

	public static implicit operator Vector3(ActVector3 actVector3)
	{
		return actVector3.val;
	}

	public static implicit operator ActVector3(Vector3 vector3)
	{
		return new ActVector3(vector3, null);
	}

	public static ActVector3 operator +(ActVector3 actVector3, Vector3 vector3)
	{
		actVector3.val += vector3;
		return actVector3;
	}

	public static ActVector3 operator +(Vector3 vector3, ActVector3 actVector3)
	{
		return actVector3 + vector3;
	}

	public static ActVector3 operator -(ActVector3 actVector3, Vector3 vector3)
	{
		actVector3.val -= vector3;
		return actVector3;
	}

	public static ActVector3 operator -(Vector3 vector3, ActVector3 actVector3)
	{
		return vector3 - actVector3.val;
	}
}

为了验证上述内容的可行性,这是一个使用案例。

using System.Collections;
using UnityEngine;

public class OpTest : MonoBehaviour
{
	[SerializeField]
	Transform tran;

	ActVector3 actPosition;
	void Start()
	{
		if (tran)
		{
			actPosition = new ActVector3(transform.position, (val) => { tran.position = val; });
			StartCoroutine(ChangePos());
		}
	}

	IEnumerator ChangePos()
	{
		yield return new WaitForSeconds(1);
		actPosition += Vector3.left;
		actPosition += Vector3.left;
		actPosition += Vector3.left;
		UndoManager.instance.FinishCurrentOperation();
		yield return new WaitForSeconds(1);
		actPosition += Vector3.up;
		actPosition += Vector3.up;
		actPosition += Vector3.up;
		actPosition += Vector3.up;
		UndoManager.instance.FinishCurrentOperation();
		yield return new WaitForSeconds(1);
		actPosition += Vector3.forward;
		actPosition += Vector3.forward;
		UndoManager.instance.FinishCurrentOperation();
		yield return new WaitForSeconds(1);
		actPosition += Vector3.right;
		actPosition += Vector3.right;
		actPosition += Vector3.right;
		UndoManager.instance.FinishCurrentOperation();
		yield return new WaitForSeconds(1);
		actPosition += Vector3.down;
		actPosition += Vector3.down;
		actPosition += Vector3.down;
		actPosition += Vector3.down;
		UndoManager.instance.FinishCurrentOperation();
		yield return new WaitForSeconds(1);
		actPosition += Vector3.back;
		actPosition += Vector3.back;
		UndoManager.instance.FinishCurrentOperation();
	}
}

基于上述脚本对物体状态进行改变之后,可以按下鼠标中键或者Ctrl+Z来执行Undo。

你可能感兴趣的:(Unity,C#,编辑器)