NullPointer exception?

Hi,
I was trying to follow Killer Game Programming in Java but I failed to do so (Too tough for me for now - I need to get a good understanding about game loops and all those delta and other variables first). Then I tried to follow Black Art of Java Game Programming. Looks easier.
It does all stuff on an applet and I hate applets. So I tried to convert it to a JFrame application.
I figured init() is like a constructor, start() would be my main method, stop() would be performed by JFrame and run() is a Thread method (no need to convert). So, I converted it into a JFrame app.

It worked, but with a little problem:

[quote]Exception in thread “AWT-EventQueue-0” java.lang.NullPointerException
at Broadway.paint(MyFrame.java:42)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.access$700(Unknown Source)
… Goes on
[/quote]
So I figured I forgot to initiate something. The 42nd line is this:

offScreen.setColor(Color.BLACK); //offScreen is the Graphics object I use for double buffering

But I actually initiated it in the constructor. Here is my full code which can compile and show the error:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JFrame;

public class MyFrame extends JFrame implements Runnable {

	Graphics offScreen; // That's how the book tells me to implement it
	Image buffer;

	int a = 5;
	int b = 3;
	int locx, locy, width, height;
	int aWidth = 400;
	int aHeight = 400;

	static final int REFRESH_RATE = 50; // Sleep duration. The lower this is,the faster the rectangle. 

	public MyFrame() {
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setBackground(Color.black);
		setSize(aWidth, aHeight);
		locx = 80;
		locy = 100;
		width = 110;
		height = 90;
		setVisible(true);

		buffer = createImage(aWidth, aHeight); // Creating an image in the same size as JFrame
		offScreen = buffer.getGraphics(); // It is initialized man...
	}

	public static void main(String args[]) {
		MyFrame b = new MyFrame();
		Thread animation = new Thread(b);
		if (animation != null) {
			animation.start();
		}
	}

	public void paint(Graphics g) {
		offScreen.setColor(Color.BLACK);
		offScreen.fillRect(0, 0, aWidth, aHeight);
		offScreen.setColor(Color.blue);
		offScreen.fillRect(80, 200, 220, 90);
		offScreen.fillRect(100, 10, 90, 80);
		offScreen.setColor(Color.lightGray);
		offScreen.fillRect(locx, locy, width, height);
		g.drawImage(buffer, 0, 0, this); // Why is this "g" instead of "offScreen"? Figure.									
	}

	public void update(Graphics g) { // Book tells this is needed. Figure why.
		paint(g); // Also look into 'active rendering' thing. This looks nasty.
	}

	public void updateRectangle() {
		locx += a;
		locy += b;
		if (locx >= 290 || locx <= 0) {
			a = -a;
		}
		if (locy >= 310 || locy <= 0) {
			b = -b;
		}

	}

	public void run() {
		while (true) {
			repaint(); // It's said that calling repaint() is not a guaranteed way.					
			updateRectangle();
			try {
				Thread.sleep(REFRESH_RATE);
			} catch (Exception exc) {
			}
		}
	}
}

You never created a new instance of offscreen

Hmm… I thought I did it here:

      buffer = createImage(aWidth, aHeight);
      offScreen = buffer.getGraphics(); .

No, you’re just somehow assigning a method or something to an object. The

offscreen

object is still never initialized. You need to create one and then call

offScreen = buffer.getGraphics();

I think the problem really is that it should be ordered in another way:

   public MyFrame() {
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setBackground(Color.black);
      setSize(aWidth, aHeight);
      buffer = createImage(aWidth, aHeight); // Creating an image in the same size as JFrame
      offScreen = buffer.getGraphics(); // It is initialized man...
      locx = 80;
      locy = 100;
      width = 110;
      height = 90;
      setVisible(true);
   }

The problem could be, that with “setVisible(true)” he already wants to paint the JFrame and it’s contents. And in paint, he needs the Graphics object offScreen. And at this point it is not initialized. Try it this way. :slight_smile:

No that’s not either. Actually, that way it never runs.

But I figured it out. Pay attention to my paint() method and see my new one:

public void paint(Graphics g) {
		if(offScreen !=null){
		offScreen.setColor(Color.BLACK);
		offScreen.fillRect(0, 0, aWidth, aHeight);
		offScreen.setColor(Color.blue);
		offScreen.fillRect(80, 200, 220, 90);
		offScreen.fillRect(100, 10, 90, 80);
		offScreen.setColor(Color.lightGray);
		offScreen.fillRect(locx, locy, width, height);
		}
		g.drawImage(buffer, 0, 0, this);									
	}

I thought if the app starts, it eventually gets something :smiley:

But I really like to learn why it is null there…

I think there is truth in what I said before:

[quote]The problem could be, that with “setVisible(true)” he already wants to paint the JFrame and it’s contents. And in paint, he needs the Graphics object offScreen. And at this point it is not initialized.
[/quote]
But I didn’t see that createImage() is a method of the Component JFrame. And this method returns:

[quote]an off-screen drawable image, which can be used for double buffering. The return value may be null if the component is not displayable. This will always happen if GraphicsEnvironment.isHeadless() returns true.
[/quote]
So no wonder that it doesn’t start when doing as I said. :wink: You could fix this by using a BufferedImage and creating it yourself with:

buffer = new BufferedImage(aWidth, aHeight, BufferedImage.TYPE_4BYTE_ABGR);

So your code would be:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;

public class MyFrame extends JFrame implements Runnable {

	Graphics offScreen; // That's how the book tells me to implement it
	BufferedImage buffer;

	int a = 5;
	int b = 3;
	int locx, locy, width, height;
	int aWidth = 400;
	int aHeight = 400;

	static final int REFRESH_RATE = 50; // Sleep duration. The lower this is,the faster the rectangle. 

	public MyFrame() {
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setBackground(Color.black);
		setSize(aWidth, aHeight);
		buffer = new BufferedImage(aWidth, aHeight, BufferedImage.TYPE_4BYTE_ABGR); // Creating an image in the same size as JFrame
		offScreen = buffer.getGraphics(); // It is initialized man...
		locx = 80;
		locy = 100;
		width = 110;
		height = 90;
		setVisible(true);
	}
[...]

You could also let buffer be of type Image, because BufferedImage is extending Image and its polymorph.

Thanks a ton Sparky83!

So, setVisible(true) also calls paint to draw the frame huh… And when it calls it, because I haven’t initialized my offScreen object, there is a null pointer exception.

Then what I did by my “if” fix was to make g object draw an empty frame (buffer was initialized, but was empty), so the next time offScreen was initialized already, hence it acted as I wanted it to act.

So mainly my problem was a null image object, not a null Graphics object? This is the only way I could completely make sense of it.

The way I wanted you to do it, it was a problem of the null image object. The way you did it, it is both but mostly a problem of the Graphics object offScreen being null. It was just not initialized already. But after the Image is created and the Graphics object of the Image obtained and stored in offScreen, it works.

But I think you get it quite right. You could debug this part of you program and trace the steps it does. This only works good if you have linked your JDK with it’s source in eclipse or something.

So, in my original code, Graphics object was null, because the image it was supposed to getGraphics() was also null, right?

no, because then you would have had the NullPointerException in the line


offScreen = buffer.getGraphics();

because buffer would have been null.

the problem in the original code was, like Sparky83 said, that you called setVisible(true) before


buffer = createImage(aWidth, aHeight); // Creating an image in the same size as JFrame
offScreen = buffer.getGraphics(); // It is initialized man...

in the original code the order was:
setVisible()
repaint() < this was called from the AWT thread “in the background” because you called setVisible()
buffer = createImage()

btw, you create an offscreen that has the size of the frame including the frame border. so if you paint to the offscreen with


offScreen.setColor(Color.red);
offScreen.fillRect(buffer.getWidth() - 1, buffer.getHeight() - 1, 1, 1);

you will not see the red pixel.
you could call setUndecorated(true) before setVisible(true), which will give you a frame without border.

or, even better, add an other Component to the Frame’s ContentPane (or replace the ContentPane) and create your offscreen in the doLayout() method of that Component.
this way you can handle when the Frame gets resized and create an offscreen that always has the same size as the ContentPane.

Thanks for the explanation. I guess now I truly understand what Sparky told. setVisible calls repaint and what repaint does is to call paint(), and because I used a Graphics object that was not initialized, it naturally threw NullPointerException.
And because my method of creating image required a visible frame, the other way did not work either.

And by using “if”, I basicly skipped these stuff and thus it had the chance to create the image and thus, offScreen object.

Right? :smiley:

I realize that frames count too when I set size. Thanks.

And, I could escape this problem by simply drawing everything on a JPanel and then adding it to the JFrame, right?

Man if my theories are correct, then I will be very happy. It would be like a milestone for me.

right. in the first paint() the if statement protected you from the NPE, and in further paint() you had the offscreen already created.

almost right :wink:
you would first create a subclass of JPanel and override the paint(Graphics g) method.
(in this method you use the param “g” to draw onto the JPanel.)
then add an instance of your JPanel subclass to the JFrame and then call the JFrame’s setVisible(true).

Perfect.
Hey, is there any way I can show my appreciation except for a post saying thanks?

Well, there is an appreciate button at the top right corner of each post. g