Java Frustration
I was looking at www.wildtangent.com, seeing what sorts of graphics they have... and even seeing most other game sites with Flash and such, and I'm really feeling like I must be doing something wrong because I still have lots of trouble getting my Java Applet games to run smoothly.
http://www.gamelizard.com/game.php?game=fallingdown
I've tried for SUCH a long time to get that to run smoothly... and the only method I've found that gets close makes the system clock more than double in speed, and the game still doesn't run smooth.
Am I missing something? Why is it that Java Applets seem so very limited?
[636 byte] By [
Malohkana] at [2007-9-29 22:23:39]

I have a slow machine (PIII 500Mhz), so it was really not running smoothly.
Maybe there are some techniques you are not using? You are of course using doble buffering. Are you using frame rate synchronization? And remember to reuse your objects. Every time the garbage collector runs, the applet freezes. So if you reuse all your objects, the GC don't run that often.
Maybe there are som errors in the paint loop, the order of which you clear, move, paint etc. Because I have a slow machine and wanted my own game to run smoothly on it, a also used a lot of time to make it run smoothly.
My game is in a way similar. A have a solid color background, and stars moving like your lines. And a spaceship moved by the arrow keys like your ball.
If you think my game runs smoothly, I could send you the source.
www.fnfconsulting.no
Hallvard
Can you host all the files somewhere and I'll take a look at them?
http://www.geocities.com/malohkan/fallingdown.zip
Ok, I just looked quickly at the code, and I'm not an expert :)
My personal opinion:
Are you using a BufferdImage as background? A quick way to erase the screen is to draw a rectangle and fill it, in your case green. When I tested BufferedImage in my game it slowed down horribly.
In my game, run calls repaint, like yours. Repaint triggers update, and I think update should do all the game logic, and then call paint, which does the clearing and then all the drawing to doublebuffer and then draw the buffer to screen. You do it differenty, but maybe the result is the same.
Just my quick opinion. I read the book "Java 2 Game Programming" and it focused on smooth running applets.
I looked at your code (GLApplet.java only), here's a few comments that should give you the red light that you're pulling Java by the feet and watching it trip.
Here's issue number one:
protected BufferedImage staticBackground, outOfFocusImage;
from what I saw, staticBackground is just there to clear the screen? BufferedImages can delay your game pretty good, so you should avoid using them when you don't need them. In this case if you need to clear the screen, call fillRect() in Graphics...
as far as outOfFocusImage is concerned, why do you need to strangle the memory with another BufferedImage just to display 3 or 4 string saying focus is gone. Instead, keep a boolean in memory indicating if you have focus or not. Then during paint, check if you have focus. If you don't, draw the strings with the current graphics object. If you do have focus, draw the game like normal.
So with those needless BufferedImages out the way, that should clear up some delay.
Now this next optimization probably won't show a huge speed difference, but you can do it anyway. Here's your method for drawing text in the middle:
public void drawMessageTextInMiddle(Graphics2D oofG2D, String string, int y) {
int offset = 0;
for (int i=0; i<string.length(); i++)
offset += oofG2D.getFontMetrics().charWidth(string.charAt(i));
oofG2D.setColor(Color.darkGray);
oofG2D.drawString(string, (getWidth()-offset)/2-1, y-1);
oofG2D.drawString(string, (getWidth()-offset)/2+1, y-1);
oofG2D.drawString(string, (getWidth()-offset)/2-1, y+1);
oofG2D.drawString(string, (getWidth()-offset)/2+1, y+1);
for (int i=1; i><6; i++)
oofG2D.drawString(string, (getWidth()-offset)/2-i, y+i);
oofG2D.setColor(Color.green);
oofG2D.drawString(string, (getWidth()-offset)/2, y);
}
That code has alot of dividing by 2 in there. Since you know you're gonna be dividing by two, you can save the computer some mathmatical frustration and not make it do as much division. Instead, you can bitshift. Bitshifting is faster because the computer isn't calculating, it's moving bits around. You can replace /2 with >>1. It's smart to put parenthesis around this because >> has low precedence. For example:
n = width>>1-1
is not the same as:
n = (width>>1)-1
So here's your method again (with the bitshifting added):
public void drawMessageTextInMiddle(Graphics2D oofG2D, String string, int y) {
int offset = 0;
for (int i=0; i<string.length(); i++)
offset += oofG2D.getFontMetrics().charWidth(string.charAt(i));
oofG2D.setColor(Color.darkGray);
oofG2D.drawString(string, ((getWidth()-offset)>>1)-1, y-1);
oofG2D.drawString(string, ((getWidth()-offset)>>1)+1, y-1);
oofG2D.drawString(string, ((getWidth()-offset)>>1)-1, y+1);
oofG2D.drawString(string, ((getWidth()-offset)>>1)+1, y+1);
for (int i=1; i<6; i++)
oofG2D.drawString(string, ((getWidth()-offset)>>1)-i, y+i);
oofG2D.setColor(Color.green);
oofG2D.drawString(string, (getWidth()-offset)>>1, y);
}
Again, this probably won't be noticeable except on slower computers...
By the way, antialiasing is a slug...
That's the basic mess I could pull out, I didn't look much at the other code. You're a really object-oriented guy, which is good, but there is such a thing as too many objects. If you're really worred about having a seperate image with antialiasing saying the game is out of focus, just load an Image (not BufferedImage) of an already made image from photoshop or something.
I know GLApplet isn't your actual game, but even optimizing GLApplet like that will speed up the general game rendering process. As far as your actual game code goes, I'll look at it later. Just lay off a bit of the antialiasing, and don't use BufferedImages when you could get away with an Image (or no image at all!). Images are faster than BufferedImages (for applets, anyway)
thanks for the tips, but I don't understand your comments about BufferedImage. I was under the impression that it's faster to use them because they stay buffered and therefore can be called to draw faster. When I use fillRect() instead, it's far more likely that images will flicker on slower computers. My profiler tells me that it's much slower as well.
A fillRect will always be as fast, or faster than the equivalent drawing of a BufferedImage (whether it is accelerated or not)
I think it's the distinction between drawing a shape from scratch and copying a shape.
I freely admit I haven't played around with BufferedImage and I may be completely and utterly wrong, but I got the impression that for larger images, it is faster to provide a formula and let Java evaluate the formula, than it is to copy every pixel from one memory location to another.
Non-buffered Images are usually in a file format that equate to a lot of complicated formulas; I don't think any of the common formats (except perhaps bitmaps) actually specify the color and z-axis value of every pixel in the image.
The worst case scenario is that you have an image that cannot be conveniently expressed by a formula. The end result is that you essentially have a formula that describes each and every pixel. At that point, you essentially have a BufferedImage. So drawing a line, using the pen and ink analogy, will always be as fast as or faster than loading an image with a line in it.
Incidently, I'm sure we'd be interested in hearing what profiler you use.I would think that a pen-and-ink draw routine would take more steps than a copy routine since it has to mathematically evaluate the formula then color the appropriate pixel.
JProfiler is the one I use. I think I most take advantage of the BufferedImage class when I have a lot of stuff that I want to draw once and not again. Like in my ReactionSlider, I have a whole bunch of antialiased shapes, and when I used the drawElipse method (I think that's what it is) for all of them, I couldn't get double buffering to work and it wouldn't stop flashing and the speed was horrible. I made my shapes into BufferedImages, one for the green circle, one for the red circle, and one to hold the background, and voila, no more flickering, and speed was smooth.
ah, you arn't *just* using [draw/fill]Rect, and drawLine?
ovals, polygons and other more complex primitives are *alot* slower, and you will almost always want to pre-rasterize them to a managed BufferedImage.
The only exception, is when you are dynamically changing the size/shape/color of the primitives - in this situation, you can't pre-rasterize them to a BufferedImage in vram.
So, either accept the sucky performance, or design around having dynamically changing primitves.
This will hopefully change in the future. (when HW acceleration for all Java2D operations is complete)
I removed my background and tried drawing my game with using:
g2d.setColor(Color.black);
g2d.fillRect(0, 0, getWidth(), getHeight());
every drawing call, and it flickers like mad. Either that means fillRect is a lot slower that drawing a BufferedImage, or my double buffering isn't really double buffering. Could one of you guys check my code I linked to and view my GLApplet to see what I'm doing wrong? Thanks again
hmm, ok I just skimmed over the code, and i've spotted these things so far :-
1) You are using passive rendering, I'd wager you realy want to be using active rendering.
2) You are using anti aliasing. Anti-aliasing in Java2D is not hardware accelerated yet, therefor performing it on every draw operation to the backbuffer is going to absolutely kill any possible hardware acceleration.
3) public void paint(Graphics g) {update(g);}
this is a) pointless, b) a potencially fatal error.
the normal sequence of calls for passive rendering are :-
i) you call repaint();
ii) repaint then places a redraw event on the Event scheduling Thread. (unless there is already 1 on the top of the queue, it may then coalesce the events into a single repaint [this behaviour is undesirable, and that is 1 of the reasons passive rendering is bad for animation])
iii) when the event scheduler gets to the repaint event, it calls the update() method of your component for which the repaint event was queued. It passes to this method the Graphics context for the component.
iv) the default implementation of update() does several things :-
a) clears the background
b) calls paint() (and passes it onto it the Graphics context)
Your paint() method calling update() is pointless (because it is unreachable unless called explicitly by your code), and also, more importantly, if you commented out your overridden update() method (so the default behaviour of update() was allowed to execute), your app. would crash due to a stack overflow (update calls paint, which then calls update etc etc)
I will recode your GLApplet class, so it is using active rendering; gimme10mins :p
urg, i just looked at the other classes, and the way they are tied together :-/its gettin late, i'll recode it 2morrow :D
haha. Well thanks a ton for looking at it! I can't wait to see what you do with it
Maybe its just me, but I would suggest against the bit-shifting suggestion made by one of the posters above. More than likely, the compiler will pick up on the optimization for you , and even if it doesn't, I don't believe the switch would be worth the loss of readibility. Again, probably a personal thing, but I dislike seeing bitshifts =/.
Another quick one re: shifting stuff...
I'd also pull out your subexpression into a temp variable, so that:
oofG2D.drawString(string, ((getWidth()-offset)>>1)-1, y-1);
oofG2D.drawString(string, ((getWidth()-offset)>>1)+1, y-1);
oofG2D.drawString(string, ((getWidth()-offset)>>1)-1, y+1);
oofG2D.drawString(string, ((getWidth()-offset)>>1)+1, y+1);
becomes: int iXPos = getWidth()-offset)>>1;
oofG2D.drawString(string, (iXPos-1, y-1);
oofG2D.drawString(string, (iXPos+1, y-1);
oofG2D.drawString(string, (iXPos-1, y+1);
oofG2D.drawString(string, (iXPos+1, y+1);
Any optimizer might perform that optimization for you,
but I always figure it can just as easily optimize the temp
variable into a register, and if the optimizer doesn't do it,
then you've knocked out a few excess method calls
and duplicatecalculations anyway.
I totally agree. I don't know why I hadn't done it already. That sort of thing makes for easier changes anyway. Thanks for pointing that out.
> I removed my background and tried drawing my game with
> using:
>
> g2d.setColor(Color.black);
> g2d.fillRect(0, 0, getWidth(), getHeight());
>
> every drawing call, and it flickers like mad. Either
> that means fillRect is a lot slower that drawing a
> BufferedImage, or my double buffering isn't really
> double buffering.
The double buffering is not working, because you are calling g2d(the applet's Graphics) instead of offG2D(the offscreen Graphics), then all your callings are directly drawn to the screen..
Rafael
g2d is the Graphics2D object for offImage. offImageG2D does not exist. The only call I use with the applet's graphics is:g.drawImage(offImage, 0, 0, null);