0
votes

I am designing a Checkers Game and once the game is finished, a new window opens where you have the possibility to either start a new game or quit. I don't know much about JavaFX and I have been trying for a while now to get my code to work, but I stand without success...

I have the difficulty, that my board is not displayed as a fxml file, but it is being created with javacode and put in the center of my borderPane. The top of the borderPane is fxml-file. Then, the other window, once the game is over, is also another fxml-file:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.text.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
 <?import javafx.scene.layout.*?>
 <?import javafx.scene.layout.AnchorPane?>

 <Pane fx:id="paneMenu" nodeOrientation="LEFT_TO_RIGHT" prefHeight="160.0" prefWidth="240.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.GameMenuController">

<children>
 <Button fx:id="buttonRestart" layoutX="15.0" layoutY="122.0" mnemonicParsing="false" onAction="#restart" prefHeight="27.0" prefWidth="90.0" text="New Game" />
  <Button fx:id="buttonQuit" layoutX="139.0" layoutY="122.0" mnemonicParsing="false" onAction="#quit" prefHeight="27.0" prefWidth="90.0" text="Quit" />
  <Label fx:id="labelWinner" alignment="CENTER" contentDisplay="CENTER" layoutX="23.0" layoutY="80.0" prefHeight="17.0" prefWidth="190.0" text="Label" textAlignment="CENTER" />
  <Text layoutX="50.0" layoutY="47.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Game over!" textAlignment="CENTER">
     <font>
        <Font size="26.0" />
     </font>
  </Text>
    </children>
   <padding>
  <Insets bottom="10.0" right="10.0" />
 </padding>
</Pane>

My Controller class for this fxml-file looks as follows:

public class GameMenuController implements Initializable {

public GameMenuController(Checkers model) {
    this.content = model;
}

Checkers content;

@FXML
private Label labelWinner;

@FXML
private Button buttonRestart;

@FXML
private Button buttonQuit;

@FXML
private Pane paneMenu;

BooleanProperty doRestart = new SimpleBooleanProperty(false);

@FXML
void restart(ActionEvent event) {
    content.cleanup();
    doRestart.set(true);
}

@FXML
void quit(ActionEvent event) {
    System.exit(0);
}

@Override
public void initialize(URL location, ResourceBundle resources) {
    labelWinner.setText("Winner here.");
    labelWinner.textProperty().bind(Bindings.createStringBinding(() -> {
        String s = " ";
        if (...);
        return s;
    }, content.whiteTurnProp));
}

}

And finally, in my main class I wanted to add a ChangeListener to the value doRestart in order to then recreate a new Scene.

My main class looks something like this:

public class Main extends Application{

Checkers content = new Checkers();
BorderPane border = new BorderPane(); 
Pane paneBoard = new Pane();
FXMLLoader loader1 = new FXMLLoader();
FXMLLoader loader2 = new FXMLLoader();

@Override
public void start(Stage primaryStage) throws Exception {

    loader1.setLocation(getClass().getResource("sample.fxml"));
    loader1.setControllerFactory(new Callback<Class<?>, Object>() {
        @Override
        public Object call(Class<?> aClass) {
            return new SampleController(content);
        }
    });

    loader2.setLocation(getClass().getResource("GameMenu.fxml"));
    loader2.setControllerFactory(new Callback<Class<?>, Object>() {
        @Override
        public Object call(Class<?> aClass) {
            return new GameMenuController(content);
        }
    });   
    GameMenuController gameMenuControl = loader2.getController(); 

    Scene scene = new Scene(startGame());
    primaryStage.setTitle("Checkers");
    primaryStage.setScene(scene);
    primaryStage.show();

    content.gameOver.addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                try {
                    scene.setRoot(loader2.load());
                    primaryStage.setHeight(180);
                    primaryStage.setWidth(240);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        });

    gameMenuControl.doRestart.addListener(new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
            if(newValue) {
            scene.setRoot(startGame());
            primaryStage.setHeight(scene.getHeight());
            primaryStage.setWidth(scene.getWidth());
            gameMenuControl.doRestart.set(false);
            }
        }
    });
}

public static void main(String[] args) {
    launch(args);
}
protected Pane createContent() {

    paneBoard.setPrefSize(Checkers.WIDTH * Checkers.TILE_SIZE, Checkers.HEIGHT * Checkers.TILE_SIZE);
    paneBoard.getChildren().addAll(content.tileGroup, content.pieceGroup);

    for (int y = 0; y < Checkers.HEIGHT; y++) {
        for (int x = 0; x < Checkers.WIDTH; x++) {
            Tile tile = new Tile((x + y) % 2 == 0, x, y);
            Checkers.board[y][x] = tile;

            content.tileGroup.getChildren().add(tile);

            Piece piece = null;

            if (y <= 3 && (x + y) % 2 != 0) {
                piece = content.makePiece(PieceType.BLACK, x, y);
            }

            if (y >= 6 && (x + y) % 2 != 0) {
                piece = content.makePiece(PieceType.WHITE, x, y);
            }

            if (piece != null) {
                tile.setPiece(piece);
                content.pieceGroup.getChildren().add(piece);
            }
        }
    }

    return paneBoard;
}

protected BorderPane startGame() {
    try {
        border.setTop(loader1.load());
    } catch (IOException e) {
        e.printStackTrace();
    }
    border.setCenter(createContent());

    return border;
}

}

I am pretty sure that my code is a mess, I have been trying to work in as much of the tips given on here as possible, but since I am still super new to this, I don't really know how to go about it all.

What I would like to do, instead of using this change listener (which also doesn't work yet, Iam getting a NullPointerException), I'd like to change the view directly from my controller. How would I go about this?

1) Instead of creating the Controllers via Factory, can I give them somehow else access to my model Checkers? (all the game logic is stored there etc.)

2) How do I basically then recreate my first window? (created by startGame())

Thank you very much!! Best, Lisa

1

1 Answers

2
votes

You are calling GameMenuController gameMenuControl = loader2.getController() before you call loader2.load(). The controller is created as part of the load process, so gameMenuControl is going to be null.

You could simply move the calls to loader2.getController() and gameMenuControl.doRestart.addListener(...) to immediately after calling loader2.load().

Alternatively, just create the controller yourself, instead of delegating to the controller factory. Remove the fx:controller attribute from the FXML file, and then do:

@Override
public void start(Stage primaryStage) throws Exception {

    // You can make similar changes to loader1 if you want...

    loader2.setLocation(getClass().getResource("GameMenu.fxml"));

    GameMenuController gameMenuControl = new GameMenuController(content) ;

    gameMenuControl.doRestart.addListener(new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
            if(newValue) {
            scene.setRoot(startGame());
            primaryStage.setHeight(scene.getHeight());
            primaryStage.setWidth(scene.getWidth());
            gameMenuControl.doRestart.set(false);
            }
        }
    });

    loader2.setController(gameMenuControl);

    content.gameOver.addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                try {
                    scene.setRoot(loader2.load());
                    primaryStage.setHeight(180);
                    primaryStage.setWidth(240);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        });

    Scene scene = new Scene(startGame());
    primaryStage.setTitle("Checkers");
    primaryStage.setScene(scene);
    primaryStage.show();


}