2D racing game

hey guys,

I am doing right now my very first java game. it is a 2d racing game.

I want to implement a map-editor where I can create new racing tracks. Therefor I´ve got some quadratic images with the parts of the track.
The problem is that I use a double for - loop to draw the squares (by calling the paintComponent - Method) and I want to call it only once, but I´ve everytime when I call the repaint() -method (in an other class) it gets repaintet as well which makes my game quite slow… :frowning:

So what can I do to draw the track only once ?
and is there a better way to bulid up the track, without using the double for -loop?

I hope you got what I mean :slight_smile:

thanks for your help

best regards

these are the two classes :

public class Car extends JPanel implements Runnable {

private static final long serialVersionUID = 007;
private BufferedImage car = null;
private float x = 100F, y = 100F;
private Thread driveThread = new Thread(this);
private double currentAngle = 0; // angel of the car
private static int[] key = new int[256]; // keyboard input
private float MAX_SPEED = 7F;
private float speed = 0F; // speed of our racing car
private float acceleration = 0.15F;
private int player;


public Car(int player) {

	this.player = player;

	this.setSize(super.getHeight(), super.getWidth());
	this.setFocusable(true); // enables keyboard

	
	try {
		if (player == 1) {
			//red car
			car = ImageIO.read(this.getClass().getResource(
					"ressources/car1.png"));
			
			System.out.println(car.getColorModel());
		} else if(player == 2){
			//blue car
			car = ImageIO.read(this.getClass().getResource(
					"ressources/car2.png"));
			x = x +30;
		}

// background = ImageIO.read(this.getClass().getResource(“ressources/level1.png”));

	} catch (IOException e) {
		System.out.println("dupi");
	}

	// starts the drive thread
	startGame();

}

private void startGame() {
	// TODO Auto-generated method stub
	driveThread.start();
}

@Override
protected void paintComponent(Graphics g) {

	// super.paintComponent(g);
	this.setOpaque(false);

	// rotation 
	Graphics2D g2d = (Graphics2D) g;
	AffineTransform rot = g2d.getTransform();
	// Rotation at the center of the car
	float xRot = x + 12.5F;
	float yRot = y + 20F;
	rot.rotate(Math.toRadians(currentAngle), xRot, yRot);
	g2d.setTransform(rot);
	//Draws the cars new position and angle
	g2d.drawImage(car, (int) x, (int) y, 25, 40, this);
	
}

protected void calculateCarPosition() {
	
	//calculates the new X and Y - coordinates 
	x += Math.sin(currentAngle * Math.PI / 180) * speed * 0.5;
	y += Math.cos(currentAngle * Math.PI / 180) * -speed * 0.5;

}

protected void carMovement() {

	// Player One Key's
	if (player == 1) {

		if (key[KeyEvent.VK_LEFT] != 0) {
			currentAngle-=2;

		} else if (key[KeyEvent.VK_RIGHT] != 0) {
			currentAngle+=2;
		}

		if (key[KeyEvent.VK_UP] != 0) {

			if (speed < MAX_SPEED) {

				speed += acceleration;
			}

		} else if (key[KeyEvent.VK_DOWN] != 0 && speed > -1) {
			speed = speed - 0.1F;
		}
		speed = speed * 0.99F;

	} else {

		//Player Two Key's
		
		if (key[KeyEvent.VK_A] != 0) {
			currentAngle -= 2;

		} else if (key[KeyEvent.VK_D] != 0) {
			currentAngle += 2;
		}

		if (key[KeyEvent.VK_W] != 0) {

			if (speed < MAX_SPEED) {

				speed += acceleration;
			}

		} else if (key[KeyEvent.VK_S] != 0 && speed > -1) {
			speed = speed - 0.1F;
		}
		//reduce speed when no key is pressed
		speed = speed * 0.99F;
	}

}

public void getUnderground() {

}
// get key events!
final protected void processKeyEvent(KeyEvent e) {
	key[e.getKeyCode()] = e.getID() & 1;
}

@Override
public void run() {
	while (true) {

		repaint();
		carMovement();
		calculateCarPosition();
	
		
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

}


public class Circuit extends JPanel {

private static final long serialVersionUID = 1;
private BufferedImage[] street = new BufferedImage[7];
private int[][] circuitIndex = new int[10][10];
private boolean built = false;

public Circuit() {

	setSize(1000, 1000);

	try {
		street[0] = ImageIO.read(this.getClass().getResource(
				"ressources/gw.png"));
		street[1] = ImageIO.read(this.getClass().getResource(
				"ressources/gs.png"));
		street[2] = ImageIO.read(this.getClass().getResource(
				"ressources/ur.png"));
		street[3] = ImageIO.read(this.getClass().getResource(
				"ressources/ul.png"));
		street[4] = ImageIO.read(this.getClass().getResource(
				"ressources/ol.png"));
		street[5] = ImageIO.read(this.getClass().getResource(
				"ressources/or.png"));

	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

	// TODO: read from textfile to fill the index

	circuitIndex[0][0] = 2;
	circuitIndex[0][1] = 0;
	circuitIndex[0][2] = 3;
	circuitIndex[0][3] = 6;
	circuitIndex[0][4] = 6;

	circuitIndex[1][0] = 1;
	circuitIndex[1][1] = 6;
	circuitIndex[1][2] = 5;
	circuitIndex[1][3] = 3;
	circuitIndex[1][4] = 6;

	circuitIndex[2][0] = 5;
	circuitIndex[2][1] = 0;
	circuitIndex[2][2] = 0;
	circuitIndex[2][3] = 4;
	circuitIndex[2][4] = 6;

	circuitIndex[3][0] = 6;
	circuitIndex[3][1] = 6;
	circuitIndex[3][2] = 6;
	circuitIndex[3][3] = 6;
	circuitIndex[3][4] = 6;

	circuitIndex[4][0] = 6;
	circuitIndex[4][1] = 6;
	circuitIndex[4][2] = 6;
	circuitIndex[4][3] = 6;
	circuitIndex[4][4] = 6;

}

@Override
protected void paintComponent(Graphics g) {
	// TODO Auto-generated method stub
	super.paintComponent(g);
	Graphics2D g2d = (Graphics2D) g;
	for (int i = 0; i <= 4; i++) {
		for (int j = 0; j <= 4; j++) {

			if (circuitIndex[j][i] == 0) {
				g2d.drawImage(street[0], i * 200, j * 200, this);
			} else if (circuitIndex[j][i] == 1) {
				g2d.drawImage(street[1], i * 200, j * 200, this);
			} else if (circuitIndex[j][i] == 2) {
				g2d.drawImage(street[2], i * 200, j * 200, this);
			} else if (circuitIndex[j][i] == 3) {
				g2d.drawImage(street[3], i * 200, j * 200, this);
			} else if (circuitIndex[j][i] == 4) {
				g2d.drawImage(street[4], i * 200, j * 200, this);
			} else if (circuitIndex[j][i] == 5) {
				g2d.drawImage(street[5], i * 200, j * 200, this);
			} else if (circuitIndex[j][i] == 0) {
				g2d.drawImage(street[1], i * 200, j * 200, this);
			}

		}
	}
}

}

Why not have a big (1000x1000 pixel) BufferedImage for the whole track? Draw your street tiles onto it at startup, then when repaint() is called you need only draw the image (or faster still, only those parts of the image which had cars on them last frame).

I’d just make sure to draw only the tiles that are visible. Drawing in Java2D should easily be fast enough to handle that every frame.
Assuming the track will scroll, you’ll have to repaint the whole screen anyway so I don’t think doing some kind of ‘dirty rectangles’ algorithm will help much, if any.

I don’t think the double for-loop really slows things down, it might be that your image drawing is not accellerated somehow. I’d advise looking at BufferStrategy and active rendering.
What might also help performance is to have your tiles in power-of-two dimensions, 32x32 or 64x64 or whatever, but try to convert your game to use BufferStrategy and active rendering first and see how that goes.

Your rendering is quite the poor manner, I apologize for it. This is passive rendering, that is delegation to Swing EDT for the refresh to proceed. I can suggest you to use another rendering API that has some enhancements on active rendering. I’ve built one on my own, which is known as the “sf3jswing project”. It directly renders to the Canvas which is activated on runtime right after to be added in a container. I named it the RenderingScene class. Today I tested it for up to 60hz refresh period. Here it is : http://www.developpez.net/forums/showpost.php?p=2639576&postcount=80 and the latest source code is there : http://sf3jswing.cvs.sourceforge.net/sf3jswing/AnimationDemo3/src/net/library/jiga/sf3/game/RenderingScene.java?hideattic=0&view=log
I’ve also considered LWJGL API as a good introduction to gaming active rendering.

thanks guys for your tips

i tried to use BufferStrategy but i didn’t get it how it works
I always get an IllegalStateException: Component must have a valid peer

here is the code again, so please tell me what the mistake is :slight_smile:

thanks for your hepl :slight_smile:

plz, provide java console output and line (viewable in Java console) that cause this error, than finding your error will be immediate

public Race() {
      super();
      initialize();
      player1.startCar();
   }

   private void initialize() {
      this.setSize(new Dimension(WIDTH, HEIGTH));
      this.setContentPane(getJContentPane());
      this.setTitle("JRace 2008");
      jContentPane.setVisible(true);
      jCars.setVisible(true);
      
   }

does’nt it come from one of those lines :

jContentPane.setVisible(true);
jCars.setVisible(true);

or maybe this one:

createBufferStrategy(2);

here is the error msg:

Exception in thread “Thread-2” java.lang.IllegalStateException: Component must have a valid peer
at java.awt.Component$FlipBufferStrategy.createBuffers(Unknown Source)
at java.awt.Component$FlipBufferStrategy.(Unknown Source)
at java.awt.Component$FlipSubRegionBufferStrategy.(Unknown Source)
at java.awt.Component.createBufferStrategy(Unknown Source)
at java.awt.Canvas.createBufferStrategy(Unknown Source)
at java.awt.Component.createBufferStrategy(Unknown Source)
at java.awt.Canvas.createBufferStrategy(Unknown Source)
at at.fhjoanneum.ima.prog2.javarace.RacingCars.createStrategy(RacingCars.java:60)
at at.fhjoanneum.ima.prog2.javarace.RacingCars.run(RacingCars.java:175)
at java.lang.Thread.run(Unknown Source)

the addNotify()-method was missing…

I would also need a transparent background … is this possible by using canvas ?

previous quoted error is thrown by the AWT EDT when you add the canvas as the canvas-component is not a child of any window or frame.