正如我在之前的问题中所说,我还是Java新手。 我正在用Java开发基于磁贴的游戏。运动在网格上。我正处于实施角色运动的阶段。
我的计划是使用我在this question中粘贴的代码。但是,这不起作用,我被告知应该使用SwingWorker。
作为一名解决问题的人,我一整天都在考虑这个问题,我有一个想法。 我可以通过稍微不同的方式来达到同样的效果。
我提到我有一个定时器,每20毫秒就会关闭一次,重新调整一次。
我的想法是,我可以在计时器的事件重绘之前进行方法调用,这将使所有人1个单位更靠近他们的目的地(下一个正方形/平铺)。 这种事情有用吗?在游戏的后期,可能有100个左右的人。 20ms是否有足够的时间来遍历所有人并将他们移动到离目的地更近的一个单位? 20ms太短了吗?我根本没有意义吗?
欢迎您的意见/答案/想法:)
答案 0 :(得分:2)
我同意Bill所说的一切,尤其是关于Swing神秘的部分(虽然不比其他图形环境更真实)。
为了给出一些参考,30 fps(这是大多数隔行扫描屏幕产生的)是每33毫秒1帧。 60 fps是人类可以感知的最高频率(1帧/ 16 ms),大多数LCD监视器以60或75 Hz的频率刷新,这将是您实际生产的绝对最快速度。 20毫秒/帧的帧速率为50 fps,这也与欧洲地区的电频率一致,人眼也很明显。
我建议反对的主要是在紧张的循环中做所有事情。这将导致您的游戏速度高度依赖于您所玩的系统。在一个更快的系统上,你可能会得到比玩家反应更快的帧喷射,即使它在较旧的系统上合理地运行(或者你会在破旧的机器上遇到相反的问题)。使用计时器可以让您在速率上更加一致,但是如果错过了帧截止日期,您必须进行一些防御性检查。这意味着您的计时器需要知道计算何时完成,因此如果另一帧勾选并且尚未完成,则它将跳过下一帧。更好的是,您可以记录计算之间实际传递的时间长度,并相应地调整角色的实际移动距离。
另一个警告:虽然绘图必须在AWT线程上完成并且计算结束(为了保持程序响应),状态也需要在AWT线程上更新。如果让执行计算的线程更新游戏状态,那么AWT线程将在重绘过程中看到这个,导致称为撕裂的效果。因此,您可能需要复制状态以发布到AWT线程。这就是SwingWorker的用途,您可能会将其与Timer结合使用。
令人惊讶的是,与我在其他问题上发布的内容相比,我说的大多数内容实际上都是新的。
答案 1 :(得分:2)
好的,首先回答20毫米问题。您可以在20ms内完成相当多的游戏逻辑。但我要说更新显示器太频繁了。您还应该记住,操作系统通常会以10-20毫秒的时间顺序排出CPU - 换句话说,任何时候的另一个进程都可能很容易地将您的进程延迟大约一段时间。例如,看看我在behaviour of Thread.sleep()上的文章 - 从图表中注意到,由于系统中等繁忙,操作系统无法满足我们要求的睡眠时间。如果你需要在100ms的帧之间平均睡眠,那么在这里20ms左右的抖动并且不会太糟糕。但是当你要求20ms时,20ms的抖动可能会更明显......
所以我可能从每秒10帧(100毫秒暂停)开始,看看它看起来如何。
就代码而言,如果你的游戏逻辑每次打勾所需的时间或多或少相同,那么我就会在每次打勾时从逻辑重绘 - 睡眠开始。记住你需要同步你正在绘制的东西,所以在你的游戏逻辑线程中,理想情况下你需要避免长时间持有锁。
答案 2 :(得分:1)
这个摆动区域可能是Java中最具“黑魔法”的东西。它可能很棘手,而且有很多方法。
最重要的规则是永远不要修改屏幕,除非你在AWT线程中,但不要使用AWT线程,而不是修改屏幕。
由于每个挥动“回调”(如按钮监听器)都在AWT线程上,因此您通常不必考虑这一点,但对于活动渲染情况,您必须密切关注。
通常在像这样的大型系统中,您可以一次完成整个计算(移动所有数据),然后重新绘制需要一步完成的所有内容。
因此,在大多数情况下,您在连续线程中完全在绘制系统之外进行计算,然后在顶层组件中调用重绘函数。重绘应该将绘制事件传递给AWT线程上的所有组件,因此这不应该是一个问题。
然后每个组件应该查看其状态(已经更新)并使用该信息绘制自己。
这是应该如何完成的,基础知识,但它可能很慢,并且有很多技巧可以加快速度。你可能想找一本涉及秋千编程和游戏的书。