在 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 确定最适合您的。
此外,还有一些不错的资源供您阅读:
- https://www.quora.com/How-does-memory-work-in-Unity-Engine-Does-1-huge-cube-take-up-more-memory-space-or-many-smaller-cubes-totaling-the-same-volume-take-up-more-space-or-are-they-the-same?share=1
- https://answers.unity.com/questions/462942/does-inactive-objects-eat-up-performance.html
- https://answers.unity.com/questions/209101/do-un-active-game-objects-still-use-memory.html
- https://answers.unity.com/questions/783847/how-does-inactive-game-objects-affect-cpu-and-memo.html
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()
时创建)
这里唯一真正的困难是如何存储信息,以及如何将其传递到新创建的对象中。
我想在 Unity3D 中构建简单的游戏,用户可以在场景中放置模型,删除和移动它们。
我已经构建了基本部分,但现在我想添加一个撤销-重做系统。撤消移动很容易(只需保存之前的位置)但我不确定如何 undo/redo 对象的创建或删除。
我目前正在使用 Destroy
方法删除它,它正在按我的意愿工作。但是我怎么能选择撤销它呢?
我考虑过禁用 GameObject 并在撤消时重新激活它,但我不确定这是否对记忆有好处。
我的游戏是针对只有 4GB 内存和弱 i3 处理器的低端 PC。
你知道撤销对象删除的方法吗?
提前致谢
正如评论中提到的那样,Premature optimization is the root of all evil.
。
首先使用更简单的方法,看看它是否可以按预期正常工作。
如果必须,请测试两者,然后使用 Profiler 确定最适合您的。
此外,还有一些不错的资源供您阅读:
- https://www.quora.com/How-does-memory-work-in-Unity-Engine-Does-1-huge-cube-take-up-more-memory-space-or-many-smaller-cubes-totaling-the-same-volume-take-up-more-space-or-are-they-the-same?share=1
- https://answers.unity.com/questions/462942/does-inactive-objects-eat-up-performance.html
- https://answers.unity.com/questions/209101/do-un-active-game-objects-still-use-memory.html
- https://answers.unity.com/questions/783847/how-does-inactive-game-objects-affect-cpu-and-memo.html
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()
时创建)
这里唯一真正的困难是如何存储信息,以及如何将其传递到新创建的对象中。