Java中的Pacman问题

5

我需要制作一个可联网的吃豆人游戏版本来完成我的大学作业。我认为最好的方法是先创建一个本地的吃豆人游戏副本,然后扩展其功能以实现联网游戏。

我必须说我对Java GUI开发和利用Java内部的这些功能还比较新。

我已经开始按照上述链接学习使用Java进行游戏开发,以“吃豆人”游戏为例。

我决定将迷宫表示为一个int数组,其中不同的值表示不同的含义。然而,当主游戏循环中的绘图方法运行时,我正在使用此方法重新绘制整个迷宫。

    for (int i : theGame.getMaze())
    {
        if (i == 4)
        {
            g.setColor(mazeWallColour);
            g.fillRect(curX, curY, cellSize, cellSize);
            curX += 25;
        }
        else
        {
            curX += cellSize;
        }

        index++;


        // Move to new row
        if (index == 25)
        {
            index = 0;
            curX = 10;
            curY += cellSize;
        }
    }

然而,这只提供了不到1fps的速度。尽管我注意到上面链接的示例使用了类似的重绘方式,我认为它在一个不可见的图像上进行(有点像双缓冲[我使用了像第一个链接中解释的BufferStrategy])。有什么更好的方法来重新绘制迷宫呢?
任何指针/建议都将很有用。
谢谢您的时间。 http://pastebin.com/m25052d5a - 用于主游戏类。
编辑:我刚刚注意到在试图查看哪些代码执行时间很长时发生了一些非常奇怪的事情。
在paintClear(Graphics g)方法中,我添加了
ocean = sprites.getSprite("oceano.gif");
g.setPaint(new TexturePaint(ocean, new Rectangle(0,t,ocean.getWidth(),ocean.getHeight())));
g.fillRect(10, 10,getWidth() - 20,getHeight() - 110);

当我加入了这些代码时,整个程序都运行得很顺利 - 但是当我删除这些代码后,整个程序变慢了?这是什么原因造成的?

更新代码


马拉基,请发布你的整个游戏代码。我宁愿能够“运行”一些东西。将其压缩并放在filedropper上。 - mmcdole
正如Simucal所述,如果没有完整的代码源访问权限,调试程序就会变得很困难。对于以上代码,暂时没有明显的问题,我建议你首先检查getSprite方法的实现。 - shsmurfy
10个回答

4
首先,我建议您在代码中使用命名常量而不是随意的数字,并考虑使用枚举类型来表示单元格类型。虽然这不会使您的代码运行更快,但肯定会使它更易于理解。另外,“i”通常用作计数器,而不是返回值。您应该将其称为“cellType”或类似的名称。我还建议您将舞台地图使用2D数组表示,因为这样做可以简化许多事情,无论从逻辑上还是从概念上。
话虽如此,这里有一些尝试的方法:
将“setColor()”移出循环并执行一次。编译器可能能够执行循环不变式提升,从而为您执行此操作(也可能会),但从概念上讲,您应该这样做,因为看起来您希望所有墙壁都是一个颜色。
尝试调用drawRect()而不是fillRect(),看看是否可以更快地绘制。我认为这不会更快,但是值得一试,即使它看起来更丑陋。同样,您可以尝试创建一个Image,然后再绘制它。这样做的好处是,您可以轻松告诉您的Graphics对象在图像上实现变换。此外,请考虑完全删除它,并确保它对性能造成了重大影响。

另外,通常您不需要请求父级的Graphics对象并直接在其上实现绘画。相反,您应该覆盖其paintComponent()方法,并仅利用提供给您的Graphics(可能调用帮助程序方法)。Swing组件默认情况下是双缓冲的,因此您不需要自己实现它;只需让Swing对象完成其工作,并告诉您何时进行绘制即可。

此外,你最终会重绘整个屏幕,这有点过头了。如果你调用repaint(Rectangle),Swing可以选择仅重绘显式标记为脏的棋盘部分。当你更新其中一个精灵时,请仅在精灵旧位置和新位置的区域上调用repaint(r)。当你完成一个关卡并需要新的棋盘时,然后你可以调用repaint()(没有参数)来重新绘制整个地图。
你还应该查看Sun's tutorial以获取Swing效率方面的一些技巧。

4
我认为自己在Java方面还是个初学者,但最近我用一些你提到的技术开发了一个动态地图和编辑器的青蛙过河游戏,并且我很乐意提供一些帮助。
正如所提到的,枚举类型是可行的方法。我将我的地图设置成一个二维数组,并为每种不同的类型设置一个枚举类型,在我的地图类中编写一个方法将一个图像分割成我的枚举类型的每个值并赋值给地图中的每个方块。
关于映射方面帮助我的教程可以在Coke and Code上找到。如果您需要任何方面的帮助,那里有所有的源代码,虽然您似乎已经对您正在做的事情有很好的掌握。如果仍然需要帮助,我可以随时提供一些源代码帮忙。

3

您上面列出的代码不可能是1fps问题的根源...我有比这个做得更多,运行速度更快的代码。

您能对该代码进行基准测试并确保它是问题的根本原因吗?


3
看起来你调用Thread.sleep并没有达到你的意图,但我认为这不是你困境的根源。你有以下代码: Thread.sleep(Math.max(0, startTime - System.currentTimeMillis())); startTime永远会小于System.currentTimeMillis(),所以startTime - System.currentTimeMillis()永远是负数,因此你的sleep将总是0毫秒。这与你展示的示例不同,因为示例在计算之前将startTime增加了40毫秒。它正在计算睡眠多长时间以填充绘制时间为40毫秒。
无论如何,回到你的问题。我建议进行测量以确定你的时间花在哪里。在知道什么很慢之前,没有优化的必要。你已经知道如何使用System.currentTimeMillis()。尝试使用它来测量所有时间的去处。是所有时间都花在绘制墙壁上吗?
编辑 - 我看到这被标记为已接受,那么我应该推断出当你修复睡眠时间时问题消失了吗?我没有太多Java GUI的经验,但我可以猜测,也许你的代码正在饿死其他重要的线程。通过将你的线程设置为最高优先级,并且只调用sleep(0),你几乎可以保证进程中没有其他线程可以执行任何操作。这里有Raymond Chen博客中的一篇文章解释了为什么。

2
我不是游戏开发者,但那个帧率似乎非常慢。
我不太确定你的代码是如何工作的,但提高渲染性能的一个可能方法是找到那些变化不大的部分(例如迷宫的墙壁),避免为每一帧重新创建它们。
创建一个包含恒定元素(迷宫?背景)的 BufferedImage,然后在每一帧中首先重新绘制它。在这个缓冲图像的顶部,绘制可变元素(PacMan、ghosts、dots 等等)。
这种技术以及许多其他 Java2D 性能提示都在 Romain Guy 的优秀著作 Filthy Rich Clients 中讨论过。

2

为了让你不必担心这是Java的问题,我曾经参与开发过一个频谱分析仪(类似示波器),其中整个GUI部分(包括迹线、菜单、按钮和滚轮处理)都是用Java完成的。当我到达时,它的帧率只有1fps,离开时已经达到12-20fps了。这其中涉及了很多处理,并且运行在一台非常慢的处理器上。

只更新需要更新的GUI部分。通常情况下,你可以重新绘制整个屏幕,但只设置一个剪辑区域来真正更新的部分。

小心内部循环--它们是速度杀手。

尽量避免分配和释放大量对象。我不是说不要使用对象,而是不要为每个像素创建一个对象:)

祝你好运!


1

哇,这是一个给刚学 Java 的人的相当棘手的问题。

我的建议是?从对象的角度出发思考。你能否编写一个不带用户界面的程序来模拟游戏本身的行为?一旦你搞定了这个,就可以专注于用户界面的特殊问题了。是的,在网络部分之前先从本地版本开始。

我不是一个游戏玩家。我想知道 Java2D API 能提供什么让你的生活更好的东西?

你有多少时间来完成它?


是的,考虑到这个模块本身就是关于网络的,这似乎有点过分了 - 我想我可能会放弃尝试自己构建这个游戏,并调整上面包含的源代码,因为我们只会在网络方面得分。 - Malachi

1

这可能听起来很明显,但你的性能问题是因为你正在重新绘制整个迷宫,这是不必要的。相反,你需要只重绘迷宫中已更改的部分。

我以前解决这个问题的方法是将迷宫的更新与实际重绘分开成不同的线程(有点像线程化的MVC)。每当你更改迷宫中的一个单元格时,你会将其标记为“脏”的,你的重绘线程会定期检查并重绘仅脏单元格。

对于极其通用的建议,我感到抱歉。


即使重新绘制整个屏幕,也应该达到1fps以上。 - Allain Lalonde

1

Java/Swing默认使用双缓冲。如果您正在使用Swing,则无需像其他答案建议的那样单独进行双缓冲。

我同意Allain的观点,您列出的代码不可能导致1fps的问题。我曾编写过高度低效的Java/Swing动画代码,其运行速度比您描述的要快得多。请进行更多测试以缩小缓慢的原因。


1
如果可能的话,您应该保留迷宫的图像,并在一个库调用中绘制它。它可能不需要完整的分辨率,如果您想要一个像素化的8位感觉,我相信图形库将会非常乐意提供帮助 8^)
另外,正如其他人所提到的,您可以通过仅重新绘制需要更新的屏幕部分来节省时间。这可能很烦人,但它可能允许您显着提高帧速率。一定要进行一些实验,以确保这是正确的情况,然后再付出必要的努力!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接