Talking about Diamond Digger Saga game, Game development, HTML5, Javascript and Phaser.
If you followed me last week, I published an HTML5 Diamond Digger Saga prototype made with Phaser which is mostly based on flood fill algorithm.
While in the first post I only showed how to remove and replace diamonds, now it’s time to see how to create dust and water tiles. Obviously, water behavior too is managed by flood fill algorithm. I am going to use Phaser in this example too.
This is what we are going to do now:
You can also play with your mobile device at this link.
You should know how to play: click/tap on a gem to remove it and all contiguous gems of the same color. Removing a gem will also remove the dirt below the gem, if any, to make water flow.
Here it is the source code, with new lines highligthed:
<!doctype html> <html> <head> <style> body{ margin:0; padding:0; } </style> <script src="phaser.min.js"></script> <script type="text/javascript"> var game = new Phaser.Game(315,315,Phaser.CANVAS,"",{preload:onPreload, create:onCreate}); var tileSize = 35; // tile size, in pixels var fieldSize = 9; // number of tiles per row/column var tileTypes = 3; // different kind of tiles allowed var tileArray = []; // array with all game tiles var groundArray = []; // array with rocks and water var tileGroup; // group for sprites representing the tiles var groundGroup; // group for sprites representing the ground function onPreload() { game.load.spritesheet("tiles","assets/tiles.png",35,35); game.load.image("rock","assets/rock.png"); game.load.image("water","assets/water.png"); game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; game.scale.setScreenSize(true); } function onCreate(){ groundGroup = game.add.group(); tileGroup = game.add.group(); for(i=0;i<fieldSize;i++){ tileArray[i]=[]; groundArray[i] = []; for(j=0;j<fieldSize;j++){ var randomTile = Math.floor(Math.random()*tileTypes) var theTile=game.add.sprite(j*tileSize+tileSize/2,i*tileSize+tileSize/2,"tiles"); theTile.frame = randomTile; theTile.anchor.setTo(0.5,0.5); tileGroup.add(theTile); tileArray[i][j]=theTile; if(Math.random()>0.4){ var theRock = game.add.sprite(j*tileSize+tileSize/2,i*tileSize+tileSize/2,"rock"); theRock.anchor.setTo(0.5,0.5); groundGroup.add(theRock); groundArray[i][j]=theRock; } } } var waterCol = Math.floor(Math.random()*fieldSize); if(groundArray[0][waterCol]!=null){ groundArray[0][waterCol].destroy(); groundArray[0][waterCol]=null; } waterFill(0,waterCol) game.input.onDown.add(pickTile, this); } // a tile has been picked function pickTile(){ // save input coordinates startX = game.input.worldX; startY = game.input.worldY; // retrieve selected row and column selectedRow = Math.floor(startY/tileSize); selectedCol = Math.floor(startX/tileSize); // delete using flood fill floodFill(selectedRow,selectedCol,tileArray[selectedRow][selectedCol].frame); // make existing gems fall down fallDown(); // replenish game field from the top fallFromTop(); } function waterFill(row,col){ if(row>=0 && row<fieldSize && col>=0 && col<fieldSize){ if(groundArray[row][col]==null){ var theWater = game.add.sprite(col*tileSize+tileSize/2,row*tileSize+tileSize/2,"water"); theWater.anchor.setTo(0.5,0.5); groundGroup.add(theWater); groundArray[row][col]=theWater; waterFill(row+1,col); waterFill(row-1,col); waterFill(row,col+1); waterFill(row,col-1); } } } function floodFill(row,col,val){ if(row>=0 && row<fieldSize && col>=0 && col<fieldSize){ if(tileArray[row][col]!=null && tileArray[row][col].frame==val){ tileArray[row][col].destroy(); tileArray[row][col]=null; if(groundArray[row][col]!=null && groundArray[row][col].key=="rock"){ groundArray[row][col].destroy(); groundArray[row][col]=null; if(nextToWater(row,col)){ waterFill(row,col); } } floodFill(row+1,col,val); floodFill(row-1,col,val); floodFill(row,col+1,val); floodFill(row,col-1,val); } } } function nextToWater(row,col){ if(col>0 && groundArray[row][col-1]!=null && groundArray[row][col-1].key=="water"){ return true; } if(row>0 && groundArray[row-1][col]!=null && groundArray[row-1][col].key=="water"){ return true; } if(row<fieldSize-1 && groundArray[row+1][col]!=null && groundArray[row+1][col].key=="water"){ return true; } if(col<fieldSize-1 && groundArray[row][col+1]!=null && groundArray[row][col+1].key=="water"){ return true; } return false; } function fallDown(){ for(var i=fieldSize-1;i>=0;i--){ for(var j=0;j<fieldSize;j++){ if(tileArray[i][j]!=null){ var delta = holesBelow(i,j); if(delta>0){ var tileTween = game.add.tween(tileArray[i][j]); tileTween.to({ y: (i+delta)*tileSize+tileSize/2 },800,Phaser.Easing.Cubic.Out,true); tileArray[i+delta][j]=tileArray[i][j]; tileArray[i][j]=null; } } } } } function fallFromTop(){ for(i=0;i<fieldSize;i++){ var holes = holesBelow(-1,i); for(j=0;j<holes;j++){ var randomTile = Math.floor(Math.random()*tileTypes); var tileXPos = i*tileSize+tileSize/2; var tileYPos = -(holes-j)*tileSize-tileSize/2; var theTile = game.add.sprite(tileXPos,tileYPos,"tiles"); theTile.frame = randomTile; theTile.anchor.setTo(0.5,0.5); tileGroup.add(theTile); tileArray[j][i]=theTile; tileTween = game.add.tween(tileArray[j][i]); tileTween.to({ y: j*tileSize+tileSize/2 },800,Phaser.Easing.Cubic.Out,true); } } } function holesBelow(row,col){ var holes = 0; for(var i=row+1;i<fieldSize;i++){ if(tileArray[i][col]==null){ holes++; } } return holes; } </script> </head> <body> </body> </html>
Let’s have a look at the new lines:
Lines 19-21: declaration of new variables to use: groundArray
to store ground information, and two groups to properly manage display list. Gems will always stay on top of the ground.
Lines 25-26: preloading new graphic assets
Lines 32-33: adding the groups. Notice tile group is added after ground group, to make it stay in front of the stage.
Line 42: now gems are placed on the proper group
Lines 44-49:placing random dirt tiles
Lines 52-57: choosing a random tile on the first row, removing the dirt if any, then add water. waterFill function will perform flood fill with water and dirt.
Lines 77-90: flood fill algorithm applied to water
Lines 97-103: removing the dirt if under a gem we are removing. If the dirt we just removed is next to a water tile, we need to perform another flood fill
Lines 112-126: simple function to detect if there’s water next to a given tile
And this is the entire Diamond Digger Saga prototype. I must admit I did not play it that much, so if you want me to add some extra feature, just leave a comment and explain how should it work. Meanwhile you can download the full source code.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.