// the game itself var game; // levels var levels = [ [ // level 1 [0, 0, 0, 0, 0, 0], [3, 2, 3, 2, 2, 2], [0, 0, 2, 3, 2, 2], [2, 0, 2, 2, 0, 0], [0, 2, 3, 0, 2, 2], [2, 3, 0, 2, 0, 4] ], [ // level 2 [0, 2, 3, 3, 2, 1], [2, 0, 3, 3, 0, 2], [1, 4, 3, 3, 0, 1], [1, 4, 3, 3, 0, 1], [2, 0, 3, 3, 0, 2], [0, 2, 3, 3, 2, 1] ], [ // level 3 [0, 2, 2, 2, 0, 2], [1, 1, 1, 1, 1, 1], [1, 3, 0, 3, 3, 1], [1, 3, 3, 3, 3, 1], [1, 1, 1, 1, 1, 1], [4, 4, 0, 4, 0, 1] ], [ // level 4 [3, 4, 2, 2, 0, 2], [1, 1, 1, 1, 1, 1], [0, 1, 3, 0, 3, 1], [1, 3, 1, 3, 1, 1], [1, 1, 1, 1, 1, 1], [3, 0, 0, 4, 0, 1] ], [ // level 5 [4, 2, 1, 2, 1, 4], [1, 2, 1, 1, 2, 1], [1, 3, 2, 3, 3, 1], [1, 1, 2, 2, 3, 1], [1, 0, 1, 1, 2, 1], [4, 0, 0, 4, 1, 4] ], [ // level 6 [3, 2, 1, 1, 2, 1], [1, 2, 1, 0, 2, 2], [2, 1, 1, 1, 2, 1], [1, 2, 1, 1, 2, 1], [0, 2, 1, 1, 2, 4], [3, 0, 0, 4, 0, 4] ], [ // level 7 [2, 0, 2, 3, 0, 2], [0, 2, 1, 3, 2, 1], [2, 0, 1, 3, 0, 2], [0, 2, 3, 3, 2, 1], [2, 0, 1, 3, 0, 2], [0, 2, 0, 3, 2, 1] ], [ // level 8 [1, 3, 0, 1, 1, 2], [0, 4, 0, 0, 2, 3], [3, 1, 0, 0, 3, 1], [1, 2, 0, 0, 1, 2], [4, 3, 1, 0, 4, 3], [2, 4, 1, 1, 2, 4] ], [ // level 9 [4, 1, 0, 0, 2, 4], [2, 4, 2, 4, 1, 0], [0, 0, 2, 0, 4, 2], [1, 1, 0, 0, 4, 2], [2, 4, 1, 4, 0, 1], [3, 0, 4, 1, 0, 4] ], [ // level 10 [3, 2, 2, 0, 2, 0], [2, 3, 2, 3, 2, 0], [0, 1, 3, 1, 2, 2], [1, 1, 0, 0, 0, 1], [2, 2, 1, 3, 3, 1], [3, 2, 1, 1, 2, 2] ] ]; // this object contains all customizable game options // changing them will affect gameplay var gameOptions = { // game width, in pixels gameWidth: 700, // game height, in pixels gameHeight: 800, // tile size, in pixels tileSize: 100, // field size, an object fieldSize: { // rows in the field, in units rows: 6, // columns in the field, in units cols: 6 }, // tile colors colors: [0x999999, 0xffcb97, 0xffaeae, 0xa8ffa8, 0x9fcfff], // array with various directions, you can make the game harder if you only use the first 4 (up, down, left, right) directions: [ new Phaser.Point(0, 1), new Phaser.Point(0, -1), new Phaser.Point(1, 0), new Phaser.Point(-1, 0), new Phaser.Point(1, 1), new Phaser.Point(-1, -1), new Phaser.Point(1, -1), new Phaser.Point(-1, 1) ] } // function to be execute once the page loads window.onload = function() { // creation of a new Phaser Game game = new Phaser.Game(gameOptions.gameWidth, gameOptions.gameHeight); // adding "TheGame" state game.state.add("TheGame", TheGame); // launching "TheGame" state game.state.start("TheGame"); } /* ****************** TheGame state ****************** */ var TheGame = function(){}; TheGame.prototype = { // function executed when the state initializes, with level argument. init: function(level){ // this.level is zero if level is undefined, or level argument value otherwise this.level = (level != undefined) ? level : 0; }, // function to be executed when the game preloads preload: function(){ // setting background color to dark grey game.stage.backgroundColor = 0xf5f5f5; // load the white tile which will be tinted on the fly game.load.image("tiles", "assets/sprites/tile.png"); // the restart button game.load.image("restart", "assets/sprites/restart.png"); }, // function to be executed as soon as the game has completely loaded create: function(){ // scaling the game to cover the entire screen, while keeping its ratio game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; // horizontally centering the game game.scale.pageAlignHorizontally = true; // vertically centering the game game.scale.pageAlignVertically = true; // this function will create the level this.createLevel(); }, createLevel: function(){ // tiles are saved in an array called tilesArray, so we can update tile layout without losing the initial configuration this.tilesArray = []; // this group will contain all tiles this.tileGroup = game.add.group(); // we are centering the group horizontally and keeping the same margin from the top this.tileGroup.x = (game.width - gameOptions.tileSize * gameOptions.fieldSize.cols) / 2; this.tileGroup.y = this.tileGroup.x; // two loops to create a grid made by "gameOptions.fieldSize.rows" x "gameOptions.fieldSize.cols" columns for(var i = 0; i < gameOptions.fieldSize.rows; i++){ this.tilesArray[i] = []; for(var j = 0; j < gameOptions.fieldSize.cols; j++){ // this function adds a tile at row "i" and column "j" this.addTile(i, j); } } // waiting for user input game.input.onDown.add(this.pickTile, this); // placing restart button game.add.button(game.width / 2, game.height - this.tileGroup.y, "restart", function(){ // restart the level game.state.start("TheGame", true, false, this.level); }, this).anchor.set(0.5, 1); }, // function to add a tile at "row" row and "col" column addTile: function(row, col){ // determining x and y tile position according to tile size var tileXPos = col * gameOptions.tileSize + gameOptions.tileSize / 2; var tileYPos = row * gameOptions.tileSize + gameOptions.tileSize / 2; // tile is added as an image var theTile = game.add.sprite(tileXPos, tileYPos, "tiles"); // setting tile registration point to its center theTile.anchor.set(0.5); // adjusting tile width and height according to tile size theTile.width = gameOptions.tileSize; theTile.height = gameOptions.tileSize; // time to assign the tile a random value, which is also a random color var tileValue = levels[this.level][row][col]; // tinting the tile theTile.tint = gameOptions.colors[tileValue]; // adding tile number var tileText = game.add.text(0, 0, tileValue.toString(), { font: (gameOptions.tileSize / 2).toString() + "px Arial", fontWeight: "bold" }); // setting tile number registration point to the center tileText.anchor.set(0.5); tileText.alpha = (tileValue > 0) ? 0.5 : 0 // now tile number is a child of tile itself theTile.addChild(tileText); // adding the image to "tilesArray" array, creating an object this.tilesArray[row][col] = { // tile image tileSprite: theTile, // the value of the tile value: tileValue, // the text of the tile text: tileText }; // also adding it to "tileGroup" group this.tileGroup.add(theTile); }, // this function is executed at each user input (click or touch) pickTile: function(e){ // method to reset all tile tweens this.resetTileTweens(); // determining x and y position of the input inside tileGroup var posX = e.x - this.tileGroup.x; var posY = e.y - this.tileGroup.y; // transforming coordinates into actual rows and columns var pickedRow = Math.floor(posY / gameOptions.tileSize); var pickedCol = Math.floor(posX / gameOptions.tileSize); // checking if row and column are inside the actual game field if(pickedRow >= 0 && pickedCol >= 0 && pickedRow < gameOptions.fieldSize.rows && pickedCol < gameOptions.fieldSize.cols){ // this is the tile we picked var pickedTile = this.tilesArray[pickedRow][pickedCol]; // getting tile value var pickedValue = pickedTile.value; // if it's a legal tile... if(pickedValue > 0){ // saving picked tile coordinate this.saveTile = new Phaser.Point(pickedRow, pickedCol); // here we will place the possible landing tiles, if ones this.possibleLanding = []; this.possibleLanding.length = 0; // tween the tile this.setTileTweens(pickedTile.tileSprite); // looping through all directions for(var i = 0; i < gameOptions.directions.length; i++){ // determining new coordinates var newRow = pickedRow + pickedValue * gameOptions.directions[i].x; var newCol = pickedCol + pickedValue * gameOptions.directions[i].y; // are we on a legal tile? if(newRow < gameOptions.fieldSize.rows && newRow >= 0 && newCol < gameOptions.fieldSize.cols && newCol >=0 && this.tilesArray[newRow][newCol].value == 0){ // we tween the tile this.setTileTweens(this.tilesArray[newRow][newCol].tileSprite); // this tile is a possible landing tile this.possibleLanding.push(new Phaser.Point(newRow, newCol)); } } } // it's not a legal tile. Maybe a possible landing? else{ // check if the picked tile is in the array of possible landings if(this.pointInArray(new Phaser.Point(pickedRow, pickedCol))){ // this tile can't be picked anymore this.tilesArray[pickedRow][pickedCol].value = -1; // showing tile text this.tilesArray[pickedRow][pickedCol].text.alpha = 0.5; // setting destination tile text as source tile value this.tilesArray[pickedRow][pickedCol].text.text = this.tilesArray[this.saveTile.x][this.saveTile.y].value.toString(); // empty source tile this.tilesArray[this.saveTile.x][this.saveTile.y].value = 0; // change source tile color this.tilesArray[this.saveTile.x][this.saveTile.y].tileSprite.tint = gameOptions.colors[0]; // hiding tile text this.tilesArray[this.saveTile.x][this.saveTile.y].text.alpha = 0; // checkinf for solution this.checkSolution(); } // empty possibleLanding array this.possibleLanding = []; this.possibleLanding.length = 0; } } }, // defines the tile tween setTileTweens: function(tile){ // defining a pulse tween which changes width and height in 200 milliseconds, with a continuous loop and with a yoyo effect this.pulseTween = game.add.tween(tile).to({ width: gameOptions.tileSize * 0.8, height: gameOptions.tileSize * 0.8 }, 200, Phaser.Easing.Cubic.InOut, true, 0, -1, true); }, // this function reset all tile tweens resetTileTweens: function(){ // get all running tweens var activeTweens = game.tweens.getAll(); // looping through all running tweens for(var i = 0; i < activeTweens.length; i++){ // set tile width and height back to original size activeTweens[i].target.width = gameOptions.tileSize; activeTweens[i].target.height = gameOptions.tileSize; } // remove all tweens game.tweens.removeAll(); }, // this function checks for solution checkSolution: function(){ // looping through the entire game field for(var i = 0; i < gameOptions.fieldSize.rows; i++){ for(var j = 0; j < gameOptions.fieldSize.cols; j++){ // if there's still a number tile on the field... if(this.tilesArray[i][j].value > 0){ // level is not solved return false; } } } // level is solved, wait one second then advance to next level game.time.events.add(Phaser.Timer.SECOND, function(){ // start "TheGame" state, clearing World display list (true), without clearing the cache (false), and passing the new level game.state.start("TheGame", true, false, (this.level + 1) % 10); }, this); }, // checks if a point is in possibleLanding array pointInArray: function(p){ // looping through possibleLanding for(var i = 0; i < this.possibleLanding.length; i++){ // do we find the point at i-th element? if(this.possibleLanding[i].x == p.x && this.possibleLanding[i].y == p.y){ // yes we found it return true; } } // p is not inside possibleLanding return false; } }If you manage to design a couple of levels, just send them to me and I’ll add them to the game, meanwhile download the source code.
HTML5 version of zNumbers game made with Phaser – all original levels added
Read all posts about "zNumbers" game
In this second post of the series about zNumbers, I added all the original 10 levels as well as a restart button. I am also writing a level generator but I am not quite satisfied about the layout it returns, I think the best way to design levels for this game is mixing manual and algorithmic generation.
I’ll sort it out, as usual, meanwhile have a look at the game with 10 levels:
Tap/touch a number then tap/touch a destination. Will you be able to move all numbers once? If you have a mobile phone, have a go directly at this link.
The source code keeps being completely commented so it should be easy for you to understand ohw it works: