Java 中的 Pacman 问题
对于我的大学作业,我必须制作一个可联网的 pacman 版本。 我认为解决这个问题的最佳方法是首先制作 pacman 的本地副本,然后扩展此功能以进行网络游戏。
我不得不说,我对 java GUI 开发和在 java 中利用这些功能还比较陌生。
- http://www.planetalia.com/cursos/Java-Invaders/
- < a href="http://javaboutique.internet.com/PacMan/source.html" rel="nofollow noreferrer">http://javaboutique.internet.com/PacMan/source.html
我已经开始关注上面的链接涉及 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 的速度。 尽管我注意到上面链接的示例在每次调用绘制方法时都使用类似的重绘方式,并且我相信在不可见的图像上执行此操作(有点像双缓冲[我使用了像第一个链接一样的 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);
这使得整个过程运行顺利 - 但是当我删除这些行时,整个过程变慢了? 是什么导致了这种情况?
For my university assignment I have to make a networkable version of pacman. I thought I would best approach this problem with making a local copy of pacman first and then extend this functionality for network play.
I would have to say that I am relatively new to java GUI development and utilizing such features within java.
I have started following the above links with regards to game development within java and an example of the pacman game.
I decided to represent the maze as an int array with different values meaning different things. However when the paint method inside the main game loop is run i am redrawing the whole maze with this method.
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;
}
}
However this is providing me with less then 1fps. Although i've noticed the example linked above uses a similar way of redrawing each time the paint method is called and i believe does this on a image that is not viewable (kinda like double buffering [I've used a BufferStrategy like the first link explains]) What would be a better way to redraw the maze?
Any pointers/advice with this would be useful.
Thank you for your time.
http://pastebin.com/m25052d5a - for the main game class.
Edit: I have just noticed something very weird happening after trying to see what code was taking so long to execute.
In the paintClear(Graphics g) method i have added
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);
which made the whole thing run smoothly - however when i removed these lines the whole thing slowed down? What could have caused this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
首先,我建议您在代码中使用命名常量,而不是使用随机幻数,并考虑对单元类型使用枚举。 虽然它不会让你的代码运行得更快,但它肯定会让代码更容易理解。 另外,“i”通常用作计数器,而不是返回值。 您可能应该将其称为
cellType
或类似的名称。 我还建议您在舞台地图中使用 2D 数组,因为它在逻辑上和概念上都使许多事情变得更容易。也就是说,这里有一些可以尝试的事情:
将
setColor()
从循环中拉出并执行一次。 编译器可能能够执行循环不变提升,从而为您执行此操作(并且可能会),但从概念上讲,您可能无论如何都应该执行此操作,因为您似乎希望所有墙壁都是一种颜色。尝试调用
drawRect()
而不是fillRect()
并查看是否绘制速度更快。 我认为不会,但值得一试,即使它看起来更丑。 同样,您可以尝试创建一个Image
,然后绘制它。 这样做的优点是可以很容易地告诉 Graphics 对象对图像实施转换。 另外,请考虑将其完全删除,并确保它对性能造成重大影响。另外,通常您不需要向父级请求其 Graphics 对象并直接在其上实现绘画。 相反,您应该重写其
paintComponent()
方法并仅利用提供给您的 Graphics(可能像您一样调用辅助方法)。 Swing 组件默认是双缓冲的,因此您不需要自己实现; 只需让摆动对象完成其工作并让您知道何时进行绘画即可。另外,你最终会重新绘制整个屏幕,这有点过分了。 如果您调用
repaint(Rectangle)
,Swing 可以选择仅重绘板上明确标记为脏的部分。 当您更新其中一个精灵时,仅在该精灵的旧位置和新位置的区域上调用 repaint(r) 。 当你完成一个关卡并需要一个新的棋盘时,你可以调用 repaint() (不带参数)来重绘整个地图。您还应该查看Sun 的教程以获取一些提高效率的技巧在摇摆中。
First off, I'd recommend that you use named constants rather than having random magic numbers in your code and consider using enums for your cell types. While it won't make your code run any faster, it certainly will make it easier to understand. Also, 'i' is normally used as a counter, not for a return value. You should probably call it
cellType
or something similar. I'd also recommend that you use a 2D array for your stage map since it makes a number of things easier, both logistically and conceptually.That said, here are a few things to try:
Pull the
setColor()
out of the loop and do it once. The compiler might be able to do loop-invariant hoisting and thus do this for you (and probably will), but conceptually, you should probably do this anyway since it appears you want all of your walls to be one color anyway.Try calling
drawRect()
instead offillRect()
and see if that draws faster. I don't think it will, but it is worth a shot, even if it looks uglier. Similarly, you can try creating anImage
and then drawing that. This has the advantage that it is really easy to tell your Graphics object to implement a transform on your image. Also, consider taking this out completely and make sure that it is being a significant performance hit.Also, normally you don't need to ask for the parent for its Graphics object and implement painting directly on it. Rather, you should override its
paintComponent()
method and just utilize the Graphics given to you (possibly calling helper methods as you do). Swing components are double-buffered by default, so you don't need to implement that yourself; just let the swing object do its job and let you know when to paint.Also, you end up repainting the entire screen, which is something of overkill. If you call
repaint(Rectangle)
, Swing can choose to redraw only the sections of your board that are explicitly marked dirty. When you update one of your sprites, call repaint(r) only on the area of the sprite's old and new locations. When you complete a level and need a new board, then you can call repaint() (without parameters) to redraw the entire map.You should also look at Sun's tutorial to get some tips for efficiency in Swing.
我仍然认为自己是 Java 初学者,但我最近使用您提到的一些技术开发了一个带有动态地图和编辑器的青蛙式游戏,我非常乐意提供一些帮助。
如前所述,枚举是可行的方法。 我将地图设置为二维数组,并为每种不同类型设置一个枚举,在地图类中编写一个方法来获取一张图像,并将地图中的每个方块划分为枚举中的每个值。
可以在 Coke and Code 上找到帮助我进行映射的教程。 如果您需要帮助,所有源代码都在那里,尽管您似乎确实对自己正在做的事情有很好的掌握。 如果您仍然需要帮助,我可以随时拖出一些源代码。
I still consider myself a beginner with Java, but I recently developed a Frogger-esque game with dynamic map and editor using some of the techniques you've mentioned, and I'm only too happy to provide some help.
As mentioned, enum's are the way to go. I set my map up as a 2-dimensional array and set an enum for each different type, writing a method inside my map class to take in one image and divide each square in the map to each value in my enum.
A tutorial that helped me with mapping can be found on Coke and Code. All the source code is there if you need a hand with any of it, although you do seem to have a decent grasp of what you're doing. If you still need help I could always drag out some source code.
看来您对 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),您几乎可以保证进程中的其他线程无法执行任何操作。 来自 Raymond Chen 博客的帖子解释了原因。
It looks like your call to Thread.sleep doesn't do what you intended, but I don't think it's the source of your trouble. You have:
Thread.sleep(Math.max(0, startTime - System.currentTimeMillis()));
startTime will always be less than System.currentTimeMillis(), so startTime - System.currentTimeMillis() will always be negative and thus your sleep will always be for 0 milliseconds. It's different from the example you showed because the example increments startTime by 40 milliseconds before doing the calculation. It is calculating how long to sleep for to pad out the drawing time to 40 milliseconds.
Anyway, back to your problem. I'd recommend measurement to figure out where your time is being spent. There's no point optimising until you know what's slow. You already know how to use System.currentTimeMillis(). Try using that to measure where all the time goes. Is it all spent drawing the walls?
EDIT - I see this got marked as accepted, so should I infer that the problem went away when you fixed the sleep time? I don't have a lot of Java GUI experience, but I can speculate that perhaps your code was starving out other important threads. By setting your thread to have maximum priority and only ever calling sleep(0), you pretty much guarantee that no other thread in your process can do anything. Here's a post from Raymond Chen's blog that explains why.
您上面列出的代码不可能是 1fps 问题的根源...我的代码执行的功能远不止于此,而且运行速度要快得多。
您能否对该代码进行基准测试并确保它是问题的根源?
The code you listed above can't be the source of the 1fps problem... I have code doing far more than this that runs far faster.
Can you benchmark that code and make sure it's the root of the problem?
我不是游戏开发人员,但帧速率似乎很慢。
我不太确定您的代码是如何工作的,但提高渲染性能的一种可能性是找到显示中那些变化不大的部分(例如迷宫的墙壁)并避免重新创建它们每一帧。
创建一个包含常量元素(迷宫?、背景)的 BufferedImage,然后首先为每个帧重新绘制它。 在此缓冲图像的顶部,绘制可变元素(吃豆人、幽灵、点等)。
Romain Guy 的优秀著作 Filthy Rich Clients 中讨论了这项技术以及许多其他 Java2D 性能技巧。
I'm no game developer, but that framerate seems very slow.
I'm not quite sure how your code is working, but one possibility for improving rendering performance would be to find those parts of the display that don't change much (such as the walls of the maze) and avoid re-creating them for each frame.
Create a BufferedImage containing the constant elements (maze?, background) and then re-draw it first for each frame. On top of this Buffered image, draw the variable elements (PacMan, ghosts, dots, etc).
This technique, along with many other Java2D performance tips, is discussed in Romain Guy's excellent book Filthy Rich Clients.
为了让您不用担心它是 Java,我开发了一个频谱分析仪(如 o 示波器),其中整个 GUI 部分(跟踪、菜单、按钮和滚轮处理)都是用 Java 完成的。 当我到达那里时,速度为 1fps,当我离开时,速度为 12-20。 它需要进行大量处理,并且在非常慢的处理器上运行。
仅查看需要更新的 GUI 的更新部分。 通常,您可以重绘整个屏幕,但只需将剪切区域设置为真正更新的部分即可。
小心内部循环——它们是速度杀手。
尽量避免分配和释放大量对象。 我并不是说不要使用对象,我是说不要为每个像素创建一个对象:)
祝你好运
Just so you don't worry that it's Java, I worked on a Spectrum Analyzer (Like an o-scope) where the entire GUI portion(the trace, menus, button & wheel handling) was done in Java. When I got there it was getting 1fps, when I left it was 12-20. That had a lot of processing going on and was running on a very slow processor.
Look at only updating parts of the GUI that you need to update. Often you can redraw the entire screen but just set a clipping region to the part that is truly updated.
Be careful about inner loops--they are The Speed Killer.
Try to avoid allocating and freeing huge numbers of objects. I'm not saying don't use objects, I'm saying don't create one for each pixel :)
Good luck
哇,这对于刚刚学习 Java 的人来说是一个非常困难的问题。
我的建议? 从对象的角度思考。 您可以在没有模仿游戏本身行为的用户界面的情况下编写一些东西吗? 一旦你开始工作,你就可以专注于用户界面的特殊问题。 是的,先从本地版本开始,然后再开始网络部分。
我不是游戏玩家。 我想知道 Java2D API 会提供什么来让您的生活更美好?
你需要多少时间来完成它?
Wow, that's a pretty tough problem to give someone just learning Java.
My advice? Think in terms of objects. Can you write something WITHOUT a user interface that mimics the behavior of the game itself? Once you get that working, you can concentrate on the special problems of the user interface. Yes, start with a local version before the networked piece.
I'm not a gamer. I wonder what Java2D API would offer to make your life better?
How much time do you have to finish it?
这可能听起来很明显,但您的性能问题是因为您正在重绘整个迷宫,这是不需要完成的,而是您只需要重绘迷宫中已更改的部分。
我之前处理这个问题的方法是将迷宫的更新与实际重绘分离到不同的线程中(有点像线程 MVC)。 每次您更改迷宫中的一个单元格时,您都会将其标记为“脏”,您的重绘线程会时不时地检查以仅重绘脏单元格。
很抱歉给了极其笼统的建议
This might sound obvious but your performance problem is because you are redrawing the entire maze, which doesn't need to be done, instead you need to redraw only changed parts of your maze.
The way I've approached this issue before is by seperating the updating of the maze from the actual redrawing into different threads (kind of like a threaded MVC). Every time you change a cell in your maze you would mark it as "dirty", your redraw thread will check every now and then to redraw only the dirty cells.
Sorry for the extremly generic advice
默认情况下,Java/Swing 双缓冲区。 如果您使用的是 Swing,则不需要像其他答案所建议的那样单独进行双缓冲。
我同意阿兰的观点,你列出的代码不可能是 1fps 的原因。 我编写了效率极低的 Java/Swing 动画代码,其运行速度比您描述的要快得多。 进行更多测试以缩小速度缓慢的原因。
Java/Swing double-buffers by default. If you're using Swing, you don't need to double-buffer separately, like other answers suggest.
I agree with Allain, that the code you listed can't be the cause of 1fps. I've written highly inefficient Java/Swing animation code that runs much faster than you describe. Do some more testing to narrow down the cause of the slowness.
如果可能,您应该保留迷宫的图像,并在一次库调用中绘制它。 它可能也不需要全分辨率——如果你想要块状、8 位的感觉,我希望图形库会非常乐意满足 8^)
另外,正如其他人提到的,你可以保存通过仅重绘屏幕上需要更新的部分来节省时间。 这实施起来可能很烦人,但它可能可以让您显着提高帧速率。 在付出所需的努力之前,请务必先做一些实验以确保情况确实如此!
If possible, you should keep an image of the maze, and draw it in one library call. It probably doesn't need to be full resolution, either -- if you want a blocky, 8-bit feel, I expect the graphics library will be more than happy to oblige 8^)
Also, as others have mentioned, you can save time by redrawing only those parts of the screen that need updating. This can be annoying to implement, but it may allow you to significantly improve your frame rate. Be sure to do some experiments to make sure this is the case before exerting the required effort!