-1

I am attempting to draw sprites out of a sprite sheet.

I have the following class

public class GTComponent extends JComponent {

    Graphics2D g2;

    @Override
    public void paintComponent(Graphics g){
        g2 = (Graphics2D)g;
    }

    public void drawSpriteFrame(Image source, int x, int y, int frame) {
        int frameX = (frame % 12) * 32;
        int frameY = (frame / 12) * 32;
        g2.drawImage(source, x, y, x + 32, y + 32,
                frameX, frameY, frameX + 32, frameY + 32, this);
    }
}

That is created as an object in the main class as so

    JFrame f = new JFrame();
    GTComponent img = new GTComponent();

    f.add(img);

    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setSize((int)(i.length * 8.1), (int)(i[0].length * 8.5));
    f.setVisible(true);
    f.setLocationRelativeTo(null);

    BufferedImage test = null;
    try {
        test = ImageIO.read(new File( /*Image File path*/));
    }
    catch (IOException e) {
        System.out.println("error");
        System.exit(0);
    }

    img.drawSpriteFrame(test, (u * 32 + 1), (z * 32 + 1), c);

The problem im facing is that the following error gets thrown

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException

After doing several debugs, setting breakpoints at paintComponent and drawSpriteFrame, i found out that the drawSpriteFrame method gets called before the paintComponent method, thus meaning that g2 = null resulting in that error being thrown.

The question here is what triggers the paintComponent method which allows me to initialise the g2 variable?

David Moles
  • 48,006
  • 27
  • 136
  • 235
DarkDestry
  • 183
  • 1
  • 11

2 Answers2

3

You seem to have a broad misconception how drawing in Swing works.

You do not call any rendering methods when you want to. You perform rendering when Swing demands it. When Swing calls paintComponent() thats where you perform all your rendering. The graphics passed to paintComponent should be treated as valid only while you're still in the paintComponent method. What happens to it after the method exits is Swings buisness.

You might want to consult the tutorial on Swing custom painting http://docs.oracle.com/javase/tutorial/uiswing/painting/index.html for example code.

Durandal
  • 19,919
  • 4
  • 36
  • 70
1

paintComponent() is called automatically from the event dispatch thread. If you want your custom component to be painted as part of the ordinary Swing painting process, you should override paintComponent() to call your drawSpriteFrame() method, not call drawSpriteFrame() directly.

If you want to control the drawing operation yourself, you need to use "active rendering" as described in the Full-Screen Exclusive Mode tutorial -- note that the technique described there also works for windowed applications. Basically you need to ask the window for a Graphics instance (instead of waiting for one to be passed into paintComponent() and then draw to that.

A basic example using double buffering:

// Initial setup
Frame mainFrame = new Frame();
mainFrame.setVisible(true); // you'll also want to set size, location, etc.
mainFrame.createBufferStrategy(2);
BufferStrategy bufferStrategy = mainFrame.getBufferStrategy();

//.... 

// Inside your draw loop (call once for each frame)
Graphics2D g2 = (Graphics2D) bufferStrategy.getDrawGraphics();
g2.drawImage(...) // etc.
g2.dispose();
bufferStrategy.show();
David Moles
  • 48,006
  • 27
  • 136
  • 235
  • Why do you still use old AWT components, while Swing provides much better rendering and automatically enables double-buffering? – Guillaume Polet Jun 27 '14 at 19:40
  • Because if you're writing something like a game, and you're doing all your own rendering, and you need fine-grained control over your multi-buffer strategy, you don't necessarily need anything Swing does for you, and in fact Swing's built-in double-buffering will get in the way. – David Moles Jun 27 '14 at 22:53