在 Unity 中创建撤消和重做系统

Create undo and redo system in Unity

我想在 Unity3D 中构建简单的游戏,用户可以在场景中放置模型,删除和移动它们。

我已经构建了基本部分,但现在我想添加一个撤销-重做系统。撤消移动很容易(只需保存之前的位置)但我不确定如何 undo/redo 对象的创建或删除。

我目前正在使用 Destroy 方法删除它,它正在按我的意愿工作。但是我怎么能选择撤销它呢?

我考虑过禁用 GameObject 并在撤消时重新激活它,但我不确定这是否对记忆有好处。

我的游戏是针对只有 4GB 内存和弱 i3 处理器的低端 PC。

你知道撤销对象删除的方法吗?

提前致谢

正如评论中提到的那样,Premature optimization is the root of all evil.

首先使用更简单的方法,看看它是否可以按预期正常工作。

如果必须,请测试两者,然后使用 Profiler 确定最适合您的。

此外,还有一些不错的资源供您阅读:

TLDR;如果您专注于优化内存使用,您可能会遇到性能问题。反之亦然。

撤消 Destroy() 调用

要撤消 Destroy() 调用,没有简单的解决方案。
你可以做的就是存储你销毁的是什么对象,以及销毁前对象的信息。

例如,我有一颗子弹,其中有一些属性,我希望它在我撤消它的破坏后仍然存在。

Bullet.cs

public class Bullet : MonoBehaviour {

    public BulletInfo BulletInfo { get; private set; }

    private void Update() {
        // Fly, etc.
    }

    public void InitalizeBullet(BulletInfo bulletInfo) {
        BulletInfo = bulletInfo;
    }

    // ...

    public void DisposeBullet() {
        BulletUndoManager.Instance.AddDeletedBulletInfo(BulletInfo, transform.position);

        Destroy(gameObject);
    }
}

BulletInfo.cs

public class BulletInfo {
    public float Speed { get; set; }
    public float Damage { get; set; }

    // Etc...
}

BulletUndoManager.cs

public class BulletUndoManager : MonoBehaviour {
    #region Singleton
    private static BulletUndoManager instance;

    public static BulletUndoManager Instance {
        get => instance;
    }
    #endregion

    private struct DeletedBulletInfo {
        public BulletInfo BulletInfo { get; private set; }
        public Vector3 LastPosition { get; private set; }
        public DeletedBulletInfo(BulletInfo bulletInfo, Vector3 lastPosition) {
            BulletInfo = bulletInfo;
            LastPosition = lastPosition;
        }
    }

    Stack<DeletedBulletInfo> lastDeletedBullet;

    [SerializeField, Tooltip("The prefab of the bullet")]
    private Bullet bulletPrefab;

    private void Awake() {
        instance = this;
    }

    public void UndoBulletDeletion() {
        if (lastDeletedBullet.Count > 0) {
            DeletedBulletInfo lastDeletedBulletInfo = lastDeletedBullet.Pop();

            var newBullet = Instantiate(bulletPrefab);

            newBullet.InitalizeBullet(lastDeletedBulletInfo.BulletInfo);
            newBullet.transform.position = lastDeletedBulletInfo.LastPosition;
        }
    }

    public void AddDeletedBulletInfo(BulletInfo bulletInfo, Vector3 bulletLastPosition) {
        lastDeletedBullet.Push(new DeletedBulletInfo(bulletInfo, bulletLastPosition));
    }
}



请注意,这个概念与撤消或重做对象的移动大致相同。

唯一的主要区别是:

  • 我存储了位置以外的其他东西。 (主要是销毁前的信息)
  • 我将存储的所有数据传递给新创建的对象。 (在撤消 Destroy() 时创建)

这里唯一真正的困难是如何存储信息,以及如何将其传递到新创建的对象中。