user8322140
user8322140

Reputation: 15

Screen resolution scale in JavaFx game

I'm creating a board game in JavaFx, and it works fine in my desktop PC, with a 1920x1080 screen. When i try to open it in my Laptop (which is 1920x1080 as well, but with the default screen size at 125%), it doesn't show the elements properly anymore (their size overflows the bounds of the screen). The only way to make the app work properly is to run it on a 1920x1080 screen with default scree size at 100%

I need a way to scale the whole stage to the right resolution

This is my Main.java:

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        final double WIDTH = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
        final double HEIGHT = Toolkit.getDefaultToolkit().getScreenSize().getHeight();

        Parent board = FXMLLoader.load(getClass().getResource("/board.fxml"));

        Pane innerBoard = ((Pane)board.getChildrenUnmodifiable().get(0));
        Assert.assertCmp(innerBoard.getId(), "BOARD");

        Scene scene = new Scene(board, WIDTH, HEIGHT);

        scene.getStylesheets().add(getClass().getResource("/style.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.setFullScreen(true);
        primaryStage.setResizable(true);
        innerBoard.setVisible(false);
        
        primaryStage.setFullScreenExitHint("");
        primaryStage.show();

        
        innerBoard.setVisible(true);
 
    }

}

This is the FXML:

<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Pane id="BOARD" fx:id="innerBoard" layoutX="356.0" layoutY="-7.0" style="-fx-background-color: #cfffd1;">
         <children>
            <!-- Lots of children -->
         </children>
      </Pane>
   </children>
</Pane>

Upvotes: 1

Views: 77

Answers (1)

VGR
VGR

Reputation: 44414

I recommend you forego Pane and numeric positions and sizes. You want your board to be capable of displaying in any resolution; explicit positions and sizes directly contradict that.

Layouts are the best way to make your board fit into any size.

(Of course, layouts only work with rectangular board regions; I will assume you aren’t going for something complex like a circular or hexagon-based board, or a board with winding paths. In those cases, a Canvas may be a better choice than a Pane.)

GridPane allows setting RowConstraints and ColumnConstraints, each of which can be percentage-based. If rows and columns have a size that is a percentage of the width of their parent, you get scaling for free. Here is a very primitive rendition of a Monopoly board:

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.Priority;

public class MonopolyBoard
extends Application {
    // Source:
    // https://commons.wikimedia.org/wiki/File:Store_Building_Flat_Icon_Vector.svg
    private static final String SIDE_SPACE_IMAGE_URI =
        "https://upload.wikimedia.org/wikipedia/commons/thumb/5/53"
        + "/Store_Building_Flat_Icon_Vector.svg"
        + "/240px-Store_Building_Flat_Icon_Vector.svg.png";

    // Source:
    // https://commons.wikimedia.org/wiki/File:Car_icon_transparent.png
    private static final String CORNER_SPACE_IMAGE_URI =
        "https://upload.wikimedia.org/wikipedia/commons/7/7e"
        + "/Car_icon_transparent.png";

    @Override
    public void start(Stage stage) {
        GridPane grid = new GridPane();

        Node[] row = new Node[11];

        row[0] = createSpace(CORNER_SPACE_IMAGE_URI);
        row[10] = createSpace(CORNER_SPACE_IMAGE_URI);
        for (int i = 1; i <= 9; i++) {
            row[i] = createSpace(SIDE_SPACE_IMAGE_URI);
        }
        grid.addRow(0, row);

        for (int i = 1; i <= 9; i++) {
            grid.add(createSpace(SIDE_SPACE_IMAGE_URI), 0, i);
            grid.add(createSpace(SIDE_SPACE_IMAGE_URI), 10, i);
        }

        row[0] = createSpace(CORNER_SPACE_IMAGE_URI);
        row[10] = createSpace(CORNER_SPACE_IMAGE_URI);
        for (int i = 1; i <= 9; i++) {
            row[i] = createSpace(SIDE_SPACE_IMAGE_URI);
        }
        grid.addRow(10, row);

        grid.getColumnConstraints().add(createColumnConstraints(2 / 11.0));
        for (int i = 1; i <= 9; i++) {
            grid.getColumnConstraints().add(createColumnConstraints(1 / 11.0));
        }
        grid.getColumnConstraints().add(createColumnConstraints(2 / 11.0));

        grid.getRowConstraints().add(createRowConstraints(2 / 11.0));
        for (int i = 1; i <= 9; i++) {
            grid.getRowConstraints().add(createRowConstraints(1 / 11.0));
        }
        grid.getRowConstraints().add(createRowConstraints(2 / 11.0));

        stage.setScene(new Scene(grid, 50 * 11, 50 * 11));
        stage.setTitle("Monopoly");
        stage.show();
    }

    private ColumnConstraints createColumnConstraints(double percentage) {
        ColumnConstraints constraints = new ColumnConstraints();
        constraints.setPercentWidth(percentage * 100);
        constraints.setHgrow(Priority.ALWAYS);
        return constraints;
    }

    private RowConstraints createRowConstraints(double percentage) {
        RowConstraints constraints = new RowConstraints();
        constraints.setPercentHeight(percentage * 100);
        constraints.setVgrow(Priority.ALWAYS);
        return constraints;
    }

    private Node createSpace(String imageURI) {
        BorderPane container = new BorderPane();
        container.setStyle(
            "-fx-border-width: 1px;"
            + " -fx-border-style: solid;"
            + " -fx-background-image: url('" + imageURI + "');"
            + " -fx-background-position: center;"
            + " -fx-background-repeat: no-repeat;"
            + " -fx-background-size: contain;");

        return container;
    }

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

In addition to using the percentage properties of RowConstraints and ColumnConstraints, the above code takes advantage of CSS image scaling using -fx-background-size: contain.

Resize (or maximize) the window to see the scaling at work. Obviously, if it were fullscreen, the same scaling would apply.

Upvotes: 2

Related Questions