Search code examples
javaimagebufferedimagejavax.imageio

Java load image to class - most efficient practice?


I am writing a 2D, tile-based game in Java. Characters are to be placed on the tiles according to the game logic. Each Character is associated with a picture that would be drawn to the screen using java.awt.Graphics2D. However, there could be more than one instance of each character on the map.

What is the recommended practice in storing and accessing the images of the characters?

There are three possible implementations I could think of:

  1. Having each Character assigned a private variable of type java.awt.image.BufferedImage. Each time the object needs to be drawn, the image is accessed using a getter. However, would this cause the size of the object to be excessively large?
  2. Having another utility Class to store all of the images in a static variable. Each time the character needs to be drawn, the image can be called by passing an id/name to a static getter method in the utility Class. Would this be more efficient or would it be rather slower than the previous method?
  3. Simply load the picture each time the character needs to be drawn. This would provoke ImageIO.read(new File(character.getImagePath()) every time the character needs to be drawn. Could this be the fastest?

Solution

  • I have read a number of articles on how to optimize games, and I've seen that loading the needed resources at the start and storing them for later use is very important. Some of the longest operations are found in file I/O code. There is even a chance that some exception occurs. For instance, in your 3rd option, what would happen if ImageIO throws an IOException? You wouldn't have an image, and you'd have to render some other thing. It could equally cripple the user's gaming experience if the image reading takes longer than expected.

    Here's an example snippet for pre-loading a resource (in your .jar):

    // Imports and other stuff...
    
    // Parameter flocation: resource location
    private BufferedImage preloadImage(String flocation){
        BufferedImage bufimg = null;
        System.out.println("Attempting to load image: '" + flocation + "'");
    
        try {
            bufimg = ImageIO.read(this.getClass().getResourceAsStream(flocation));
        } catch(IOException e){
            System.err.println(
                    "An error occurred while attempting to load '" + flocation + "'");
    
            return null;
        }
    
        // Now store this somewhere safe!
        return bufimg;
    }
    

    For your 1st and 2nd options, there really isn't much of a difference if you think about it. The first just adds a field to each character, while the other one manages all the character images for you. Personally, I prefer the 1st option because it reduces the code clutter, increasing readability. Of course you might decide to group your code based on themes, so the 2nd option is totally fine.