0

I have a problem working with instances of different objects and this is what happens:

I have been developing a small game in Java (Swing & AWT) for a while and I have the following classes:

  • App.java
  • Play.java
  • Event.java
  • GameScene.java
  • MenuScene.java
  • Timer.java

Where:

  • App extends JFrame and is a frame with the main function of the application (main), this class creates the game window, and only exists this JFrame

  • The MenuScene and GameScene classes are scenes of the application, for example when you see the menu and you want to see the highest score, it is a scene, the levels of game are a scene, etc., but in this case I have only two scenes and I have represented them in JPanels: MenuScene extends JPanel and creates the game menu (buttons, images, etc.), the same applies to the GameScene class, this also extends JPanel and creates the game.

  • The other classes (Play, Event, Timer) are simple classes, they have the "logic of the game", keyboard control, timers, game operation and are instantiated in three global variables of the GameScene class.

Everything starts with App, creates an instance of it and in its constructor calls a method to "create" the menu (MenuScene.java). Now the menu has a JButton that when pressed "creates" the game (GameScene.java) and this class has a JButton to return to the menu at any time ... It is here where I have problems because if I am playing and I return to the menu Game still exists and I can lose, it does not make sense, it is as if you play but instead of seeing the game you see the menu, interestingly the graphic part works excellent, ie if I press a button it removes what I have and draws the scene that I want it quickly. It is because Play, Timer and Event are instantiated or "exist" in memory if I am not mistaken. So if I press again the "create game" JButton I would recreate a second instance of GameScene? And so infinitely for MenuScene and GameScene. Is there a solution to this? How do you think I should structure the application?

I give you an outline of the most important classes:

App.java

public class App extends JFrame {
   private JPanel rootPanel;

   public App() {
      //Define frame
      ...
      runScene(new MenuScene(this));
   }

   public void runScene(JPanel scene) {
      destroyScene();

      rootPanel.setBackground(scene.getBackground());
      rootPanel.add(scene);
      rootPane.validate();
      rootPanel.repaint();
   }

   private void destroyScene() {
      rootPanel.removeAll();
      rootPanel.revalidate();
      rootPanel.repaint();
   }


   public static void main(String[] args) { //Main
      new App();
   }
}

MenuScene.java

public class MenuScene extends JPanel {
   private App app;

   public MenuScene(App app) {
      this.app = app;
      //Define JPanel
      ...
      buttonStart.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            app.runScene(new GameScene(app));
        }
      });
   }
}

GameScene.java

public class GameScene extends JPanel {
   private App;
   private Play;
   private Timer;
   private Event; //Define controls (keyboard)

   public GameScene(App app) {
      this.app = app;
      //Define JPanel, Play, Timer and Event
      ...
      buttonBackMenu.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            app.runScene(new MenuScene(app));
        }
      });
   }
}

Play.java

public class Play {
   private JLabel[][] x;

   public Play(JLabel[][] x) { //This matrix is important (is an reference), is instanced in GameScene, this is an problem?
      this.x = x;
      //Define others variables
   }
}

I appreciate any help.

Julián
  • 1,238
  • 1
  • 12
  • 24
  • 1
    When you press back from game, you should be dismantling/destroying the games controls, the timer in particular, you should also dereference the app variable to prevent any circular references – MadProgrammer Jan 18 '17 at 19:31
  • 1
    1) *"I give you an outline of the most important classes:"* For better help sooner, post a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). 2) `rootPanel.removeAll(); rootPanel.revalidate(); rootPanel.repaint();` Use a [`CardLayout`](http://download.oracle.com/javase/8/docs/api/java/awt/CardLayout.html) as shown in [this answer](http://stackoverflow.com/a/5786005/418556). – Andrew Thompson Jan 18 '17 at 19:31

1 Answers1

0

I have found a somewhat peculiar solution, but I do not know if it is the best:

The best way is that since the GC does not select the active timers then it is better to stop them and match the other objects to null. Using the Singleton pattern I have a single instance of a Frame, that same instance would be used in any class (Scene) that wants to run another scene, here an implementation:

App.java

   public class App extends JFrame {
       private JPanel rootPanel;
       private static App app;

       private App() {
          super("x");
          createApp();
       }

       public static App getInstance() {
          if (app == null) {
            app = new App();
          }

          return app;
       }

       private void createApp() {
         //Define JFrame, rootPanel, buttons, etc ...
       }

       public void runScene(JPanel scene) {
          rootPanel.removeAll();

          rootPanel.add(scene);
          rootPanel.revalidate();
          rootPanel.repaint();
       }

       public static void main(String[] args) { //Main
          getInstance().runScene(new MenuScene());
       }
    }

GameScene.java

    public class GameScene extends JPanel {

    private Play p;
    private Timer t;
    private Event e; //Define controls (keyboard)
    private JLabel[][] mat;

    public GameScene() {
       //Define JPanel, Matrix, Play, Timer and Event
       ...
       buttonBackMenu.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent x) {
             This method is useful to create another scene for example from another instance other than this (GameScene)
             changeScene(new MenuScene());
         }
       });
    }

    public void changeScene(JPanel scene) {
        e.removeKeyDispatcher(); //You must create a method that allows you to move the event key dispatcher
        t.stopAllTimers(); //You must create a method to stop all timers, or any object that is active.
        t = null;
        e = null;
        p = null;
        //If you have more active objects and work with other instances of other classes they should be "broken" or "stopped" and then match their instance to null
        App.getInstance().runScene(scene);
    }

    //Optional...
    public JLabel[][] getMat() {
       return mat;
    }
 }

Play.java, Event.java, Timer.java (X)

public class X {

   private GameScene g;
   private JLabel[][] mat;

   public X(GameScene g) {
      this.g = g;
      //I would use the instance of the class I need to access the variables I'm going to use, for example:
      this.mat = g.getMat();
   }
} 
Julián
  • 1,238
  • 1
  • 12
  • 24