0

I'm new to JavaFX and I wanted to create a simple 2D game using JavaFX. I read this JavaFX game tutorial which I found very useful. I wrote a simple game with the following structure:

public class MyGame extends Application 
{
(...)      
public void start(Stage theStage) 
{
    (...)
    Group root = new Group();
    Scene theScene = new Scene( root );
    theStage.setScene( theScene );

    Canvas canvas = new Canvas( 800, 600 );
    root.getChildren().add( canvas );
    (...)               
    theScene.setOnMouseClicked(
            new EventHandler<MouseEvent>()
                {
                    public void handle(MouseEvent e)
                    {
                        //mouse events                
                    }
                });
    final GraphicsContext gc = canvas.getGraphicsContext2D();
   (...)
    final LongValue lastNanoTime = new LongValue( System.nanoTime() );

    new AnimationTimer()
    {

        public void handle(long currentNanoTime)
        {

            double elapsedTime = (currentNanoTime - lastNanoTime.value) / 1000000000.0;
            lastNanoTime.value = currentNanoTime;
            //drawing all sprites with gc etc.
        }
    }.start();

    theStage.show();
}
}

And I was quite happy with the result. There was no menu etc. in the game, just one screen with the game itself. The problem is, I now want to expand the application and include a few different games in it, which means I somehow need to handle a few different screens (the main menu and each of the games) and switch between them. I looked for help in the Internet, but all I found were some examples with FXML which I don't really use.

My question is: how can I easily create a few other screens and switch between them, given the structure that I have now?

  • This question just seems too broad: which particular part are you stuck with? Creating a menu? Reacting to the user choosing a menu item? Doing the layout? Structuring the overall application? You probably need to try something and post a specific question when you are stuck. To get you started, consider using a `BorderPane` with a `MenuBar` (or `ToolBar`) in the top; change the center when the user selects a game. Factor the code for each game into a different class: perhaps see my answer to http://stackoverflow.com/questions/32464698/ for structuring the application. – James_D Jun 17 '16 at 18:59
  • I guess the precise problem I have is that I don't know how to tackle the handle method of AnimationTimer which has so far worked as the main loop of my game. This is where I update sprites, check collision etc. So far, the single game just initiated everything before entering the method and then stayed in there till I closed the application. Now, I don't know how I can "exit" a single game from inside this method, show the main menu to choose another game. – user3018717 Jun 17 '16 at 19:23
  • `AnimationTimer` has a `stop()` method... otherwise I think you just described exactly what you need to do. – James_D Jun 17 '16 at 19:23
  • Read this: https://blogs.oracle.com/acaicedo/entry/managing_multiple_screens_in_javafx1. Alternatively, you can simply create a new Scene and set it on your Stage. Or, you can create a new Parent Node and set that on your existing Scene. – ManoDestra Jun 17 '16 at 20:07

1 Answers1

1

Why not using different scenes ? Here is a small example I've just made :

public void start(Stage primaryStage){
        Group g1 = new Group();
        Group g2 = new Group();
        Scene sc1 = new Scene(g1, 150, 100);
        Scene sc2 = new Scene(g2, 150, 100);

        Label t1 = new Label("This is panel 1");
        Button b1 = new Button("Go to panel 2");
        Label t2 = new Label("This is panel 2");
        Button b2 = new Button("Go to panel 1");
        t1.setTranslateY(15);
        t2.setTranslateY(15);
        b1.setTranslateY(50);
        b2.setTranslateY(50);
        g1.getChildren().addAll(t1, b1);
        g2.getChildren().addAll(t2, b2);

        b1.setOnMouseClicked(e -> { primaryStage.setScene(sc2); });
        b2.setOnMouseClicked(e -> { primaryStage.setScene(sc1); });

        primaryStage.setScene(sc1);
        primaryStage.show();
    }

As you can see, groups g1 and g2 are two different screens. Each contains a text and a button, by clicking it, it will switch beetween them.

I hope it helps you

EDIT: James_D made me notice that the menu would have to be replicated to have it on each screen, the other solution is to add a BorderPane as the root node of the scene, as this we always have the menu on the top and our screen in the center :

public void start(Stage primaryStage){
        BorderPane root = new BorderPane();
        Menu file = new Menu("File");
        MenuBar tb = new MenuBar(file);
        Group g1 = new Group();
        Group g2 = new Group();
        Scene sc1 = new Scene(root, 150, 100);
        root.setCenter(g1);
        root.setTop(tb);

        Label t1 = new Label("This is panel 1");
        Button b1 = new Button("Go to panel 2");
        Label t2 = new Label("This is panel 2");
        Button b2 = new Button("Go to panel 1");
        t1.setTranslateY(15);
        t2.setTranslateY(15);
        b1.setTranslateY(50);
        b2.setTranslateY(50);
        g1.getChildren().addAll(t1, b1);
        g2.getChildren().addAll(t2, b2);

        b1.setOnMouseClicked(e -> { root.setCenter(g2); });
        b2.setOnMouseClicked(e -> { root.setCenter(g1); });

        primaryStage.setScene(sc1);
        primaryStage.show();
    }
Omar
  • 943
  • 6
  • 9
  • Though if you use different scenes you will need to replicate the menu in each of them... – James_D Jun 17 '16 at 18:56
  • Oh right, the second solution is to change the root of the scene – Omar Jun 17 '16 at 18:59
  • Surely that suffers from the same problem? – James_D Jun 17 '16 at 19:00
  • You are right, as the toolbar (menu) is attached to the root Node. The solution is to create a BorderPane (containing the menu toolbar, thanks to my_borderpane.setTop(my_menutoolbar)) and to put the g1 in the middle of this border pane. (setCenter()) As this, when clicking, we simply need to change the center of the border pane, the menu is then unchanged. Of course, this border pane is the root node of the scene – Omar Jun 17 '16 at 19:06
  • @Omar, this looks OK for static screens with buttons etc., but my game uses an AnimationTimer with a handle() method where I actually draw everything onto the screen in an infinite loop -- that is essentially the main loop of the game. The problem is, I don't know how to use the loop of AnimationTimer with different Scenes. – user3018717 Jun 17 '16 at 19:13
  • I understand. If you draw everything in the handle() method, so the transitions between each screens is done inside it, so a solution (not the best) would be to have a kind of flag (enumeration for example), which tells the handle which screen should be drawn. Another solution would be to stop the AnimationTimer and change the principal Node but seems harder to do – Omar Jun 17 '16 at 19:26
  • @user3018717 As I suggested in the comment below your question, the question is really too broad to get an answer that is likely to help you: everyone's just going to have to guess which part you are stuck with and what the problem is (e.g., for me, I don't understand why having an `AnimationTimer` associated with each game would make any difference at all to what Omar suggests here). You need to clarify the question considerably if you want help. – James_D Jun 17 '16 at 19:29