Do you like my tutorials?

Then consider supporting me on Ko-fi

Talking about Hexagonal Tiles game, HTML5, Javascript and Phaser.

This is the update to a post I started a loooong time ago, back in 2008 with Understanding hexagonal tiles where I talked about – guess what – hexagonal tiles and above all about the difference from horizontal hexagonal tiles and vertical hexagonal tiles.

There is a lot of more theory and some ActionScript examples in the posts Hex maps creation and rollover, Finding adjacent cells in an hex map, Finding adjacent cells in an hex map – part 2 and Finding adjacent cells in an hex map – AS3 Version where all the theory behind hex maps is explained.

Now I am showing you how to create an hex map, both horizontal and vertical, center it on the stage and how to detect which hexagon you are moving the mouse over just using math. This way you should be able to port these scripts in any language you need.

Vertical hex maps

Here it is the example, move the mouse over the hexagons to place a marker on them:

And this is the source code:

window.onload = function() {
	
	var game = new Phaser.Game(640, 480, Phaser.CANVAS, "", {preload: onPreload, create: onCreate});                

	var hexagonWidth = 80;
	var hexagonHeight = 70;
	var gridSizeX = 10;
	var gridSizeY = 12;
	var columns = [Math.ceil(gridSizeY/2),Math.floor(gridSizeY/2)];
     var moveIndex;
     var sectorWidth = hexagonWidth/4*3;
     var sectorHeight = hexagonHeight;
     var gradient = (hexagonWidth/4)/(hexagonHeight/2);
     var marker;
     var hexagonGroup;
     
	function onPreload() {
		game.load.image("hexagon", "hexagon.png");
		game.load.image("marker", "marker.png");
	}

	function onCreate() {
		hexagonGroup = game.add.group();
		game.stage.backgroundColor = "#ffffff"
	     for(var i = 0; i < gridSizeX/2; i ++){
			for(var j = 0; j < gridSizeY; j ++){
				if(gridSizeX%2==0 || i+1<gridSizeX/2 || j%2==0){
					var hexagonX = hexagonWidth*i*1.5+(hexagonWidth/4*3)*(j%2);
					var hexagonY = hexagonHeight*j/2;	
					var hexagon = game.add.sprite(hexagonX,hexagonY,"hexagon");
					hexagonGroup.add(hexagon);
				}
			}
		}
		hexagonGroup.y = (game.height-hexagonHeight*Math.ceil(gridSizeY/2))/2;
          if(gridSizeY%2==0){
               hexagonGroup.y-=hexagonHeight/4;
          }
		hexagonGroup.x = (game.width-Math.ceil(gridSizeX/2)*hexagonWidth-Math.floor(gridSizeX/2)*hexagonWidth/2)/2;
          if(gridSizeX%2==0){
               hexagonGroup.x-=hexagonWidth/8;
          }
		marker = game.add.sprite(0,0,"marker");
		marker.anchor.setTo(0.5);
		marker.visible=false;
		hexagonGroup.add(marker);
          moveIndex = game.input.addMoveCallback(checkHex, this);   		
	}
     
     function checkHex(){
          var candidateX = Math.floor((game.input.worldX-hexagonGroup.x)/sectorWidth);
          var candidateY = Math.floor((game.input.worldY-hexagonGroup.y)/sectorHeight);
          var deltaX = (game.input.worldX-hexagonGroup.x)%sectorWidth;
          var deltaY = (game.input.worldY-hexagonGroup.y)%sectorHeight; 
          if(candidateX%2==0){
               if(deltaX<((hexagonWidth/4)-deltaY*gradient)){
                    candidateX--;
                    candidateY--;
               }
               if(deltaX<((-hexagonWidth/4)+deltaY*gradient)){
                    candidateX--;
               }
          }    
          else{
               if(deltaY>=hexagonHeight/2){
                    if(deltaX<(hexagonWidth/2-deltaY*gradient)){
                         candidateX--;
                    }
               }
               else{
                    if(deltaX<deltaY*gradient){
                         candidateX--;
                    }
                    else{
                         candidateY--;
                    }
               }
          }
          placeMarker(candidateX,candidateY);
     }
     
     function placeMarker(posX,posY){
		if(posX<0 || posY<0 || posX>=gridSizeX || posY>columns[posX%2]-1){
			marker.visible=false;
		}
		else{
			marker.visible=true;
			marker.x = hexagonWidth/4*3*posX+hexagonWidth/2;
			marker.y = hexagonHeight*posY;
			if(posX%2==0){
				marker.y += hexagonHeight/2;
			}
			else{
				marker.y += hexagonHeight;
			}
		}
	}
	
}

And this now the same concept applied to…

Horizontal hex maps

Just like in previous example, move the mouse over the hexagons to place a marker:

And this is the – pretty similar – source code:

window.onload = function() {
	
	var game = new Phaser.Game(640, 480, Phaser.CANVAS, "", {preload: onPreload, create: onCreate});                

	var hexagonWidth = 70;
	var hexagonHeight = 80;
	var gridSizeX = 17;
	var gridSizeY = 7;
	var columns = [Math.ceil(gridSizeX/2),Math.floor(gridSizeX/2)];
     var moveIndex;
     var sectorWidth = hexagonWidth;
     var sectorHeight = hexagonHeight/4*3;
     var gradient = (hexagonHeight/4)/(hexagonWidth/2);
     var marker;
     var hexagonGroup;
     
	function onPreload() {
		game.load.image("hexagon", "hexagon.png");
		game.load.image("marker", "marker.png");
	}

	function onCreate() {
		hexagonGroup = game.add.group();
		game.stage.backgroundColor = "#ffffff"
	     for(var i = 0; i < gridSizeY/2; i ++){
			for(var j = 0; j < gridSizeX; j ++){
				if(gridSizeY%2==0 || i+1<gridSizeY/2 || j%2==0){
					var hexagonX = hexagonWidth*j/2;
					var hexagonY = hexagonHeight*i*1.5+(hexagonHeight/4*3)*(j%2);	
					var hexagon = game.add.sprite(hexagonX,hexagonY,"hexagon");
					hexagonGroup.add(hexagon);
				}
			}
		}
		hexagonGroup.x = (game.width-hexagonWidth*Math.ceil(gridSizeX/2))/2;
          if(gridSizeX%2==0){
               hexagonGroup.x-=hexagonWidth/4;
          }
		hexagonGroup.y = (game.height-Math.ceil(gridSizeY/2)*hexagonHeight-Math.floor(gridSizeY/2)*hexagonHeight/2)/2;
          if(gridSizeY%2==0){
               hexagonGroup.y-=hexagonHeight/8;
          }
		marker = game.add.sprite(0,0,"marker");
		marker.anchor.setTo(0.5);
		marker.visible=false;
		hexagonGroup.add(marker);
          moveIndex = game.input.addMoveCallback(checkHex, this);   		
	}
     
     function checkHex(){
          var candidateX = Math.floor((game.input.worldX-hexagonGroup.x)/sectorWidth);
          var candidateY = Math.floor((game.input.worldY-hexagonGroup.y)/sectorHeight);
          var deltaX = (game.input.worldX-hexagonGroup.x)%sectorWidth;
          var deltaY = (game.input.worldY-hexagonGroup.y)%sectorHeight; 
          if(candidateY%2==0){
               if(deltaY<((hexagonHeight/4)-deltaX*gradient)){
                    candidateX--;
                    candidateY--;
               }
               if(deltaY<((-hexagonHeight/4)+deltaX*gradient)){
                    candidateY--;
               }
          }    
          else{
               if(deltaX>=hexagonWidth/2){
                    if(deltaY<(hexagonHeight/2-deltaX*gradient)){
                         candidateY--;
                    }
               }
               else{
                    if(deltaY<deltaX*gradient){
                         candidateY--;
                    }
                    else{
                         candidateX--;
                    }
               }
          }
          placeMarker(candidateX,candidateY);
     }
     
     function placeMarker(posX,posY){
		if(posX<0 || posY<0 || posY>=gridSizeY || posX>columns[posY%2]-1){
			marker.visible=false;
		}
		else{
			marker.visible=true;
			marker.x = hexagonWidth*posX;
			marker.y = hexagonHeight/4*3*posY+hexagonHeight/2;
			if(posY%2==0){
				marker.x += hexagonWidth/2;
			}
			else{
				marker.x += hexagonWidth;
			}
		}
	}
	
}

I hope this will help you in the creation of a game based on hexagonal maps. You can download the source code of all examples, and if you have questions, just leave a comment.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.