javajavafxgridpane

Why do the node positions in my gridpane affect the layout of my grid?


I'm trying to recreate the "snake" game using GridPane in JavaFX. My code seems to run properly except for this specific error, where anytime I use my keys to traverse the green node (titled head) toward the yellow node (titled food), the rows or columns of the grid seem to shorten by one unit, causing the grid to collapse in some manner. Is there a way to stop the GridPane from resizing? Below is my code:

package snake;

import java.util.ArrayList;
import java.util.Random;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class snakemain extends Application {
    Random random = new Random();
    int posX = random.nextInt(21), posY = random.nextInt(21), foodposX = random.nextInt(24), foodposY = random.nextInt(24);

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

    public void start(Stage stage) {
        //Creates Scene and stage setting + gridpane
        stage.setTitle("Snake by Yeldor");
        GridPane gridpane = new GridPane();
        Random random = new Random();
        Scene scene = new Scene(gridpane, 500, 500);
        scene.setFill(Color.WHITE);
        gridpane.setGridLinesVisible(true);
        gridpane.setHgap(20);
        gridpane.setVgap(20);
        //creates head of snake, Arraylist of snakes body parts, and food consumable for snake
        ArrayList<Rectangle> snakeBody = new ArrayList<Rectangle>();
        Rectangle head = new Rectangle(20,20,Color.GREEN.brighter().brighter());
        //invisible block to manage grid
        Rectangle food = new Rectangle(20,20, Color.YELLOW.brighter().brighter());
        //adds rectangles to grippane
        gridpane.add(head, posX, posY);
        gridpane.add(food, foodposX, foodposY);
        //makes food non mangaged by the gridpane
        scene.setOnKeyPressed(e -> 
        {
            String s = e.getCode().toString();
        try {

            switch(s) {
            case "W":   gridpane.getChildren().remove(head);
                        gridpane.add(head, posX, --posY);
                    break;
            case "A":   gridpane.getChildren().remove(head);
                        gridpane.add(head, --posX, posY);
                    break;
            case "S":   gridpane.getChildren().remove(head);
                        gridpane.add(head, posX, ++posY);
                    break;
            case "D":   gridpane.getChildren().remove(head);
                        gridpane.add(head, ++posX, posY);
                    break;
            }


            if (posX == 24 || posY == 24 || posX == -1 || posY == -1) {
                missionFailed();
                stage.close();
            }

        }

        catch(IllegalArgumentException error) {
            missionFailed();
            stage.close();
        }

        });

        stage.setScene(scene);
        stage.show();

    }

    void missionFailed() {
        Stage failedPopup = new Stage();
        failedPopup.setTitle("You Died!");
        failedPopup.initModality(Modality.APPLICATION_MODAL);
        Button OK = new Button("OK");
        Group group = new Group();
        Scene miniScene = new Scene(group, 150, 100);
        group.getChildren().add(OK);
        failedPopup.setScene(miniScene);
        failedPopup.show();
        OK.setOnMouseClicked(q -> {
            failedPopup.close();
        });
    }

}

Solution

  • Using hgap and vgap does not modify the column size, it modifies the space between columns. If you e.g. place the Rectangles next to each other horizontally (same row index, column index differing by 1), the distance between both nodes is vgap; there are exactly 2 columns that are filled and have a width != 0. Now move the Rectangles to the same cell and only a single column with a width != 0 leading to unexpected results.

    Instead you should restrict the size of the rows/columns:

    // gridpane.setHgap(20);
    // gridpane.setVgap(20);
    
    int rows = 500 / 20;
    int columns = 500 / 20;
    
    RowConstraints rConstraints = new RowConstraints(20);
    ColumnConstraints cConstraints = new ColumnConstraints(20);
    
    for (int i = 0; i < columns; i++) {
        gridpane.getColumnConstraints().add(cConstraints);
    }
    for (int i = 0; i < rows; i++) {
        gridpane.getRowConstraints().add(rConstraints);
    }