Why is my paint method so slow?

I'm only getting about 50 fps when I use this paint() method. Why is it so slow? Eventually I need to add another loop to the draw() method that will draw all of the transition tiles, but that would lower the fps to 25!

//located in main game loop

publicvoid paint(){

Graphics g = bufferStrategy.getDrawGraphics();

g.setColor(Color.black);

g.fillRect(0, 0, WIDTH, HEIGHT);

//draw map (see below)

map.draw(g);

//draw game status if not 'normal'

if (status != NORMAL){

g.setFont(new Font("serif", Font.BOLD, 32));

g.setColor(Color.white);

g.drawString(statusMessage, (WIDTH - g.getFontMetrics()

.stringWidth(statusMessage)) / 2, 200);

}

bufferStrategy.show();

}

//located in map class

publicvoid draw(Graphics g){

//draw each tile (on a 15 x 15 map, 225 tiles total)

for (int i = 0; i < 15; i++){

for (int j = 0; j < 15; j++){

//retrieves the sprite from the HashMap 'sprite'--the key is the sprite id

//which is stored in the byte array 'data' (which has length = 225)

Sprite sprite = (Sprite)sprites.get(new Integer(data[j + i * 15]));

sprite.draw(g, j * 32, i * 32);

}

}

}

[2133 byte] By [jamdr] at [2007-9-30 22:13:29]
# 1

Presumably status is not "normal" when you're drawing.That's only for debugging, at which point you don't care about the speed, right?

If the data you're looking up is strictly ordered like that, indexed by an integer, then using a HashMap probably isn't worthwhile.You're creating objects, invoking their hashCode methods (which, granted, for Integer is probably just returning itself), then doing some kind of an array lookup and then a series of comparisons, depending on how they've implemented HashMap. Just use an array.

But I'll never understand this obsession with getting framerates higher than humans are capable of perceiving.

paulcw at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 2

Thanks for the reply.

Presumably status is not "normal" when you're drawing. That's only for debugging, at which point you don't care about the speed, right?

Well, status is either 0, 1, or 2, corresponding to normal, loading, or paused. I guess I should probably figure out a different way of doing this, though, so I don't have to check during each iteration of the loop.

Just use an array.

I had the same thought. I don't like having to create a new object just to get something out a hashmap. The problem is that the indexes are necessarily in any particular order, and could be over a relatively large range. There may be some way around this, though.

I'll never understand this obsession with getting framerates higher than humans are capable of perceiving

The thing is, this is just the beginning of my game--I haven't implemented drawing anything else yet--just the background map. So if I'm only getting 25-50 fps with just drawing the background, I suspect it will probably drop below an acceptable level when I start drawing characters, items, etc.

jamdr at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 3

> Well, status is either 0, 1, or 2, corresponding to

> normal, loading, or paused.

I brought it up because you're allocating a Font object and invoking stringWidth each time paint() is invoked. I'm not sure but I suspect that Font objects are relatively heavy and stringWidth is probably pretty intensive too.

> The problem is that the indexes are necessarily in any

> particular order,

Eh? The order isn't necessarily relevant. If you have a corresondence from an integer index to whatever you're looking up, an array should work.

> and could be over a relatively large

> range. There may be some way around this, though.

Do a literature search on "sparse arrays".

But, unless I'm reading this wrong, the array is only going to have ((15 * 15) + 15) values, which isn't very much.

paulcw at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 4

From a glance view, I see only this thing that overkill:

Sprite sprite = (Sprite)sprites.get(new Integer(data[j + i * 15]));

You create 15x15=225 NEW Integer object every loop and also getting object from HashMap.

I'm not quite sure about getting object from HashMap performance,

but creating new object on tight loop is really really bad.

For the font drawing, I think there's nothing wrong on it, also it's only happen sometimes (only when status is not normal, right!?)

But the draw method is executed each loop, in each loop it creates 225 new object plus it getting object from HashMap 225 times.

Object creation is expensive, and the whole 225 object also needed to be garbaged each time, the garbage collector makes your game even slower (you can see what I mean by running the game with -verbose:gc)

Anyway if you are really on the beginning of making your game, you could try my game engine, here is the link: http://www.goldenstudios.or.id/

You can convert your game to use the engine easily, cos the game engine is only setup the basic things, also everything is using interface, so you able to make your own implementation later (if you want to).

The main point is: the game engine also provide advance class such as the tile background you make above, that's why I recommend it. :-)

javaweird at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 5

It doesn't make any sense to use a HashMap for an Array of tiles.

for(int x=0;x<13;x++)

for(int y=0;y<29;y++)

if(map[x+y*13]!=-1)

g.drawImage(brick[map[x+y*13]],FOX+x*32,FOY+y*16,null);

That's easy and **** fast.

oNyx at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 6
Taking a guess here. Isn't it relatively slow to be using drawString? Wouldn't it be better to use image-based fonts?
Eliwood at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 7

Well, it can make sense to use a Map to implement a (virtual, basically) sparse array. For example if you have a dataset where positions 1 and 1,000,000,0001 have values and everything else is null, then the OP's solution makes sense.

But if every position has a value, and there are only about 300 of them, then just use a plain old array.

paulcw at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 8

>Do a literature search on "sparse arrays".

I looked this up and it sounds like exactly what I want. However, how would I implement this in Java? As far as I know, Java automatically initializes int arrays to 0. So if I have the index values 1, 142, 1205, everything in between would still be 0 and a lot of space would be wasted.

>creating new object on tight loop is really really bad.

Of course! No wonder it was so slow. Another game programmer told me this once, I just forgot. Apparently this is one of the top ten things to never do in a game.

>Well, it can make sense to use a Map to implement a (virtual, basically) sparse array. For example if

>you have a dataset where positions 1 and 1,000,000,0001 have values and everything else is null,

>the OP's solution makes sense.

Yes! This is what I'm doing, I just didn't know the name for it. I don't have a billion elements, but I could have thousands. And only certain ones have values. But as I said earlier, I don't think Java has native support for sparse arrays, so how should I do this?

>you could try my game engine, here is the link: http://www.goldenstudios.or.id/

Thanks. I would like to spend a few more days working on this, but I may end up doing just that.

jamdr at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 9

What you've already done is a way to handle sparse arrays. It may be a naive implementation, but honestly I haven't worked with them enough to know. But that's the name of what you're trying to do. The literature search will probably turn up some basic algorithms that you could adapt to Java. Also I'll bet someone already created a Java library for this. It's worth reading the theory to see if your case really applies.

But note that it isn't a sparse array if you're getting values for every single index. So if you use a sparse array design for something that isn't so sparse, you're just going to be wasting cycles and slowing down performance.

What's the percentage of elements that have values? If it's not low enough, a sparse arrays design may not be worth it. If it is worth it, then you could save space by using a default, like this:

public Sprite getSprite(int x) {

// each position in outer array specifies a range of values of length innerArray.length

Sprite[] inner = outerArray[x / INNERARRAY_LENGTH];

if (inner != null) {

// note: only expect about 5% of outer array elements to contain anything

Sprite returnme = inner[x % INNERARRAY_LENGTH];

if (returnme != null)

return returnme;

}

return DEFAULT_SPRITE;

}

But this leads to another question -- are you planning to draw every sprite in the sparse array?

Maybe you need larger sprites... Or are you going to add some view code later to only select sprites that are appropriate?

Another example would be to keep the solution you have, but don't create a "value-less" value or default value, which I'm guessing you're doing since you're looping through every index. Instead, just print stuff for which there's a value, as in:

public void draw(Graphics g) {

// draw the background, e.g., a solid color or a selection from a large image Just one datum here.

// this is the "value-less" value.

// Now print ONLY the stuff in your sparse array:

Iterator i = sprites.values().iterator();

while(i.hasNext())

((Sprite) i.next()).draw(g);

}

So rather than looking up every index, you're only drawing values that you know exist.

Even thousands of array positions may not be so much that it's worth using any design other than a regular array. Have you tried just using an array and seeing if you got any memory errors or the like?

paulcw at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 10

Thanks for that last suggestion paulcw. I am definitely working with a sparse array--not every element has a value. The percentage of value-less elements could be almost anything, depending on the current game world. However, it is likely that the array won't be too sparse--most elements (> 50%) probably will have values. So I took your second suggestion, used a standard array, and had my draw() method check to make sure the element is not zero before getting the sprite. Now my maps are drawn at over 100fps! Thanks so much for your help! I'm sure I'll have other questions shortly...

jamdr at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...
# 11

>Isn't it relatively slow to be using drawString? Wouldn't it be better to use image-based fonts?

Yes, drawString is "slow" or to be more precise it's slower than sprite fonts. However, usually it doesn't matter, because the amount of text is too small. It's really unlikely that that would be a bottleneck. (You can draw more than 15k lines of text per second with drawString on 2-3 year old PCs.)

@ jamdr

You can also create a copy of the orgininal map (which points to the new indices). This way you can remove the check from the loop and you also can use a simple array then.

oNyx at 2007-7-7 11:26:38 > top of Java-index,Other Topics,Java Game Development...