【问题标题】:Pacman in Java questionsJava 中的 Pacman 问题
【发布时间】:2009-01-14 23:14:10
【问题描述】:

为了我的大学作业,我必须制作一个可联网的 pacman。我认为我最好先制作 pacman 的本地副本来解决这个问题,然后将此功能扩展为网络游戏。

我不得不说我对 java GUI 开发和在 java 中利用这些特性还比较陌生。

我已经开始关注上面关于 java 中的游戏开发的链接以及 pacman 游戏的示例。

我决定将迷宫表示为一个 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 的速度。尽管我注意到上面链接的示例每次调用paint方法时都使用类似的重绘方式,并且我相信在不可见的图像上会这样做(有点像双缓冲[我使用了像第一个链接一样的 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);

这使整个事情顺利进行 - 但是当我删除这些行时,整个事情变慢了?这可能是什么原因造成的?

Updated code

【问题讨论】:

  • Malachi,发布您的游戏完整代码。我宁愿能够 run 一些东西。将其压缩并放在 filedropper 上
  • 正如 Simucal 所提到的,如果不访问整个源代码,就很难调试您的程序。使用上面的代码不会立即出现问题,我建议首先查看的是 getSprite 方法的实现。

标签: java network-programming pacman


【解决方案1】:

首先,我建议您使用命名常量,而不是在代码中使用随机幻数,并考虑为您的单元格类型使用枚举。虽然它不会让你的代码运行得更快,但它肯定会让它更容易理解。此外,“i”通常用作计数器,而不是返回值。您可能应该称它为cellType 或类似名称。我还建议您为舞台地图使用 2D 数组,因为它使许多事情变得更容易,无论是逻辑上还是概念上。

也就是说,这里有一些事情可以尝试:

setColor() 拉出循环并执行一次。编译器可能能够进行循环不变的提升,从而为您执行此操作(并且可能会),但从概念上讲,您可能无论如何都应该执行此操作,因为您似乎希望所有墙壁都是一种颜色。

尝试调用drawRect() 而不是fillRect(),看看是否画得更快。我不认为它会,但它值得一试,即使它看起来更丑陋。同样,您可以尝试创建一个Image,然后绘制它。这样做的好处是很容易告诉您的 Graphics 对象对您的图像进行转换。此外,请考虑彻底解决此问题,并确保它对性能造成重大影响。

此外,通常您不需要为其 Graphics 对象询问父级并直接在其上实现绘画。相反,您应该重写它的 paintComponent() 方法并只使用给您的 Graphics(可能像您一样调用辅助方法)。 Swing 组件默认是双缓冲的,所以你不需要自己实现;让摆动对象完成它的工作,让你知道什么时候上色。

此外,您最终会重新绘制整个屏幕,这有点矫枉过正。如果您调用repaint(Rectangle),Swing 可以选择仅重绘您的板上明确标记为脏的部分。当您更新其中一个精灵时,仅在精灵的旧位置和新位置区域调用 repaint(r)。当你完成一个关卡并需要一个新的棋盘时,你可以调用 repaint()(不带参数)来重绘整个地图。

您还应该查看Sun's tutorial 以获取一些提高 Swing 效率的技巧。

【讨论】:

    【解决方案2】:

    我仍然认为自己是 Java 的初学者,但我最近使用您提到的一些技术开发了一个带有动态地图和编辑器的 Frogger 式游戏,我很高兴能提供一些帮助。

    如前所述,枚举是要走的路。我将我的地图设置为二维数组并为每种不同类型设置一个枚举,在我的地图类中编写一个方法来获取一个图像并将地图中的每个正方形划分为我的枚举中的每个值。

    可以在Coke and Code 上找到帮助我进行映射的教程。如果您需要任何源代码,所有源代码都在那里,尽管您似乎对自己正在做的事情有很好的掌握。如果你还需要帮助,我总是可以拖出一些源代码。

    【讨论】:

      【解决方案3】:

      看起来您对 Thread.sleep 的调用没有达到您的预期,但我认为这不是您的麻烦的根源。你有:

      Thread.sleep(Math.max(0, startTime - System.currentTimeMillis()));

      startTime 将始终小于 System.currentTimeMillis(),因此 startTime - System.currentTimeMillis() 将始终为负数,因此您的睡眠时间将始终为 0 毫秒。它与您展示的示例不同,因为该示例在进行计算之前将 startTime 增加了 40 毫秒。它正在计算休眠多长时间以将绘图时间填充到 40 毫秒。

      无论如何,回到你的问题。我建议通过测量来确定您的时间都花在了哪里。在你知道什么是慢之前,优化是没有意义的。您已经知道如何使用 System.currentTimeMillis()。尝试使用它来衡量所有时间都在哪里。都花在画墙上了吗?


      编辑 - 我看到这被标记为已接受,所以我应该推断当您修复睡眠时间后问题就消失了吗?我没有太多 Java GUI 经验,但我可以推测您的代码可能正在耗尽其他重要线程。通过将您的线程设置为具有最高优先级并且只调用 sleep(0),您几乎可以保证您的进程中没有其他线程可以做任何事情。 a post from Raymond Chen's blog 解释了原因。

      【讨论】:

        【解决方案4】:

        您上面列出的代码不能成为 1fps 问题的根源...我的代码做得比这要快得多。

        您能否对该代码进行基准测试并确保它是问题的根源?

        【讨论】:

          【解决方案5】:

          我不是游戏开发者,但帧率似乎很慢。

          我不太确定您的代码是如何工作的,但提高渲染性能的一种可能性是找到显示变化不大的那些部分(例如迷宫的墙壁)并避免重新为每一帧创建它们。

          创建一个包含常量元素(迷宫?背景)的 BufferedImage,然后首先为每一帧重新绘制它。在这个缓冲图像的顶部,绘制可变元素(PacMan、ghosts、dots 等)。

          Romain Guy 的优秀书籍Filthy Rich Clients 讨论了这项技术以及许多其他 Java2D 性能技巧。

          【讨论】:

            【解决方案6】:

            只是为了让您不必担心它是 Java,我在一个频谱分析仪(就像一个 o-scope)上工作,其中整个 GUI 部分(跟踪、菜单、按钮和滚轮处理)都是用 Java 完成的。当我到达那里时它得到 1fps,当我离开时它是 12-20。这需要进行大量处理,并且在非常慢的处理器上运行。

            只着眼于更新您需要更新的 GUI 部分。通常您可以重绘整个屏幕,但只需为真正更新的部分设置一个剪辑区域。

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

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

            祝你好运

            【讨论】:

              【解决方案7】:

              哇,对于刚刚学习 Java 的人来说,这是一个相当棘手的问题。

              我的建议?从对象的角度思考。你能在没有模仿游戏本身行为的用户界面的情况下编写一些东西吗?一旦你开始工作,你就可以专注于用户界面的特殊问题。是的,从网络版之前的本地版本开始。

              我不是游戏玩家。我想知道 Java2D API 会提供什么来让您的生活更美好?

              你有多少时间完成它?

              【讨论】:

              • 是的,考虑到模块本身是关于网络的,这似乎有点极端——我想我可能会放弃为自己构建这个游戏并调整上面包含的源代码,因为我们只是被标记了网络方面。
              【解决方案8】:

              这听起来很明显,但您的性能问题是因为您正在重绘整个迷宫,这并不需要这样做,而是您只需要重绘迷宫的更改部分。

              我之前处理这个问题的方法是将迷宫的更新与实际重绘分离到不同的线程中(有点像线程 MVC)。每次更改迷宫中的单元格时,都会将其标记为“脏”,重绘线程会不时检查以仅重绘脏单元格。

              很抱歉提供了非常笼统的建议

              【讨论】:

              • 即使重绘整个屏幕也应该超过 1fps。
              【解决方案9】:

              默认情况下Java/Swing 双缓冲区。如果您使用的是 Swing,则不需要像其他答案所建议的那样单独进行双缓冲。

              我同意艾伦的观点,您列出的代码不能导致 1fps。我编写了效率极低的 Java/Swing 动画代码,运行速度比您描述的要快得多。进行更多测试以缩小速度缓慢的原因。

              【讨论】:

                【解决方案10】:

                如果可能,您应该保留迷宫的图像,并在一次库调用中绘制它。它可能也不需要全分辨率——如果你想要块状的 8 位感觉,我希望图形库会非常乐意提供 8^)

                此外,正如其他人所提到的,您可以通过仅重绘屏幕中需要更新的部分来节省时间。实现起来可能很烦人,但它可能可以让您显着提高帧速率。在进行所需的努力之前,请务必进行一些实验以确保情况如此!

                【讨论】:

                  猜你喜欢
                  • 2015-03-31
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-09-14
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-06-28
                  • 2015-01-16
                  相关资源
                  最近更新 更多