1

I have some class which creates a Ship (class Ship extends GameObject) and attempts to add it to a gameBoard.
To do this, it tells the gameFrame to add the object as follows:

public void startNewGame() {
    Ship myShip = new Ship(GAME_BOARD_WIDTH / 2, GAME_BOARD_HEIGHT-1, SHIP_WIDTH,
            SHIP_HEIGHT);
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            gameFrame = new InvadersGameFrame();
        }
    });
    gameFrame.addGameObject(myShip); //Problem line
    gameFrame.repaint();
}

The gameFrame then calls:

GameBoard gameBoard = new GameBoard();
...
...
public void addGameObject(GameObject ob) {
    gameBoard.addGameObject(ob);
}

Which in turn calls:

public class GameBoard extends JPanel implements GameData{
    private JPanel gameBoard;
    private List<GameObject> objects = new ArrayList<>();

    public GameBoard() {
        gameBoard = new JPanel();
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        setBackground(Color.black);
        g.setColor(Color.RED);
        for(GameObject ob : objects){
            g.drawOval(ob.x, ob.y, ob.width, ob.height);
        }
    }

    //Places object into list for drawing upon next repaint.
    public void addGameObject(GameObject ob) {
        objects.add(ob);
    }
}

Now my problem is that I receive a Null Pointer Exception when I gameFrame.addGameObject(myShip);
The tricky thing is when I run via debugger I do not receive the NPE at all (but my objects list still seems empty).
Also, I can follow into each of these and still see myShip, so am I just referencing my GameObject (Ship) wrong?
Should my parameters for addGameObject somehow be more abstract?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
user1695505
  • 265
  • 2
  • 13
  • 1
    Do you have a stacktrace to post? – matt forsythe Apr 21 '14 at 21:22
  • Your stack trace is the most valuable piece of information you have when debugging things like this. Why did you forget to post it? – Jason C Apr 21 '14 at 21:24
  • Because I am too much a newb to even know how to get a stacktrace. I will google and return. – user1695505 Apr 21 '14 at 21:24
  • When you call addGameObject, are you passing a new'd object to it? so for example: GameObject foo = new GameObject(args1, args2); addGameObject(foo); –  Apr 21 '14 at 21:25

4 Answers4

4

The problem is that you are assigning gameFrame inside of the Runnable, which does not get run until later. So gameFrame may be null at the point you call gameFrame.addGameObject(myShip)

matt forsythe
  • 3,863
  • 1
  • 19
  • 29
  • 1
    +1: To clarify: "*may be* null"; it's unpredictable, hence the inconsistency in the received errors. – Jason C Apr 21 '14 at 21:25
  • 1
    Quite true - "may be null". But is definitely null those times in which you get a NullPointerException. :) Edited my answer - thanks. – matt forsythe Apr 21 '14 at 21:26
  • Will accept as answer in 3min. Thank you, very insightful! I was looking towards the objects for the error, would have taken ages to even start looking at the frame. – user1695505 Apr 21 '14 at 21:32
2

The problem probably is the invokeLater you use, that is scheduled whenever Swing feels like it and probably happens after gameFrame.addGameObject(myShip);

This doesnt happen in the debugger because it is somewhat of a race condition, in the debugger Swing invokes the runmethod before gameFrame.addGameObject(myShip) because you can't click fast enough to preemt that statement ;)

windwarrior
  • 456
  • 2
  • 11
1

The best solution to this problem is to move those statements inside the run method, like this:

public void startNewGame() {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            Ship myShip = new Ship(GAME_BOARD_WIDTH / 2, GAME_BOARD_HEIGHT-1, 
                SHIP_WIDTH, SHIP_HEIGHT);
            gameFrame = new InvadersGameFrame();
            gameFrame.addGameObject(myShip); //Problem line
            gameFrame.repaint();
        }
    });
}

Doing it this way, the 4 statements will be run in the expected order, and the attribute will not be null when required.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
0

check the scope where you initialized that variable. i.e. the curly braces { } around the new InvadersGameFrame(). Make sure you are using the object after creating it

jambriz
  • 1,273
  • 1
  • 10
  • 25