2

我正在开发一个 Java 桌面飞行模拟。我需要记录驾驶舱内发生的所有飞行员动作,例如油门控制、转向、武器部署等,以便稍后查看这些事件(或实时流式传输)。

我想在事件的回放上添加一个视觉回放功能,这样当我及时向前和向后移动时,我可以直观地看到驾驶舱。只要我按时间顺序回放事件,回放就没有问题,但是倒带有点棘手。

您将如何实现倒带功能?

4

6 回答 6

3

我会使用修改后的Memento 模式

不同之处在于我将让 Memento 对象存储所有试点操作的列表。

Memento 模式通常用于回滚(撤消),但是在您的情况下,我也可以看到它也适用。您还需要将试点操作设为可存储状态。

于 2009-07-07T13:30:29.440 回答
2

您可以使用命令模式的一种变体,并让您的每个试点操作都实施撤消操作。

例如,如果您的飞行员做出了向左转向的动作(我知道很简单),那么它的反面将是向右转向。

public interface IPilotAction {
    void doAction(CockpitState state);
    void undoAction(CockpitState state);
}

public class ThrottleControl implement IPilotAction {

     private boolean increase;
     private int speedAmount;

     public ThrottleControl(boolean increase, int speedAmount) {
         this.increase = increase;
         this.speedAmount = speedAmount;
     }

     public void doAction(CockpitState state) {
         if (increase) {
            state.speed += speedAmount;
         } else {
            state.speed -= speedAmount;
         }
     }

     public void undoAction(CockpitState state) {
         if (increase {
             state.speed -= speedAmount;
         } else {
             state.speed += speedAmount;
         }
}
于 2009-07-07T13:29:39.160 回答
1

您正在寻找的实际上是CommandMemento模式的混合。每个试点操作都应该是您可以记录的命令。如果需要,每个记录的命令都有一个备忘录,记录了(A)不在命令中的任何附加状态,并且(B)不能可靠地重建。“B”很重要,在几乎任何重要的领域中都有一些这种状态。它需要被存储以恢复准确的重建。

如果您合并这些概念,实质上为每个命令附加一个备忘录,您将拥有一系列完整记录的确定性事件。

我在另一个答案中更详细地讨论了这个问题。不要害怕根据您的特定需求大幅调整设计模式。:)

RE 性能问题:

如果您预计跳跃几分钟是一种常见情况,并且在实施后您表明这是一个不可行的性能瓶颈,我建议您实施一个偶尔的“快照”以及日志记录机制。基本上每隔几分钟保存一次整个应用程序状态,以最大限度地减少您需要执行的日志滚动量。然后,您可以从最近的保存状态访问所需的时间范围。这类似于动画和媒体中的关键帧

于 2009-07-07T13:32:55.160 回答
0

不是直接的答案,但请查看有关实施撤消的讨论。大多数情况下,它们将与文本编辑器有关,但应该适用相同的原则。

如果您更喜欢不变性,它会有所帮助。撤消复杂的更改很困难。即使是自动化系统也存在性能问题(软件事务内存,STM)。

于 2009-07-07T13:39:30.333 回答
0

您可以在每个实例中存储状态。1kb 用于状态(风速、物体速度 + 方向/控制输入状态,x 30fps x 20 分钟 ~ 36megs。1kb 的状态可以让您记录大约 16 个物体(位置/速度/角速度/方向/和 5 个控制轴/影响)

这对你来说可能太多了,但它最容易实现。根本不需要做任何工作来重新创建状态(即时 acecss),并且您可以很容易地在状态之间进行插值(为了更快/更慢的播放)。对于磁盘空间,您可以将其压缩,这可以在录制时完成,因此在播放时不会占用内存。

节省空间的一种快速方法是对录制文件进行分页,并分别压缩每个 bin。即每分钟一个压缩流。这样,您只需解压缩当前的 bin,节省大量内存,但这取决于您的状态数据压缩的程度。

录制命令并让你的类文件实现多个播放方向需要大量的调试工作。减慢/加速播放也将更加计算密集。您唯一节省的就是空间。

如果那是溢价,还有其他方法可以节省。

于 2009-08-23T17:12:56.973 回答
0

确保您已经以模拟的“状态”是一个函数的方式实现了模拟。也就是时间的函数。

给定时间的初始状态T0,您应该能够Tn为任何时间构建模拟框架n。例如,初始静止状态和没有事件(还)可能等于恒等函数,所以Tn == Tn+1.

给定一些试验性的动作事件Ta,你应该能够Ta+n为任何事件构建一个框架n。因此,您可以将事件视为修改函数,该函数将时间值作为参数并返回该时间的模拟帧。

我会将事件历史实现为(时间,函数)对的拉链,表示模拟的控制状态。“当前”状态将成为焦点,右侧是未来状态列表,左侧是过去状态。像这样:

([past], present, [future])

每次模拟状态变化时,记录一个新的状态函数在future. 然后运行模拟就变成了从列表中取出函数future并将当前时间传递给它们的问题。向后运行它完全相同,只是您将事件从past列表中取出。

因此,如果您在 timeTn并且想要倒退到 time Tn-1,请查看属性小于past的最新状态的列表。传入它的属性,你就有了 time 的模拟状态。timen-1n-1functionTn-1

我在这里用 Java 实现了一个 Zipper 数据结构。

于 2009-07-07T16:19:19.653 回答