1

正如我在之前的问题中所说,我对 Java 还是很陌生。我正在用 Java 开发一个基于图块的游戏。运动在网格上。我正处于实施角色移动的阶段。

我的计划是使用我在这个问题中粘贴的代码。但是,这不起作用,我被告知应该使用 SwingWorker。

作为一个问题解决者,我这一天一直在思考这个问题,我有了一个想法。我可以通过做一些不同的事情来达到同样的效果。

我提到我有一个每 20 毫秒关闭一次的计时器,称为重绘。

我的想法是,我可以在计时器事件的重绘之前调用一个方法,这将使所有人更接近他们的目的地(下一个正方形/瓷砖)。那种东西行得通吗?在游戏后期,可能有 100 人左右。20 毫秒是否足以循环遍历所有人并将他们移动到离目的地更近的一个单元?20ms的时间太短了吗?我只是没有意义吗?

欢迎您的意见/答案/想法:)

4

3 回答 3

2

我几乎同意比尔所说的一切,尤其是关于 Swing 神秘的部分(尽管并不比其他图形环境更是如此)。

举几个参考,30 fps(这是大多数隔行屏幕产生的速度)是每 33 毫秒 1 帧。60 fps 大约是人类可以感知的最高速度(1 帧/16 毫秒),大多数 LCD 显示器以 60 或 75 Hz 刷新,这绝对是您实际可以产生的最快速度。20 毫秒/帧是 50 fps 的帧速率,这也与欧洲地区的电频率相吻合,人眼即可察觉。

我建议反对的主要事情是在一个紧凑的 while 循环中做所有事情。这将导致您的游戏速度高度依赖于您所玩的系统。在较快的系统上,即使在较旧的系统上可以正常播放(或者在破旧的机器上会遇到相反的问题),您的帧喷射速度也可能超过玩家的反应速度。使用计时器将使您的速度更加一致,但是您必须进行一些防御性检查,以防错过帧截止日期。这意味着您的计时器需要知道计算何时完成,因此如果另一个帧滴答作响并且尚未完成,那么它将跳过下一帧。更好的是,您记录计算之间实际经过的时间,并相应地调整角色的实际移动。

另一个警告:虽然必须在 AWT 线程上进行绘图并对其进行计算(以保持程序响应),但也需要在 AWT 线程上更新状态。如果您让执行计算的线程更新游戏状态,那么 AWT 线程将在重绘过程​​中看到这一点,从而导致称为撕裂的效果。因此,您可能需要制作一份状态副本以发布到 AWT 线程。这就是 SwingWorker 的用途,您可能会将其与 Timer 结合使用。

令人惊讶的是,与我在您的其他问题上发布的内容相比,我所说的大部分内容实际上都是新的。

于 2009-04-23T17:50:36.673 回答
2

好的,首先回答20毫秒的问题。你可以在 20 毫秒内完成相当多的游戏逻辑。但我想说更新显示太频繁了。您还应该记住,操作系统通常以 10 到 20 毫秒的时间片来分配 CPU——换句话说,任何时候另一个进程都可以轻松地将您的进程延迟大约该时间。例如,看一下我关于Thread.sleep() 行为的文章——从图中注意到,由于系统处于中等繁忙状态,操作系统无法满足我们要求的睡眠时间。如果您需要 100ms 的帧之间的平均睡眠,那么 20ms 左右的抖动在这里并不会太糟糕。但是当你要求 20 毫秒时,20 毫秒的抖动可能会更明显......

所以我可能会以每秒 10 帧(暂停 100 毫秒)开始,然后看看效果如何。

就代码而言,如果您的游戏逻辑每次滴答花费或多或少相同的时间,那么我会在每个滴答时从 logic-repaint-sleep 开始。请记住,您需要同步您正在绘制的内容,因此在您的游戏逻辑线程中,您最好避免长时间持有锁。

于 2009-04-23T17:55:31.500 回答
1

这个挥杆领域可能是 Java 中最“黑魔法”的领域。这可能很棘手,并且有很多方法。

最重要的规则是永远不要修改屏幕,除非你在 AWT 线程中,但不要使用 AWT 线程来修改屏幕。

由于每个摆动“回调”(如按钮侦听器)都进入 AWT 线程,因此您通常不必考虑这一点,但对于活动渲染情况,您必须密切关注。

通常在像这样的大型系统中,您一次进行整个计算(移动所有数据),然后重绘所有需要一步绘制的内容。

因此,在大多数情况下,您在一个连续线程中完全在您的绘图系统之外进行计算,然后您将在顶级组件中调用一个重绘函数。重绘应该将绘制事件传递给 AWT 线程上的所有组件,所以这应该不是问题。

然后每个组件都应该查看它的状态(已经更新)并使用该信息来绘制自己。

这是应该如何完成的,基本的,但它可能很慢,并且有很多技巧可以加快速度。你可能想找一本涉及挥杆编程和游戏的书。

于 2009-04-23T17:21:03.640 回答