撤消/重做功能Java的对象序列化

时间:2010-03-11 23:58:08

标签: java serialization stack undo

我一直在尝试在我用Java编写的游戏中实现和撤消/重做系统。我采取的方法是在每次移动后序列化游戏状态。我有一种方法可以将序列化对象保存在堆栈上并访问它们以进行撤消/重做吗?

4 个答案:

答案 0 :(得分:3)

游戏编程与“企业应用程序”编程和“webapp编程”没什么关系。

所以这取决于你正在进行什么样的游戏,但是你的方法和gaven所以这些票价绝对没有与游戏状态如何在“真实”游戏中保存。表演将会非常糟糕,你最终会使用很多的内存和磁盘空间。

像暴雪的魔兽争霸III 或微软的帝国时代这样的精彩游戏只会存储重现任何游戏状态所需的用户输入< /强>

这就是他们如何实现超高效的网络传输,即使玩家有数百或数千个对象,这就是他们如何实现微小的保存游戏文件(可用于继续游戏或重放):想要低延迟?简单,只需通过UDP发送玩家的击键和鼠标点击以及这些输入发生的时间。

这与撤消/重做有什么关系?琐碎...

想象一下,自您的游戏开始以来,您已经拥有200个用户输入,并希望撤消最后一步:您从头开始游戏并提供199个第一个输入。而已。完美的撤消。

当然,您只需在重放这199个步骤时重放逻辑,而无需更新需要更新视图。对于大多数游戏而言,逻辑只占花费总时间的一小部分(大部分时间花在视图中)。所以重播这些输入非常快。

去过那里,完成了:)

现在,如果您想要另一种方式进行无限制的撤销/重做,那么必须要说一下“对不可变对象的OO”:我编写了游戏工具编辑器(如地图编辑器)无限制的撤销/重做。诀窍是使用不可变对象。然后坚持以前的状态很容易:只是层次结构顶部的对象('gamemap239')。

因为您只使用不可变对象,所以“新”游戏地图与之前的游戏地图共享99.9%的对象,因此内存使用率非常低,可以保留大量的状态。

我已经成功地在非商业软件中将这个“OO用于不可变对象以实现高效撤消/重做”,这不是游戏,而且它确实是一项了不起的技术。

现在它取决于您正在进行的游戏:当然,对于扫雷游戏,您可以使用Swing和常规Java编程技术。但是对于更高级的游戏,常规的Java集合和API以及常规的企业/ webapp / fatclient编程技术根本就不会削减它。

答案 1 :(得分:2)

Java提供的javax.swing.undo.UndoManager本质上是UndoableEdit的堆栈(即命令)。如果您在UndoableEdit中保存前后状态,那么每当您发出撤消或重做时,只需在撤消或重做时恢复该状态即可。

如果您不能使用上面的swing类,只要确保在执行不同的编辑/命令时执行正确的操作,就可以使用基本堆栈或列表。例如,如果要撤消一些操作,然后进行不同的编辑,则先前撤消的命令将丢失。

答案 2 :(得分:2)

Kaleb的建议太棒了(+1),但没有详细说明。我无法在评论中提供足够的细节,也没有看到如何使用它的简单明了的解释。

可撤消编辑由3个重要部分组成 - 构造函数,撤消方法和重做方法。诀窍是让每个“编辑”非常简单。根据游戏的复杂程度,“移动”之间可能需要进行数百次编辑。

让我们说在一个回合制模拟中,你移动一支军队 - 这是一个编辑 - 然后计算机移动它的所有军队(每一个都是一个编辑)。一个随机的天气条件 - 另一个编辑,一个城市建立一个新的坦克 - 另一个编辑,...

每种类型的编辑都是不同的对象类型。假设你正在将一个单位从(35,150)移动到(35,151)。移动可能如下所示:

undoManager.addEdit( new MoveOne(map, new Point(35,150), new Point(35,151)) );

并撤消此修改只会是:

undoManager.undo();

自动将所有内容撤消到最后一次“重要”编辑(人类玩家最后一次编辑)

以下是“移动”类的内容:

MoveOne extends AbstractUndoableEdit {
    private final Point start;
    private final Point finish;
    private final Map map;

    public MoveOne(Map pMap, Point pStart, Point pFinish) {
        start=pStart;
        finish=pFinish;
        map=pMap;
        redo();
    }

    public redo() {
        MapObject piece=map.getObjectAt(start);
        map.moveObject(piece, finish);
    }

    public undo() {
        MapObject piece=map.getObjectAt(finish);
        map.moveObject(piece, start);
    }
}

像这样的一连串小动作可能会累积成一个“人类”动作。这些“人类”可以通过isSignificant“标记进行标记 - 这样您就可以轻松撤消到最后一个”重要“事件。游戏中的其他所有内容都只是简单地操纵这些琐碎的小物体。

我喜欢这个模式,并且向@Kaleb提出道具,指出java现在支持它在JDK中!

答案 3 :(得分:0)

我实现了可重用的Undo / Redo,并在此处发布了有关此内容的博客: https://odoepner.wordpress.com/2020/10/22/undo-redo-in-java-using-protostuff-serialization-and-binary-diffs/