Fix hex grid rendering - proper pointy-top hexagons with tight adjacency
This commit is contained in:
114
public/game.js
114
public/game.js
@@ -5,10 +5,9 @@
|
||||
import { HexMap, CELL_TYPES } from './map.js';
|
||||
|
||||
// Game constants
|
||||
const HEX_SIZE = 20;
|
||||
const HEX_SIZE = 18;
|
||||
const MAP_SIZE = 20;
|
||||
const ANIMATION_DURATION = 300;
|
||||
const ISO_ANGLE = Math.PI / 6; // 30 degrees for isometric view
|
||||
|
||||
// Colors
|
||||
const COLORS = {
|
||||
@@ -96,12 +95,13 @@ class GameUI {
|
||||
const canvasWidth = this.canvas.width;
|
||||
const canvasHeight = this.canvas.height;
|
||||
|
||||
// Calculate map dimensions for isometric view
|
||||
const mapWidth = MAP_SIZE * HEX_SIZE * 2;
|
||||
const mapHeight = MAP_SIZE * HEX_SIZE;
|
||||
const sqrt3 = Math.sqrt(3);
|
||||
// Map dimensions for pointy-top hex grid
|
||||
const mapWidth = HEX_SIZE * sqrt3 * (MAP_SIZE + MAP_SIZE/2);
|
||||
const mapHeight = HEX_SIZE * 1.5 * (MAP_SIZE - 1) + HEX_SIZE * 2;
|
||||
|
||||
this.offsetX = canvasWidth / 2;
|
||||
this.offsetY = HEX_SIZE * 2;
|
||||
this.offsetX = (canvasWidth - mapWidth) / 2 + HEX_SIZE * sqrt3;
|
||||
this.offsetY = (canvasHeight - mapHeight) / 2 + HEX_SIZE;
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
@@ -115,27 +115,26 @@ class GameUI {
|
||||
}
|
||||
|
||||
hexToPixel(q, r) {
|
||||
// Isometric projection for pointy-top hex grid
|
||||
// q axis goes top-left to bottom-right
|
||||
// r axis goes top-right to bottom-left
|
||||
const isoX = (q - r) * HEX_SIZE * 1.5;
|
||||
const isoY = (q + r) * HEX_SIZE * 0.75;
|
||||
// Pointy-top hex grid with proper adjacency
|
||||
// For pointy-top: width = sqrt(3) * size, height = 2 * size
|
||||
// Horizontal spacing = width = sqrt(3) * size
|
||||
// Vertical spacing = 3/4 * height = 1.5 * size
|
||||
const sqrt3 = Math.sqrt(3);
|
||||
|
||||
return {
|
||||
x: this.offsetX + isoX,
|
||||
y: this.offsetY + isoY
|
||||
};
|
||||
// Convert axial to pixel coordinates for pointy-top hex
|
||||
const x = this.offsetX + HEX_SIZE * sqrt3 * (q + r/2);
|
||||
const y = this.offsetY + HEX_SIZE * 1.5 * r;
|
||||
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
pixelToHex(x, y) {
|
||||
const adjX = x - this.offsetX;
|
||||
const adjY = y - this.offsetY;
|
||||
|
||||
// Reverse isometric projection
|
||||
// From: isoX = (q - r) * HEX_SIZE * 1.5
|
||||
// isoY = (q + r) * HEX_SIZE * 0.75
|
||||
const q = (adjX / (HEX_SIZE * 1.5) + adjY / (HEX_SIZE * 0.75)) / 2;
|
||||
const r = (adjY / (HEX_SIZE * 0.75) - adjX / (HEX_SIZE * 1.5)) / 2;
|
||||
const sqrt3 = Math.sqrt(3);
|
||||
const q = (adjX / (HEX_SIZE * sqrt3) - adjY / (HEX_SIZE * 1.5) / 2);
|
||||
const r = adjY / (HEX_SIZE * 1.5);
|
||||
|
||||
const qi = Math.round(q);
|
||||
const ri = Math.round(r);
|
||||
@@ -147,26 +146,23 @@ class GameUI {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get neighboring cells for isometric hex grid
|
||||
* Directions for pointy-top hex in axial coordinates with isometric projection:
|
||||
* hexToPixel: isoX = (q-r)*1.5, isoY = (q+r)*0.75
|
||||
*
|
||||
* Visual directions:
|
||||
* - top: (-1, -1) - both decrease
|
||||
* - bottom: (+1, +1) - both increase
|
||||
* - top-left: (-1, 0) - q decreases
|
||||
* - bottom-left: (0, +1) - r increases
|
||||
* - top-right: (0, -1) - r decreases
|
||||
* - bottom-right: (+1, 0) - q increases
|
||||
* Get neighboring cells for pointy-top hex grid
|
||||
* Directions for pointy-top hex in axial coordinates:
|
||||
* - north-east: (+1, -1)
|
||||
* - north-west: (0, -1)
|
||||
* - west: (-1, 0)
|
||||
* - south-west: (-1, +1)
|
||||
* - south-east: (0, +1)
|
||||
* - east: (+1, 0)
|
||||
*/
|
||||
getValidTargets(q, r) {
|
||||
const directions = [
|
||||
[+1, 0], // bottom-right
|
||||
[+1, +1], // bottom (straight down)
|
||||
[0, +1], // bottom-left
|
||||
[-1, 0], // top-left
|
||||
[-1, -1], // top (straight up)
|
||||
[0, -1] // top-right
|
||||
[+1, -1], // north-east
|
||||
[0, -1], // north-west
|
||||
[-1, 0], // west
|
||||
[-1, +1], // south-west
|
||||
[0, +1], // south-east
|
||||
[+1, 0] // east
|
||||
];
|
||||
|
||||
const targets = [];
|
||||
@@ -186,19 +182,18 @@ class GameUI {
|
||||
drawHex(q, r, fillStyle, strokeStyle = COLORS.stroke, lineWidth = 2) {
|
||||
const { x, y } = this.hexToPixel(q, r);
|
||||
|
||||
// Draw elongated hexagon for isometric view
|
||||
const hexWidth = HEX_SIZE * 1.3;
|
||||
const hexHeight = HEX_SIZE * 0.75;
|
||||
// Draw pointy-top hexagon - size matches grid spacing
|
||||
const size = HEX_SIZE * 0.98; // Almost full size for tight fit
|
||||
|
||||
// Hexagon vertices for isometric view (elongated horizontally)
|
||||
const vertices = [
|
||||
{ x: x + hexWidth, y: y }, // right
|
||||
{ x: x + hexWidth * 0.5, y: y - hexHeight }, // top-right
|
||||
{ x: x - hexWidth * 0.5, y: y - hexHeight }, // top-left
|
||||
{ x: x - hexWidth, y: y }, // left
|
||||
{ x: x - hexWidth * 0.5, y: y + hexHeight }, // bottom-left
|
||||
{ x: x + hexWidth * 0.5, y: y + hexHeight } // bottom-right
|
||||
];
|
||||
// Pointy-top hexagon vertices (flat sides left/right)
|
||||
const vertices = [];
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const angle = Math.PI / 6 + (Math.PI / 3) * i; // Start at 30 degrees
|
||||
vertices.push({
|
||||
x: x + size * Math.cos(angle),
|
||||
y: y + size * Math.sin(angle)
|
||||
});
|
||||
}
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(vertices[0].x, vertices[0].y);
|
||||
@@ -247,17 +242,16 @@ class GameUI {
|
||||
const ownerColor = cell.getOwner() === 1 ? COLORS.player1 : COLORS.player2;
|
||||
|
||||
// Draw border matching the hexagon shape
|
||||
const hexWidth = HEX_SIZE * 1.3 - 3;
|
||||
const hexHeight = HEX_SIZE * 0.75 - 3;
|
||||
const size = HEX_SIZE * 0.98 - 3;
|
||||
|
||||
const vertices = [
|
||||
{ x: x + hexWidth, y: y },
|
||||
{ x: x + hexWidth * 0.5, y: y - hexHeight },
|
||||
{ x: x - hexWidth * 0.5, y: y - hexHeight },
|
||||
{ x: x - hexWidth, y: y },
|
||||
{ x: x - hexWidth * 0.5, y: y + hexHeight },
|
||||
{ x: x + hexWidth * 0.5, y: y + hexHeight }
|
||||
];
|
||||
const vertices = [];
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const angle = Math.PI / 6 + (Math.PI / 3) * i;
|
||||
vertices.push({
|
||||
x: x + size * Math.cos(angle),
|
||||
y: y + size * Math.sin(angle)
|
||||
});
|
||||
}
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(vertices[0].x, vertices[0].y);
|
||||
|
||||
Reference in New Issue
Block a user