我正在开发一款游戏,玩家可以在棋盘/网格上移动。它有moveLeft()、moveRight()、moveUp()和moveDown()方法,可以在棋盘上一次移动玩家1个空间。
我正在计划我的方法,以实现玩家移动所需的撤消/恢复功能(例如,如果玩家向左移动了一个空格,并且调用了撤消,玩家将向右移动一个空格)。我是一名学生,正在调查斯塔克斯。
然而,当在编程之前在纸上规划逻辑时(我发现这对我做家庭作业有帮助),我不知道Stacks是否适合以下问题。。。。
调用每个move方法时,会根据调用的方法将字符串"Up"、"Down"、"Left"、"Right"添加到Stack 1中。这会跟踪玩家在棋盘上的移动。
如果调用undo(),则堆栈1顶部的字符串将被删除,并添加到新堆栈"堆栈2"中。这是用来跟踪"撤消"的移动,这样redo()就有了一个路径。
如果调用了redo(),则堆栈2上的字符串将被删除,并重新添加到堆栈1中。
这非常有效,但前提是您调用Redo()的次数与Undo()完全相同,并且直接在它之后
例如:
- 玩家进行4次移动:(堆栈1现在有4个字符串)
- Undo()调用一次:(堆栈1现在有3个字符串,堆栈2有1个字符串)
- 玩家多出4步:(堆叠1现在有7个字符串,堆叠2仍然有1个字符串)
- Undo()调用一次:(堆栈1现在有6个字符串,堆栈2有2个字符串)
- Redo()被调用:(这是问题所在)这可以被调用两次,因为Stack 2有两个字符串,但实际上,棋盘上的玩家应该只能调用一次
在过去的4个小时里,我一直在孤立地尝试研究这背后的逻辑!如有任何建议,不胜感激。也就是说,基于以上内容,是否仍然可以用Stacks做我需要做的事情,并且我需要找到解决问题的方法?或者我应该放弃,因为这是不可能的。
如果因为不确定何时清除撤消Stack
而不想使用Stacks
,可以使用List
。然后,您的逻辑可以以类似的方式进行维护:
- 保留您正在进行的移动的索引(从0开始,每次新移动递增1)
- 如果执行了撤消操作,则递减索引(除非您位于列表的开头)
- 如果完成了重做,请增加索引(除非您在列表的末尾)
- 如果执行了新的移动,请增加索引并清除所有大于或等于索引的移动(从而清除撤消/重做移动)
那么,你的例子是:
- 玩家进行4次移动(索引为3,列表有4项)
- Undo()调用一次(索引为2,列表有4项)
- 玩家再移动4次(索引为6,列表中有7个项目,所有撤消/重做项目都已清除)
- Undo()调用一次(索引为5,列表有7项)
- 重做调用一次(索引为6,列表有7个项目)
- 再次调用重做(由于没有可用的重做操作而被拒绝)
这样想吧。。撤消和重做的全部思想都围绕着应用程序(程序)的状态。撤销意味着您希望立即转到应用程序的上一个状态;类似地,Redo也是如此。是的,堆栈是保存以前状态的最佳解决方案。对于撤销,你可以弹出并将其推到另一个堆栈。使用另一个堆栈进行重做操作。您可能需要设置撤消和重做的限制。如何保存应用程序的状态取决于您自己。
在步骤3中
Player makes 4 more moves: (Stack 1 now has 7 strings, Stack 2 still has 1 string)
您必须清空第二个堆栈。
修复起来很简单。基本上,只有当上一次操作是撤消时,重做才有效。
一个解决方案是:当玩家移动时,清空堆栈2(用于重做的撤消堆栈)。
其他解决方案:将堆栈2的间隙推迟到以后。维护一个布尔标志,跟踪上一个操作是否是撤消(比如boolean isRecentActionUndo),它应该始终表示上一个动作是否是撤消,方法是在每次调用Undo()时将其设置为true,在每次调用Move()时设置为false。在Redo()中检查此标志。
if(isRecentActionUndo) {
// perform redo
} else {
// clear stack 2
}
可以有其他更好的解决方案来实现这一点,执行有效的Redo()。