HTML5 Dungeon Raid tile engine made with Phaser
Read all posts about "Dungeon Raid" game
With the new match-three games like Bejeweled Stars starting to conquer the mobile market once again, it’s time to port some of my old match-three tutorials to HTML5 and Phaser, as well as to start writing new ones.
I am going to start with a basic prototype of Dungeon Raid engine. I wrote a lot about this game as you can see, and with its engine I made Globez game which is also available as a full commented HTML5 source code but for those of you which just want to see the basics, here is an HTML5 prototype:
You can select circles by connecting them horizontally, vertically and diagonally and you can also backtrack. The images I used are the circle itself and a sprite sheet with two arrows:

var game; var gameOptions = { gameWidth: 750, gameHeight: 750, tileSize: 140, fieldSize: 5 } window.onload = function() { game = new Phaser.Game(gameOptions.gameWidth, gameOptions.gameHeight); game.state.add("TheGame", TheGame); game.state.start("TheGame"); } var TheGame = function(){}; TheGame.prototype = { preload: function(){ game.stage.backgroundColor = 0x444444; game.load.image("tiles", "assets/sprites/tiles.png"); game.load.spritesheet("arrows", "assets/sprites/arrows.png", 420, 420); }, create: function(){ game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; game.scale.pageAlignHorizontally = true; game.scale.pageAlignVertically = true; this.createLevel(); game.input.onDown.add(this.pickTile, this); }, createLevel: function(){ this.tilesArray = []; this.arrowsArray = []; // group creation and placement to stay in the center of the canvas this.tileGroup = game.add.group(); this.arrowsGroup = game.add.group(); var groupSize = gameOptions.tileSize * gameOptions.fieldSize; this.tileGroup.x = (game.width - groupSize) / 2; this.tileGroup.y = (game.height - groupSize) / 2; this.arrowsGroup.x = (game.width - groupSize) / 2; this.arrowsGroup.y = (game.height - groupSize) / 2; // tile creation for(var i = 0; i < gameOptions.fieldSize; i++){ this.tilesArray[i] = []; for(var j = 0; j < gameOptions.fieldSize; j++){ this.addTile(i, j); } } }, addTile: function(row, col){ // adding a new tile var tileXPos = col * gameOptions.tileSize + gameOptions.tileSize / 2; var tileYPos = row * gameOptions.tileSize + gameOptions.tileSize / 2; var theTile = game.add.sprite(tileXPos, tileYPos, "tiles"); theTile.anchor.set(0.5); theTile.picked = false; theTile.coordinate = new Phaser.Point(col, row); this.tilesArray[row][col] = theTile; this.tileGroup.add(theTile); }, pickTile: function(e){ // picking the first tile this.visitedTiles = []; this.visitedTiles.length = 0; if(this.tileGroup.getBounds().contains(e.position.x, e.position.y)){ var col = Math.floor((e.position.x - this.tileGroup.x) / gameOptions.tileSize); var row = Math.floor((e.position.y - this.tileGroup.y) / gameOptions.tileSize); this.tilesArray[row][col].alpha = 0.5; this.tilesArray[row][col].picked = true; game.input.onDown.remove(this.pickTile, this); game.input.onUp.add(this.releaseTile, this); game.input.addMoveCallback(this.moveTile, this); this.visitedTiles.push(this.tilesArray[row][col].coordinate); } }, moveTile: function(e){ // we are over a tile if(this.tileGroup.getBounds().contains(e.position.x, e.position.y)){ var col = Math.floor((e.position.x - this.tileGroup.x) / gameOptions.tileSize); var row = Math.floor((e.position.y - this.tileGroup.y) / gameOptions.tileSize); var distance = new Phaser.Point(e.position.x - this.tileGroup.x, e.position.y - this.tileGroup.y).distance(this.tilesArray[row][col]); // we are inside enough a tile if(distance < gameOptions.tileSize * 0.4){ // a new, adjacent tile if(!this.tilesArray[row][col].picked && this.checkAdjacent(new Phaser.Point(col, row), this.visitedTiles[this.visitedTiles.length - 1])){ this.tilesArray[row][col].picked = true; this.tilesArray[row][col].alpha = 0.5; this.visitedTiles.push(this.tilesArray[row][col].coordinate); this.addArrow(); } // backtrack else{ if(this.visitedTiles.length > 1 && row == this.visitedTiles[this.visitedTiles.length - 2].y && col == this.visitedTiles[this.visitedTiles.length - 2].x){ this.tilesArray[this.visitedTiles[this.visitedTiles.length - 1].y][this.visitedTiles[this.visitedTiles.length - 1].x].picked = false; this.tilesArray[this.visitedTiles[this.visitedTiles.length - 1].y][this.visitedTiles[this.visitedTiles.length - 1].x].alpha = 1; this.visitedTiles.pop(); this.arrowsArray[this.arrowsArray.length - 1].destroy(); this.arrowsArray.pop(); } } } } }, releaseTile: function(){ // clear the path this.arrowsGroup.removeAll(true); for(var i = 0; i < gameOptions.fieldSize; i++){ for(var j = 0; j < gameOptions.fieldSize; j++){ this.tilesArray[i][j].alpha = 1; this.tilesArray[i][j].picked = false; } } game.input.onUp.remove(this.releaseTile, this); game.input.deleteMoveCallback(this.moveTile, this); game.input.onDown.add(this.pickTile, this); }, checkAdjacent: function(p1, p2){ return (Math.abs(p1.x - p2.x) <= 1) && (Math.abs(p1.y - p2.y) <= 1); }, addArrow: function(){ // adding the arrows var fromTile = this.visitedTiles[this.visitedTiles.length - 2]; var arrow = game.add.sprite(this.tilesArray[fromTile.y][fromTile.x].x, this.tilesArray[fromTile.y][fromTile.x].y, "arrows"); this.arrowsGroup.add(arrow); arrow.anchor.set(0.5); // this routine handles arrow frame and angle according to its direction var tileDiff = new Phaser.Point(this.visitedTiles[this.visitedTiles.length - 1].x, this.visitedTiles[this.visitedTiles.length - 1].y) tileDiff.subtract(this.visitedTiles[this.visitedTiles.length - 2].x, this.visitedTiles[this.visitedTiles.length - 2].y); if(tileDiff.x == 0){ arrow.angle = -90 * tileDiff.y; } else{ arrow.angle = 90 * (tileDiff.x + 1); if(tileDiff.y != 0){ arrow.frame = 1; if(tileDiff.y + tileDiff.x == 0){ arrow.angle -= 90; } } } this.arrowsArray.push(arrow); } }You can also download the source code while you wait the port of this minigame I made in only 2Kb. If you are interested in a more commented and detailed example of this kind of engine, check my full commented HTML5 source code.