views:

326

answers:

4

Hi all,

I've just finished my entry for the 14th Ludum Dare 48-hours game making competition yesterday, and decided to do it in java using java2d for the graphics.

I'm not that familiar with the API and haven't done a lot of graphics programming, but my game is quite small (only a dozen or so very small moving object) so I assumed I could program it naively and still encounter no performance problems.

Needless to say, I was wrong. The game performs alright most of the time but once there are a bit too many 'enemies' moving around on the screen or the resolution is cranked up too high it start getting visibly slower.

I've determined the performance bottleneck to be the screen drawing functions, when those are commented out the game is very fast.

Could someone give me a heads up on what I might be doing wrong here? The (very short) source code is located here with most of it the Main class, with the usual suspects being the draw() function that is called in the inner game loop.

I already use a BufferStrategy to update the screen, so that shouldn't be the problem unless I'm doing it wrong.

Thanks in advance, Ido.

+5  A: 

Your code isn't following the single threading rule:

http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html

I'm not sure if this will cause the specific performance problems that you are seeing, but it stands out as a big potential problem, imo.

jsight
Yes, this will actually cause huge problems in both performance and bizarre visual effects as the number of threads go up, and is very likely the problem.
Bill K
Do you mean the painting outside of the gui thread like James said, or something else?
Ido Yehieli
+5  A: 

Well, just from a brief look at your draw() function, it seems that you're declaring a number of new objects (especially in drawPolygonBody). Try to reuse objects instead of declaring a new ones each time.

EDIT: instanceof has overhead. It would be a better idea to extend Body to have a draw(Graphics g) function that draws itself. eg:

public class Circle extends Body
{
    // override
    public void draw(Graphics g)
    {
        ...
    }
}

...

void drawBody(Body body) 
{
    body.draw();
}
CookieOfFortune
+3  A: 

Have you tried profiling it to see where in the drawing functions the bottleneck is?

It's hard to say much just by looking at it, but I'm wondering why you're both drawing and filling the polygon in drawPolygonBody.
Also, in drawBoxBody, you're drawing four lines separately instead of just calling drawRect().

Michael Myers
I guess you are right, if I fill the polygon there is no need to also draw it.In drawBoxBody I first did it that way but encountered some issues and reverted it back, but I will take another look at it.
Ido Yehieli
+6  A: 

A few observations, although I don't think any of them will help much.

The main thing is that you're painting off of the AWT thread. Override paintComponent() and instead call repaint() on the object. This can cause all sorts of problems otherwise.

You recreate colors every frame. This may or may not be one of those things that you want to cache. I don't think though that having a constant for your colors is likely to screw up GCing and will make things easier to keep straight when you want to reuse colors later.

You redraw the entire window every frame. You just need to repaint the sections that changed.

You don't need to draw the background. Set the background color and let the parent take care of everything.

As a design thing, the bodies should be in charge of drawing themselves. Their owner should notify them that they need to be drawn rather than drawing them.

The bodies recreate their state every time. Consider having them store it between times and mutate them as needed. You may be spending a lot of time doing trig calculations in the drawCircleBody()

Something to consider is setting up a timer rather than using a sleep in a while loop. This will get you a more consistent frame rate, but you need to make sure that you can actually meet your obligations (or coalesce multiple frames into one if you miss a deadline) or you'll end up creating too many threads.

Consider using a SwingWorker to do the calculations, then update the state in the done() method, finishing by calling repaint().

These are just a few things. You should experiment to see what works and what doesn't. It's been a while since I've done Java graphics drawing.

James
This is the answer, I wish I had a +4!
Bill K
Thanks for the detailed answers.Regarding the 1st point, do you mean that I should have extended e.g. JPanel instead of JFrame and then override its paintComponent()? Because JFrame does not have such a function for me to override.Regarding the last one, that would also have the added bonus of making my app scale well to more than 1 cpu/core, right? Because then most of the calculation work happens in a thread separate from the GUI?
Ido Yehieli
Yes, it should probably be a JPanel. It should extend a JFrame only when you need to change actual window behavior: you actually draw on its content pane (which you can set to be a JPanel).As for scaling, this may not scale as well as you think it will since some of the work has to be done sequentially. In particular, each frame has to be done sequentially. However, future JVMs may actually be able to do some multi-core optimization for you.
James