Search code examples
javajavafxhexagonal-tiles

Create hexagonal field with flat tiles with JavaFX


I want to create an hexagonal field with flat tiles in JavaFX. The following stackoverflow question allows to create a field with pointy tiles: Create hexagonal field with JavaFX

This code example works perfectly with pointy tiles:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;

public class UISolution extends Application {

    private final static int WINDOW_WIDTH = 800;
    private final static int WINDOW_HEIGHT = 600;

    private final static double r= 20; // the inner radius from hexagon center to outer corner
    private final static double n= Math.sqrt(r * r * 0.75); // the inner radius from hexagon center to middle of the axis
    private final static double TILE_HEIGHT = 2 * r;
    private final static double TILE_WIDTH = 2 * n;

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

    public void start(Stage primaryStage) {
        AnchorPane tileMap = new AnchorPane();
        Scene content = new Scene(tileMap, WINDOW_WIDTH, WINDOW_HEIGHT);
        primaryStage.setScene(content);

        int rowCount = 4; // how many rows of tiles should be created
        int tilesPerRow = 6; // the amount of tiles that are contained in each row
        int xStartOffset = 40; // offsets the entire field to the right
        int yStartOffset = 40; // offsets the entire fiels downwards

        for (int x = 0; x < tilesPerRow; x++) {
            for (int y = 0; y < rowCount; y++) {
                double xCoord = x * TILE_WIDTH + (y % 2) * n + xStartOffset;
                double yCoord = y * TILE_HEIGHT * 0.75 + yStartOffset;
                
                Polygon tile = new Tile(xCoord, yCoord);
                tileMap.getChildren().add(tile);
            }
        }
        primaryStage.show();
    }

    private class Tile extends Polygon {
        Tile(double x, double y) {
            // creates the polygon using the corner coordinates
            getPoints().addAll(
                    x, y,
                    x, y + r,
                    x + n, y + r * 1.5,
                    x + TILE_WIDTH, y + r,
                    x + TILE_WIDTH, y,
                    x + n, y - r * 0.5
            );

            // set up the visuals and a click listener for the tile
            setFill(Color.ANTIQUEWHITE);
            setStrokeWidth(1);
            setStroke(Color.BLACK);
            setOnMouseClicked(e -> System.out.println("Clicked: " + this));
        }
    }
}

I think that I only have to modify the part here:

     getPoints().addAll(
         x, y,
         x, y + r,
         x + n, y + r * 1.5,
         x + TILE_WIDTH, y + r,
         x + TILE_WIDTH, y,
         x + n, y - r * 0.5
     );

but I'm struggling to have a correct shape and position for my tiles. And if I'm doing:

  getPoints().addAll(x, y,
     x + n * 0.5, y + r,
     x + n * 1.5, y + r,
     x + TILE_WIDTH, y,
     x + n * 1.5, y - r,
     x + n * 0.5, y - r
  );

the tiles have a correct flat shape but are not positioned correctly relative to each other. I think that this time I should modify the following code:

     double xCoord = x * TILE_WIDTH + (y % 2) * n + xStartOffset;
     double yCoord = y * TILE_HEIGHT * 0.75 + yStartOffset;

An example of the pointy tiles result with this code:

enter image description here


Solution

  • I found a solution for flat tiles. Here it is:

    package org.hexagon.check;
    
    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    import javafx.scene.layout.AnchorPane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Polygon;
    
    public class HexagonFlat extends Application {
       private final static double TILE_WIDTH = 20;
       private final static double TILE_HEIGHT = TILE_WIDTH;
       private final static int WINDOW_WIDTH = 800;
       private final static int WINDOW_HEIGHT = 600;
       double v = Math.sqrt(3) / 2.0;
       double v2 = Math.sqrt(3);
    
       public static void main(String[] args) {
          launch(args);
       }
    
       public void start(Stage primaryStage) {
          AnchorPane tileMap = new AnchorPane();
          Scene content = new Scene(tileMap, WINDOW_WIDTH, WINDOW_HEIGHT);
          primaryStage.setScene(content);
    
          int rowCount = 4; // how many rows of tiles should be created
          int tilesPerRow = 6; // the amount of tiles that are contained in each row
          int xStartOffset = 40; // offsets the entire field to the right
          int yStartOffset = 40; // offsets the entire fiels downwards
          for (int y = 0; y < rowCount; y++) {
             double yCoordInit = yStartOffset + y * TILE_WIDTH * v2;
             double yCoord = yCoordInit;
             for (int x = 0; x < tilesPerRow; x++) {
                double xCoord = 1.5 * x * TILE_WIDTH + xStartOffset;
                Polygon tile = new Tile(xCoord, yCoord);
                tileMap.getChildren().add(tile);
                yCoord = yCoord == yCoordInit ? yCoord + TILE_HEIGHT * v : yCoordInit;
             }
          }
          primaryStage.show();
       }
    
       private class Tile extends Polygon {
          Tile(double x, double y) {
             // creates the polygon using the corner coordinates
             getPoints().addAll(
                x, y,
                x + TILE_WIDTH, y,
                x + TILE_WIDTH * 1.5, y + TILE_HEIGHT * v,
                x + TILE_WIDTH, y + TILE_HEIGHT * v2,
                x, y + TILE_WIDTH * v2,
                x - (TILE_WIDTH / 2.0), y + TILE_HEIGHT * v
             );
             // set up the visuals and a click listener for the tile
             setFill(Color.ANTIQUEWHITE);
             setStrokeWidth(1);
             setStroke(Color.BLACK);
             setOnMouseClicked(e -> System.out.println("Clicked: " + this));
          }
       }
    }
    

    The result is:

    enter image description here