Search code examples
javaawtresolution

What is the most efficient way of rendering 2D game with the possibility of changing the resolution?


I've tried:

1.Creating a separate variable called "factor" and multiplying or dividing literally everything with it: entity velocities, object sizes, fonts, resolution etc.. (the factor is always relative to the resolution so the objects are scaled properly)

public class Player extends Entity{

    float size;

    public Player(needed variables) {
        super(needed variables);
        resize();
    }

    public void resize() {
        /*
        Resize everything.
        This method is supposed to be called from a separate resizing
        function located in another class when the JFrame size is changed.

        the function has to play with the choice between divide or multiply
        variables with the factor
        */
    }

    public void tick() {
        x += velX*factor;
        y += velY*factor;
    }

    etc..

}

By using this factor to multiply literally everything, it makes the code really messy and hard to read sometimes.

2.Rendering to a BufferedImage and scaling the BufferedImage to fit to the JFrame.

void render() {
    //Render the game to a new BufferedImage
    BufferedImage renderedFrame = new BufferedImage(1920, 1080, BufferedImage.TYPE_RGB);
    renderedFrame.createGraphics();
    Graphics g = renderedFrame.getGraphics();

    //Render the game ....

    //Scale the BufferedImage to fit the current resolution and render it to the Canvas
    BufferStrategy bs = getBufferStrategy();
    Graphics f = bs.getDrawGraphics();
    f.drawImage(renderedFrame.getScaledInstance(1280, 720, Image.SCALE_FAST), 0, 0, null);

    f.dispose();
    bs.show();
}

Which makes the code much more readable but then there comes 2 problems:

Mouse input problems and resizing the BufferedImage is taking too much resources which makes the game laggy.

3.I could basically try to make a separate unit system for the game.. but then there's the same problem, when it comes to rendering strings or rectangles I'd have to multiply everything with the factor and the code is horrible after that.

Is there any better ways of rendering 2D games? If no then I'll think about moving on to OpenGL.

Thanks in advance.


Solution

  • The way I've done this most successfully is by scaling the graphics object. You end up with something like the following:

    final int gameUnitsPerScreenDim = 32;
    
    void render(JPanel panel, Graphics2D g2) {
        double pixelHeight = panel.getHeight();
        double pixelsPerGameUnit = pixelHeight / gameUnitsPerScreenDim;
    
        g2.scale(pixelsPerGameUnit, pixelsPerGameUnit);
    
        ...
    }
    

    And then for the simulation, you use game units. How big a game unit actually is is a bit arbitrary, although if you're making a tiled game there's probably some obvious value that it should be.

    Instead of using scale, you can also create an AffineTransform which lets you reuse it:

    if (this.tf == null || /* image size changed since the last frame */) {
        ...
        this.tf = AffineTransform.getScaleInstance(pxPerGu, pxPerGu);
    }
    g2.setTransform(this.tf);
    

    (Calling scale creates a new AffineTransform every time you call it.)

    That's even a little more efficient, although probably not by much.

    (If you want, you can also use a transform to invert the y-axis and translate so the origin is at the center of the image. This makes a lot of trigonometry and stuff feel more natural. Inverting the y-axis makes working with text a pain, though.)

    Also, using OpenGL is probably better. Having written a couple of simple games using Swing for fun, I don't see a good reason to do it.