0

I'm building a Java Virtual Pet game, currently the menu panel is working fine, buttons are loading and operating, but when I click my new game button which is supposed to launch into the GamePanel Panel I get a nullpointerException.

This is the main class which builds the original frame and Jpanel to switch from.

public class MainFrame extends JPanel {

public JPanel mainPanel;
public CardLayout cl;
private final GamePanel gamePanel;
private final MenuPanel menuPanel;

/**
 *  Constructs the main panel to be used to switch between panels.
 * 
 */
public MainFrame() {

    // creates a new panel to add panels to.
    cl = new CardLayout();

    // panel to be used as a main switch.
    mainPanel = new JPanel();
    Dimension size = getPreferredSize();
    size.width = 600;
    size.height = 600;
    setPreferredSize(size);
    setBackground(Color.BLACK);

    add(mainPanel);

    gamePanel = new GamePanel();
    menuPanel = new MenuPanel();

    // sets layout
    mainPanel.setLayout(cl);

    mainPanel.add(menuPanel, "menuPanel");
    mainPanel.add(gamePanel, "gamePanel");

}

public void changePanel(String name) {

    cl.show(mainPanel, name);

}

/**
 * Main frame used by the game.
 * 
 * @param args 
 */
public static void main(String[] args) {

MainFrame game = new MainFrame();

JFrame frame = new JFrame("Main Window");
frame.add(game);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);    
frame.setLocationRelativeTo(null);             

}

}

This is the code from my MenuPanel class which should be accessing the changePanel() method.

public class MenuPanel extends JPanel {

MainFrame mf;

public MenuPanel() {
    this.mf = mf;

    Dimension size = getPreferredSize();
    size.width = 600;
    size.height = 600;
    setPreferredSize(size);

    ImageIcon menuIcon = new ImageIcon("C:\\Programming\\NetBeansProjects\\PDCMain\\src\\Data\\the_menu_title2.png");
    JLabel menuLbl = new JLabel();
    menuLbl.setIcon(menuIcon);

    JButton newGameBtn = new JButton("New Game");
    JButton loadGameBtn = new JButton("Load Game");
    JButton helpBtn = new JButton("Instructions");
    JButton exitBtn = new JButton("Exit");

    newGameBtn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("New Game");
            mf.changePanel("gamePanel");
        }
    });

loadGameBtn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {

        }
    });

    helpBtn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            showHelp();
        }
    });

    exitBtn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    });


    setLayout(new GridBagLayout());

    GridBagConstraints gc = new GridBagConstraints();

    gc.weightx = 0;
    gc.weighty = 0.1;

    gc.gridx = 0;
    gc.gridy = 1;
    add(menuLbl, gc);        

    gc.gridx = 0;
    gc.gridy = 2;
    add(newGameBtn, gc);

    gc.gridx = 0;
    gc.gridy = 3;
    add(loadGameBtn, gc);

    gc.gridx = 0;
    gc.gridy = 4;
    add(helpBtn, gc);

    gc.gridx = 0;
    gc.gridy = 5;
    add(exitBtn, gc);  

}

public void showHelp(){
    String[] help = new String[100];
    try {
        BufferedReader br = new BufferedReader(new FileReader("instruct.txt"));
        String line = "";
        int i = 0;
        while((line = br.readLine()) != null) {
            help[i] = line;
            i++;
        }
        JOptionPane.showMessageDialog(null, help);
    } catch(IOException ex) {
        System.err.println("IOException Error: " + ex.getMessage());
    }
}

and here is the game panel

public class GamePanel extends JPanel{

private ButtonsPanel buttonsPanel;
private GraphicsPanel graphicsPanel;
private final JPanel gamePanel;

public GamePanel(){

    super();
    this._initGUI();

    gamePanel = new JPanel();
    Dimension size = getPreferredSize();
    size.width = 600;
    size.height = 600;
    setPreferredSize(size);
    setBackground(Color.RED);

}
private void _initGUI(){

    this.buttonsPanel = new ButtonsPanel();
    this.graphicsPanel = new GraphicsPanel();

    this.setLayout(new BorderLayout());
    this.add(buttonsPanel, BorderLayout.SOUTH);
    this.add(graphicsPanel, BorderLayout.CENTER);
}

public void run(){
    graphicsPanel.run();
}

}

This is my first attempt at building a GUI from scratch and using cardlayout. I can't see where it would be assigning a null value as the panel is declared in the main panel used for the switching, any ideas?

user2797454
  • 85
  • 1
  • 9
  • For better help sooner, post one [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete Verifiable Example) as opposed to three source files. – Andrew Thompson Oct 20 '14 at 06:36
  • *"..get a nullpointerException."* 1) See [What is a stack trace, and how can I use it to debug my application errors?](http://stackoverflow.com/q/3988788/418556) & [What is a Null Pointer Exception, and how do I fix it?](http://stackoverflow.com/q/218384/418556) 2) Always copy/paste error and exception output! – Andrew Thompson Oct 20 '14 at 06:38
  • When I try and run your code, I get an (basically) blank screen, because the buttons aren't added – MadProgrammer Oct 20 '14 at 06:41

1 Answers1

3

Take a second to look at this piece of code...

public static class MenuPanel extends JPanel {

    MainFrame mf;

    public MenuPanel() {
        this.mf = mf;

Basically, you are assigning (this) mf to this.mf, but since mf is already null, this.mf will remain null...

You should be passing a reference of MainFrame to your MenuPanel

Now, because I hate passing around references of UI components to other parts of the program, which should have no idea about the rest of the UI and don't direct control over them, I would advice against passing MainFrame blinding like this.

Instead, create a interface which provides the functionality you expect that MenuPanel should be allowed to perform (newGame, loadGame, showInstructions, exit) for example and allow the implementation of this interface (probably the MainFrame) to deal with it

You would then pass the instance of this interface to MenuPanel...This limits the exposure of classes to other classes which don't need those classes functionality and decouples your code so you can pass any old instance of the interface to the class without having to rewrite whole sections of your code...

Updated with a basic concept

Start with some kind game controller interface, for example...

public interface GameController {

    public void newGame();
    public void loadGame();
    public void showInstructions();
    public void exit();

}

This describes the actions that can take against the implementation of this interface.

Use MainFrame and implement the GameController...

public class MainFrame extends JPanel implements GameController {

    //...

    public void newGame() {
    }

    public void loadGame() {
    }

    public void showInstructions() {
    }

    public void exit() {
    }

Change MenuPane to require an instance of GameController when it's constructed...

public class MenuPanel extends JPanel {

    private GameController controller;

    public MenuPanel(GameController gameController) {
        this.controller = gameController;
        //...

Now, when you handle the user interaction, you would pass the request to the controller....

newGameBtn.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("New Game");
        controller.newGame();
    }
});

In this way, the MenuPanel doesn't know how a new game is created, it just knows that it will be taken care of...

Now, you will need to update the newGame method in MainFrame to do something, for example...

public void newGame() {
    // Setup the new game...
    cl.show(mainPanel, "gamePanel");
}

This places all the responsibility squarely with the implementation of the GameController to make decisions about how certain actions should take place, decouples the process, so if you change the implementation of the GameController to some other class, you don't need to worry about updating the MenuPanel, cause it's only interested in taking to the GameController...nice ;)

MadProgrammer
  • 323,026
  • 21
  • 204
  • 329
  • Sorry I had this working in previous version of the code, but due to it getting messy, I've started again. I'm confused as to how to implement the interface, what would be the best way to just pass in MainFrame.. mostly so I can test the graphicsPanel is working, public MenuPanel(Mainframe mainframe) etc? – user2797454 Oct 20 '14 at 06:53
  • No, you're not creating a new instance of `MainFrame` in `MenuPanel`, you're passing a reference either, so you end up with a `NullPointerException` because `mf` in `MenuPanel` is `null`, it's never assigned a valid value... – MadProgrammer Oct 20 '14 at 06:55
  • Thank you for your help and explanation, I'll see how I go implementing this – user2797454 Oct 20 '14 at 07:24
  • Hey it's still giving me the nullpointer error @ controller.newGame(); is it perhaps something to do with my cardLayout? I've just tried controller.exit(); with the exit interface running System.exit(0); which also gives a nullpointer? – user2797454 Oct 20 '14 at 07:39
  • If the `controller.newGame` is causing a `NullPointerException` I would think that the `controller` is `null`, but I don't have the code. Try putting a `System.out.println(controller)` before you call `newGame` and put a `System.out.println` in `newGame` to see if your actually getting the results you think you are. – MadProgrammer Oct 20 '14 at 09:38