Skip to main content
1 of 4
Justin
  • 3.3k
  • 1
  • 21
  • 39

As strange as it sounds, the performance drain really is on this one line of code right here:

g2d.drawImage(background, 0, 0, this);

The reason is that background is a rather large image, and the g2d is performing a scale and a rotate every time. If you enabled OpenGL via -Dsun.java2d.opengl=True as suggested here might help, but I haven't been able to enable it on my machine.

Do note as well that it may be worth just using an existing game engine.

That said, the solution to the problem is to take the work out of the paintComponent method. If you never will view the background from a different angle, just draw it at this tilt to begin with. Otherwise, you need yet another thread. So there would be three threads:

  1. The GUI thread
  2. Your game loop thread
  3. The rendering thread

In a proof-of-concept style, I added another BufferedImage drawnBackground as a field, and I added this to the end of your Main constructor:

new Thread(() -> {
    while(true) {
        if (background == null) continue;
        // A new BufferedImage to avoid concurrency issues; in this way,
        // the reference to this.drawnBackground is always a valid image
        // to be drawn. If we reassign this.drawnBackground = drawnBackground
        // while paintComponent() is running, that's fine, as it will simply
        // draw the old image.
        BufferedImage drawnBackground = new BufferedImage(background.getWidth(), background.getHeight(), background.getType());
        Graphics2D g2d = (Graphics2D) drawnBackground.getGraphics();

        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, background.getWidth(), background.getHeight());
        float xscale = (float) SCREENWIDTH / WIDTH;
        float yscale = (float) SCREENHEIGHT / HEIGHT;
        g2d.scale(xscale, yscale);
        g2d.rotate(Math.toRadians(45), WIDTH / 2, HEIGHT / 2);
        g2d.drawImage(background, 0, 0, this);

        this.drawnBackground = drawnBackground;
    }
}).start();

The paintComponent method changes to look like so:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    AffineTransform plain = g2d.getTransform();

    float xscale = (float)SCREENWIDTH/WIDTH;
    float yscale = (float)SCREENHEIGHT/HEIGHT;

    g2d.drawImage(drawnBackground, 0, 0, this);

    g2d.scale(xscale, yscale);
    g2d.rotate(Math.toRadians(45), WIDTH/2, HEIGHT/2);

    AffineTransform normal = g2d.getTransform();
    // g2d.drawImage(background, 0, 0, this);
    // rest of code

In this way, we move the expensive operation of rendering the image to another thread. Note that that other thread will still be getting around 30 fps instead of 60 fps, but the game itself will be running at a better framerate. In other words, the background image will be updating at closer to 30 fps, while everything else will be faster.

Justin
  • 3.3k
  • 1
  • 21
  • 39