Hey all,
Needed a hex board factory / generator for when I hopefully have time to put a game together and have come up with the following…
Hey all,
Needed a hex board factory / generator for when I hopefully have time to put a game together and have come up with the following…
is it just drawing or even calculating hexagons?
Calculating then drawing to an image (in this example) you can specify how big you want the board based on “rings” around the center hex. You can see it all at the github link I posted.
Here’s some code.
import java.awt.Color;
import java.awt.Polygon;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
public class Hex {
private String identifier;
private int size = 100;
private Point2D.Double middlePoint;
private Point2D.Double[] points;
private Polygon poly;
private Hex adjacentHexNorth;
private Hex adjacentHexNorthEast;
private Hex adjacentHexSouthEast;
private Hex adjacentHexSouth;
private Hex adjacentHexSouthWest;
private Hex adjacentHexNorthWest;
private Color lineColor = Color.decode("#BED6FF");
private Color fillColor = Color.decode("#1E1E1E");
private boolean accessible = true;
private boolean decorated = false;
private BufferedImage decoration;
/**
* Constructor
*
* @param point {@link Point2D.Double}
* @param identifier {@link String}
*/
public Hex(Point2D.Double point, String identifier) {
this.identifier = identifier;
this.middlePoint = point;
double h = calculateH(size);
double r = calculateR(size);
double x = (point.getX() - (size / 2));
double y = (point.getY() - r);
this.points = new Point2D.Double[6];
this.points[0] = new Point2D.Double(x, y);
this.points[1] = new Point2D.Double(x + size, y);
this.points[2] = new Point2D.Double(x + size + h, y + r);
this.points[3] = new Point2D.Double(x + size, y + r + r);
this.points[4] = new Point2D.Double(x, y + r + r);
this.points[5] = new Point2D.Double(x - h, y + r);
int[] xPoints = new int[6];
int[] yPoints = new int[6];
for (int i = 0; i < points.length; i++) {
xPoints[i] = (int) points[i].getX();
yPoints[i] = (int) points[i].getY();
}
this.poly = new Polygon(xPoints, yPoints, 6);
}
/**
* Returns true if the passed in point is within this hex
*
* @param point {@link Point2D.Double}
* @return {@link boolean}
*/
public boolean isPointInHex(Point2D.Double point) {
int j = points.length - 1;
boolean oddNodes = false;
for (int i = 0; i < points.length; i++) {
if (points[i].getY() < point.getY() && points[j].getY() >= point.getY() || points[j].getY() < point.getY() && points[i].getY() >= point.getY()) {
if (points[i].getX() + (point.getY() - points[i].getY()) / (points[j].getY() - points[i].getY()) * (points[j].getX() - points[i].getX()) < point
.getX()) {
oddNodes = !oddNodes;
}
}
j = i;
}
return oddNodes;
}
// Split these 3 functions into separate util class?
private float calculateH(float side) {
return (float) (Math.sin(degreesToRadians(30)) * side);
}
private float calculateR(float side) {
return (float) (Math.cos(degreesToRadians(30)) * side);
}
private double degreesToRadians(double degrees) {
return degrees * Math.PI / 180;
}
// Getter and Setters
public float getHeight() {
float r = calculateR(size);
return r + r;
}
public float getWidth() {
float h = calculateH(size);
return size + h;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public Point2D.Double getMiddlePoint() {
return middlePoint;
}
public void setMiddlePoint(Point2D.Double middlePoint) {
this.middlePoint = middlePoint;
}
public Point2D.Double[] getPoints() {
return points;
}
public void setPoints(Point2D.Double[] points) {
this.points = points;
}
public Polygon getPoly() {
return poly;
}
public void setPoly(Polygon poly) {
this.poly = poly;
}
public Color getLineColor() {
return lineColor;
}
public void setLineColor(Color lineColor) {
this.lineColor = lineColor;
}
public Color getFillColor() {
return fillColor;
}
public void setFillColor(Color fillColor) {
this.fillColor = fillColor;
}
public boolean isAccessible() {
return accessible;
}
public void setAccessible(boolean accessible) {
this.accessible = accessible;
}
public boolean isDecorated() {
return decorated;
}
public void setDecorated(boolean decorated) {
this.decorated = decorated;
}
public BufferedImage getDecoration() {
return decoration;
}
public void setDecoration(BufferedImage decoration) {
this.decoration = decoration;
}
public Hex getAdjacentHexNorth() {
return adjacentHexNorth;
}
public void setAdjacentHexNorth(Hex adjacentHexNorth) {
this.adjacentHexNorth = adjacentHexNorth;
}
public Hex getAdjacentHexNorthEast() {
return adjacentHexNorthEast;
}
public void setAdjacentHexNorthEast(Hex adjacentHexNorthEast) {
this.adjacentHexNorthEast = adjacentHexNorthEast;
}
public Hex getAdjacentHexSouthEast() {
return adjacentHexSouthEast;
}
public void setAdjacentHexSouthEast(Hex adjacentHexSouthEast) {
this.adjacentHexSouthEast = adjacentHexSouthEast;
}
public Hex getAdjacentHexSouth() {
return adjacentHexSouth;
}
public void setAdjacentHexSouth(Hex adjacentHexSouth) {
this.adjacentHexSouth = adjacentHexSouth;
}
public Hex getAdjacentHexSouthWest() {
return adjacentHexSouthWest;
}
public void setAdjacentHexSouthWest(Hex adjacentHexSouthWest) {
this.adjacentHexSouthWest = adjacentHexSouthWest;
}
public Hex getAdjacentHexNorthWest() {
return adjacentHexNorthWest;
}
public void setAdjacentHexNorthWest(Hex adjacentHexNorthWest) {
this.adjacentHexNorthWest = adjacentHexNorthWest;
}
}
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ajb.domain.Hex;
import ajb.domain.HexBoard;
public class HexBoardFactory {
/**
* Creates a new {@link HexBoard} with the amount of rings as per the passed in value
*
* @param amountOfRings {@link int}
* @return {@link HexBoard}
*/
public HexBoard createHexBoard(int amountOfRings) {
HexBoard hexBoard = createBlankHexBoard(amountOfRings);
return hexBoard;
}
/**
* Creates a new {@link HexBoard} with the amount of rings as per the passed in value
*
* @param amountOfRings {@link int}
* @return {@link HexBoard}
*/
private HexBoard createBlankHexBoard(int ringCount) {
List<Hex> hexList = new ArrayList<Hex>();
Hex hex00 = new Hex(new Point2D.Double(0, 0), "0,0");
hexList.add(hex00);
for (int i = 1; i < ringCount + 1; i++) {
createHexRing(i, hexList);
}
HexBoard hexBoard = new HexBoard(hexList);
hexBoard.setAmountOfRings(ringCount);
hexBoard.setHexesByIdentifier(createHexesByIdentifierMap(hexList));
calculateAdjacentHexes(hexBoard);
return hexBoard;
}
/**
* Returns a Map of {@link Hex}'s by their identifier
*
* @param hexList {@link List} of {@link Hex}
* @return {@link Map}[Key:{@link String}][Value:{@link Hex}]
*/
private Map<String, Hex> createHexesByIdentifierMap(List<Hex> hexList) {
Map<String, Hex> hexesByIdentifierMap = new HashMap<String, Hex>();
for (Hex hex : hexList) {
hexesByIdentifierMap.put(hex.getIdentifier(), hex);
}
return hexesByIdentifierMap;
}
private void createHexRing(int modifier, List<Hex> hexList) {
Hex sampleHex = new Hex(new Point2D.Double(0, 0), "SampleHex");
float height = sampleHex.getHeight();
float width = sampleHex.getWidth();
float halfHeight = height / 2;
Integer hexCount = 1;
// North
Hex hex = new Hex(new Point2D.Double(0, 0 - (height * modifier)), modifier + "," + hexCount);
hexList.add(hex);
hexCount++;
hexCount = createNorthToNorthEastFillerHexes(modifier, height, width, halfHeight, hexList, hexCount);
// North East
hex = new Hex(new Point2D.Double(0 + (width * modifier), 0 - (halfHeight * modifier)), modifier + "," + hexCount);
hexList.add(hex);
hexCount++;
hexCount = createNorthEastToSoutEastFillerHexes(modifier, height, width, halfHeight, hexList, hexCount);
// South East
hex = new Hex(new Point2D.Double(0 + (width * modifier), 0 + (halfHeight * modifier)), modifier + "," + hexCount);
hexList.add(hex);
hexCount++;
hexCount = createSouthEastToSouthFillerHexes(modifier, height, width, halfHeight, hexList, hexCount);
// South
hex = new Hex(new Point2D.Double(0, 0 + (height * modifier)), modifier + "," + hexCount);
hexList.add(hex);
hexCount++;
hexCount = createSouthToSouthWestFillerHexes(modifier, height, width, halfHeight, hexList, hexCount);
// South West
hex = new Hex(new Point2D.Double(0 - (width * modifier), 0 + (halfHeight * modifier)), modifier + "," + hexCount);
hexList.add(hex);
hexCount++;
hexCount = createSouthWestToNorthWestFillerHexes(modifier, height, width, halfHeight, hexList, hexCount);
// North West
hex = new Hex(new Point2D.Double(0 - (width * modifier), 0 - (halfHeight * modifier)), modifier + "," + hexCount);
hexList.add(hex);
hexCount++;
hexCount = createNorthWestToNorthFillerHexes(modifier, height, width, halfHeight, hexList, hexCount);
}
private int createNorthToNorthEastFillerHexes(int modifier, float height, float width, float halfHeight, List<Hex> hexList, int hexCount) {
float posX = 0 + width;
float posY = 0 - (height * modifier) + halfHeight;
for (int i = 1; i < modifier; i++) {
Hex hex = new Hex(new Point2D.Double(posX, posY), modifier + "," + hexCount);
hexList.add(hex);
posX += width;
posY += halfHeight;
hexCount++;
}
return hexCount;
}
private int createNorthEastToSoutEastFillerHexes(int modifier, float height, float width, float halfHeight, List<Hex> hexList, int hexCount) {
float posX = 0 + (width * modifier);
float posY = 0 - (halfHeight * modifier) + height;
for (int i = 1; i < modifier; i++) {
Hex hex = new Hex(new Point2D.Double(posX, posY), modifier + "," + hexCount);
hexList.add(hex);
posY += height;
hexCount++;
}
return hexCount;
}
private int createSouthEastToSouthFillerHexes(int modifier, float height, float width, float halfHeight, List<Hex> hexList, int hexCount) {
float posX = 0 + (width * modifier) - width;
float posY = 0 + (halfHeight * modifier) + halfHeight;
for (int i = 1; i < modifier; i++) {
Hex hex = new Hex(new Point2D.Double(posX, posY), modifier + "," + hexCount);
hexList.add(hex);
posX -= width;
posY += halfHeight;
hexCount++;
}
return hexCount;
}
private int createSouthToSouthWestFillerHexes(int modifier, float height, float width, float halfHeight, List<Hex> hexList, int hexCount) {
float posX = 0 - width;
float posY = 0 + (height * modifier) - halfHeight;
for (int i = 1; i < modifier; i++) {
Hex hex = new Hex(new Point2D.Double(posX, posY), modifier + "," + hexCount);
hexList.add(hex);
posX -= width;
posY -= halfHeight;
hexCount++;
}
return hexCount;
}
private int createSouthWestToNorthWestFillerHexes(int modifier, float height, float width, float halfHeight, List<Hex> hexList, int hexCount) {
float posX = 0 - (width * modifier);
float posY = 0 + (halfHeight * modifier) - height;
for (int i = 1; i < modifier; i++) {
Hex hex = new Hex(new Point2D.Double(posX, posY), modifier + "," + hexCount);
hexList.add(hex);
posY -= height;
hexCount++;
}
return hexCount;
}
private int createNorthWestToNorthFillerHexes(int modifier, float height, float width, float halfHeight, List<Hex> hexList, int hexCount) {
float posX = 0 - (width * modifier) + width;
float posY = 0 - (halfHeight * modifier) - halfHeight;
for (int i = 1; i < modifier; i++) {
Hex hex = new Hex(new Point2D.Double(posX, posY), modifier + "," + hexCount);
hexList.add(hex);
posX += width;
posY -= halfHeight;
hexCount++;
}
return hexCount;
}
private void calculateAdjacentHexes(HexBoard hexBoard) {
for (Hex hex : hexBoard.getHexList()) {
Point2D.Double currentHexCenter = hex.getMiddlePoint();
Point2D.Double adjacentNorthHexCenter = new Point2D.Double(currentHexCenter.getX(), currentHexCenter.getY() - hex.getHeight());
Point2D.Double adjacentNorthEastHexCenter = new Point2D.Double(currentHexCenter.getX() + hex.getWidth(), currentHexCenter.getY()
- (hex.getHeight() / 2));
Point2D.Double adjacentSouthEastHexCenter = new Point2D.Double(currentHexCenter.getX() + hex.getWidth(), currentHexCenter.getY()
+ (hex.getHeight() / 2));
Point2D.Double adjacentSouthHexCenter = new Point2D.Double(currentHexCenter.getX(), currentHexCenter.getY() + hex.getHeight());
Point2D.Double adjacentSouthWestHexCenter = new Point2D.Double(currentHexCenter.getX() - hex.getWidth(), currentHexCenter.getY()
+ (hex.getHeight() / 2));
Point2D.Double adjacentNorthWestHexCenter = new Point2D.Double(currentHexCenter.getX() - hex.getWidth(), currentHexCenter.getY()
- (hex.getHeight() / 2));
Hex adjacentNorthHex = null;
Hex adjacentNorthEastHex = null;
Hex adjacentSouthEastHex = null;
Hex adjacentSouthHex = null;
Hex adjacentSouthWestHex = null;
Hex adjacentNorthWestHex = null;
List<Hex> otherHexes = new ArrayList<Hex>();
otherHexes.addAll(hexBoard.getHexList());
otherHexes.remove(hex);
for (Hex otherHex : otherHexes) {
if (otherHex.isPointInHex(adjacentNorthHexCenter)) {
adjacentNorthHex = otherHex;
}
if (otherHex.isPointInHex(adjacentNorthEastHexCenter)) {
adjacentNorthEastHex = otherHex;
}
if (otherHex.isPointInHex(adjacentSouthEastHexCenter)) {
adjacentSouthEastHex = otherHex;
}
if (otherHex.isPointInHex(adjacentSouthHexCenter)) {
adjacentSouthHex = otherHex;
}
if (otherHex.isPointInHex(adjacentSouthWestHexCenter)) {
adjacentSouthWestHex = otherHex;
}
if (otherHex.isPointInHex(adjacentNorthWestHexCenter)) {
adjacentNorthWestHex = otherHex;
}
}
hex.setAdjacentHexNorth(adjacentNorthHex);
hex.setAdjacentHexNorthEast(adjacentNorthEastHex);
hex.setAdjacentHexSouthEast(adjacentSouthEastHex);
hex.setAdjacentHexSouth(adjacentSouthHex);
hex.setAdjacentHexSouthWest(adjacentSouthWestHex);
hex.setAdjacentHexNorthWest(adjacentNorthWestHex);
}
}
}
uugh, maybe you should put that into a spoiler or such…
I ment converting a xy coordinate to a hexagon-compatible one and back.
You seem to have two hexagons labelled 2,2
.
Personally I would pick two of the three natural axes, call one x
and the other y
, and work out which 2 of the 4 vectors (-1,-1)
, (-1,1)
, (1,-1)
, and (1,1)
correspond to the third axis.
Good call - funny, but I never noticed that allot of the identifiers are duplicated. needs some more work!
Superb! Thank you for sharing! I have been thinking about doing hex for the longest but keep getting distracted from it. Looks like you are planning a space game!
That’s the plan if I ever find enough time to put into it.
Fixed the issue with the identifiers and checked it in - have update the image and the code blocks.