Rotating images, fast?

Hey,

So I’m revisiting the tower defense game I started making some time back, and decided to give it another whirl in Java2D, while waiting for LWJGL to hit 1.9.0 and be usable on OS X with Java 7.

The reason I stopped working on it, was that the rendering became horribly slow after placing ~110 towers. This time around I did a little digging and found out that it’s because I rotate my tower images that it starts to get slow. I rotate them, so that they’re always facing the nearest enemy they can reach.

I use the following code to render each tower:


public void render(Graphics2D g2d) {
	// Turret base shadow
	g2d.drawImage(this.actor.getImage(1), this.pos.x - 16 + 1, this.pos.y - 16 + 1, null);

	// Turret base
	g2d.drawImage(this.actor.getImage(0), this.pos.x - 16, this.pos.y - 16, null);

	AffineTransform tx = new AffineTransform();
	tx.rotate(this.rotation, 16, 16);
	AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);

	// Turret shadow
	g2d.drawImage(op.filter(this.actor.getImage(5), null), this.pos.x - 16 + 2, this.pos.y - 16 + 2, null);

	// Turret
	g2d.drawImage(op.filter(this.actor.getImage(this.towerSprite), null), this.pos.x - 16, this.pos.y - 16, null);
}

When rendering 166 towers that way(Each tower renders itself, btw), the fps has gone from 60 to ~34, when I remove the rotating of the images, then it has no problem rendering the towers at 60fps.

Soo, golden question, how can I optimize this? :slight_smile:

Disable bilinear filtering.

EDIT:
You’re currently creating a rotated version of the image instance each time you render. It’s incredibly inefficient. Instead, just call g2d.rotate(rotation). It’ll be much faster to draw the original image rotated than to create a rotated version of the image and then drawing it (note the difference).

What should I use instead? AffineTransformOp.TYPE_NEAREST_NEIGHBOR makes the images all grainy. :confused:

I personally don’t use any AffineTransformOp, and it looks fine.

A trick you could use is to make a spritesheet of the turret image rotated in 10’ steps and just draw the one closest to the true rotation value. (You could alternatively generate 36 rotated sprites at runtime and use them in the same way). Not perfect, but very fast!

Then what do you use?

I was considering that, if there isn’t any faster way to rotate it “live.”

I just rotate it by the amount i need, draw the image, and rotate it back.
Sorta like this.


public void draw(Graphics2D g){
     g.rotate(theta, x, y);
     g.drawImage(x,y);
     g.rotate(-theta,x,y);
}

The g.rotate is the fastest other then probably doing the sprite sheet. I can confirm that almost all computers will be able to do at least 2k objects changing rotation every frame at 60fps. So I do not think rotation is going to be a huge bottle neck in a TD game unless you have like a biggilion towers which would be silly.

Set RenderingHints on the graphics object.

I recommend setting them in the main graphics object passed around at the beginning of each render. If you find my old post that was a menu demo in java2D, there should also be a source link that shows how I did the rendering which I found to be fast but Java2D unreliable.

Edit: here. Look at the Window class

public void render( Graphics g )
	{
		Graphics2D g2d = (Graphics2D)g.create();
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) (fade)));
        g2d.setTransform(AffineTransform.getRotateInstance(rotation.x, loc.x, loc.y));//replace this bad line with the g.rotate
		if(image != null)
		{
			if(alive)//bad coding mixing logic and rendering bad bad
			{
				int x1 = (int) (loc.x - (size/2)); 
				int y1 = (int) (loc.y - (size/2));
				g2d.drawImage(image, x1, y1, (int)size, (int)size, null);
			}
		}
		else if(alive)//bad bad bad
		{
			int x1 = (int)size >> 1;
            int y1 = (int)size >> 1;
			g2d.setColor(color);
			g2d.fillRect((int)(loc.x - x1), (int)(loc.y - y1), (int)size, (int)size);
		}
		g2d.dispose();
	}

I’m currently failing hard in getting that to work as intended xD I should probably give it another try, when I’m less tired.

Alright, I got it working now. :slight_smile: But it’s still making jagged edges on my beautiful circles… :confused: But at least I can render lots more towers now, without it starting to drop fps, it’s steady around 60 where it would’ve been ~25 with the above code.

If you are rendering primitives, then turn on anti aliasing. If you are not, then try bluring the edges of your tower sprites slightly as they will help with jagged edges.

I’ll try and blur the edges on my sprites and see how that looks. :slight_smile:

And I think the performance now is good enough that it will be able to handle all the towers that I can throw at it(which is rather limited, because of the map and such)

Next up is implementing the A* algorithm I made for my AI class into this game so the mobs can actually do things, like moving around obstacles. :stuck_out_tongue:

I’ve actually got 1 last question.

I’m drawing both a shadow and the turret, but when rotating the image, the shadow get’s offset wrong.

How do I translate my (x, y) coords, so that they match the new rotated image? (Such that the shadow stays in the same position, but get’s rotated around (x, y))

Example of how it looks now:

I want the shadow to stay underneath the tower, offset down and to the right a few pixels, not rotating around as it’s doing right now.

When you render images, I always have it so the location in the world (x,y) is the center of the sprite. If you do that, and then call g.rotate() with the x,y then you will not have that issue.

So if my sprite is 32x64 at 256,432

I will take 256-32/2 and 432-64/2 and draw with the width 32 and height 64. Then have the shadow and main sprite at the same location. If the shadow needs and offset, add it to the loc before rendering.

I’m doing this:

		Graphics2D g2d = (Graphics2D) g.create();
		// Rotate
		g2d.rotate(this.rotation, this.pos.x, this.pos.y);

		// Turret shadow
		g2d.drawImage(this.actor.getImage(5), this.pos.x - 16 - 2, this.pos.y - 16 - 2, null);

		// Turret
		g2d.drawImage(this.actor.getImage(this.towerSprite), this.pos.x - 16, this.pos.y - 16, null);
		g2d.dispose();

the pos.x and pos.y coords is the center of the sprite, and the Turret itself behaves nicely, but the shadow does as you can see in the image :confused:

what is the size of your tower and shadow sprites? are they the same? I would almost always give the width and height even if they are the same. Scaling is rather cheap in java2D and basically free in opengl.

I nice way to draw things I think is to have the width and height already know in a class like sprite so you don’t have to use magic constants. This will also allow you to easily change the size without have to do any extra coding.

Ooh! ooh! I know this one! when you call g.rotate(…);, you are providing the coordinates for the origin as the top right corner of the tower. You need to add half of the width to the x position and half of the height to the y position. So it would look like:


g.rotate(this.rotation, this.pos.x + (this.actor.getImage(....).getWidth() / 2), this.pos.y + (this.actor.getImage(.....).getHeight() / 2)); 

Uhm, I’m not sure I get what you guys are saying. xD

The tower and shadow sprite are the same size, 32x32 pixels. And as you can see, then the tower is rendering in the right spot, but the shadow isn’t, and the only difference is the “-2” on each axis of the shadow. :confused:

(In the picture it was something like -5 to show how odd it behaves. If I don’t use any offset, then it’s rendered correctly, right underneath the tower)

I can only see two solutions. Maybe there is a better one.

a) rotate based on the coordinates of the shadow, draw shadow, then rotate on the coordinates of the turret and draw the turret.

b) work out the trig to compensate for the angle of the rotation and add it to the draw position of the shadow (hurts my head at this late hour, but I know it is solvable). Don’t know if it is faster or slower doing a bit of trig compared to a second rotate.

However, none of this matters if you are managing a pair of good sprite sheets for the turret and shadow.

Don’t apply the same transform to both your turret and its shadow. Use a different transformation matrix to transform your shadow.