HTML5 Drag and Match engine updated to Phaser 2.6.2 with masks and a lot of custom options
Talking about Drag and Match game, Game development, HTML5, Javascript and Phaser.
Do you like my tutorials?
Then consider supporting me on Ko-fi.
gameOptions
global object. You can change several things such as tile size, board size and offset, as well as the number of different colors on the board
* All the code which handles something which moves – basically the tiles – has been placed inside update
method which Phaser runs at each frame, to prevent strange flickering which appears in latest Phaser versions when you move sprites outside update
method.
Now, it’s time to play:
I placed the board inside a way bigger canvas to show you how mask works, hiding the part of the sprites which are outside the board. Drag and move the tiles around the board.
If you have a mobile device, you can play directly fron this link.
And here we go with the completely commented source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | // the game itself var game; // global game options var gameOptions = { // width of the game, in pixels gameWidth: 600, // height of the game, in pixels gameHeight: 600, // size of the sprite sheet, in pixels spritesheetSize: 50, // size of each tile, in pixels tileSize: 50, // size of the field, in tiles fieldSize: 6, // different tile types tileTypes: 6, // distance from the left of the screen to the left of the board, in pixels offsetX: 150, // distance from the top of the screen to the top of the board, in pixels offsetY: 150 } // some constants to be used in the game // I am not dragging var NO_DRAG = 0; // I am dragging horizontally var HORIZONTAL_DRAG = 1; // I am dragging vertically var VERTICAL_DRAG = 2; // The game state is "doing nothing" var GAME_STATE_IDLE = 0; // When the player is dragging a row/column var GAME_STATE_DRAG = 1; // When the player stops dragging var GAME_STATE_STOP = 2; // when the window has been fully loaded window.onload = function () { // creation of a Game instance game = new Phaser.Game(600, 600); // adding "PlayGame" state game.state.add( "PlayGame" , playGame) // starting "PlayGame" state game.state.start( "PlayGame" ); } var playGame = function (game){} playGame.prototype = { // when the state preloads preload: function (){ // loading the spritesheet with all tile images game.load.spritesheet( "tiles" , "tiles.png" , gameOptions.spritesheetSize, gameOptions.spritesheetSize); // setting the game on maximum scale mode to cover the entire screen game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; game.scale.pageAlignHorizontally = true ; game.scale.pageAlignVertically = true ; }, // once the state has been created create: function (){ // tileArray is the array which will contain all tiles this .tileArray = []; // creation of the group which will contain all tiles this .tileGroup = game.add.group(); // adjusting group position according to offset this .tileGroup.x = gameOptions.offsetX; this .tileGroup.y = gameOptions.offsetY; // creation of a mask with the same size of the board to be placed in the same position of the group // this way we are hiding everything is outside the board this .tileMask = game.add.graphics( this .tileGroup.x, this .tileGroup.y); this .tileMask.beginFill(0xffffff); this .tileMask.drawRect(0, 0, gameOptions.fieldSize * gameOptions.tileSize, gameOptions.fieldSize * gameOptions.tileSize); this .tileGroup.mask = this .tileMask; this .tileMask.visible = true ; // filling the board with tiles thanks to "addTile" method for ( var i = 0; i < gameOptions.fieldSize; i++){ this .tileArray[i] = []; for (j = 0; j < gameOptions.fieldSize; j++){ this .addTile(i, j); } } // adding the temporary tile thanks to addTempTile method this .addTempTile(); // waiting for player input to call pickTile method game.input.onDown.add( this .pickTile, this ); // the game has just been created, so we are doing nothing this .gameState = GAME_STATE_IDLE; }, // function to add a tile at a given row and column addTile: function (row, col){ // choosing a random tile var randomTile = game.rnd.integerInRange(0, gameOptions.tileTypes - 1); // creation of the sprite in the proper position var theTile = game.add.sprite(col * gameOptions.tileSize, row * gameOptions.tileSize, "tiles" ); // setting tile width and height theTile.width = gameOptions.tileSize; theTile.height = gameOptions.tileSize; // showing the frame according to tile value theTile.frame = randomTile; // saving the value inside a custom property theTile.value = randomTile; // inserting the tile in tileArray array this .tileArray[row][col] = theTile; // adding the sprite to tileGroup group this .tileGroup.add(theTile); }, // function to add the temporary tile addTempTile: function (){ // creation of the sprite, no matter the position, we won't show it at the moment this .tempTile = game.add.sprite(0, 0, "tiles" ); // setting its width and height this .tempTile.width = gameOptions.tileSize; this .tempTile.height = gameOptions.tileSize; // setting the sprite to non visible this .tempTile.visible = false ; // adding the sprite to tileGroup group this .tileGroup.add( this .tempTile); }, // function to triggered when the player touches/clicks on the canvas pickTile: function (e){ // determining row and column according to input position, tile size and offset this .movingRow = Math.floor((e.position.y - gameOptions.offsetY) / gameOptions.tileSize); this .movingCol = Math.floor((e.position.x - gameOptions.offsetX) / gameOptions.tileSize); // if row and column are actually inside game field... if ( this .movingRow >= 0 && this .movingCol >= 0 && this .movingRow < gameOptions.fieldSize && this .movingCol < gameOptions.fieldSize){ // at the moment we aren't dragging this .dragDirection = NO_DRAG; // removing the listener which waits for the input to begin game.input.onDown.remove( this .pickTile, this ); // adding a listener which waits for the input to end then call releaseTile method game.input.onUp.add( this .releaseTile, this ); // adding a listener which waits for the input to move then call moveTile method game.input.addMoveCallback( this .moveTile, this ); } }, // function to be executed at each frame update: function (){ // checking game state to see what to do switch ( this .gameState){ // we are dragging case GAME_STATE_DRAG: // call handleDrag method this .handleDrag(); break ; // we just stopped dragging case GAME_STATE_STOP: // call handleStop method this .handleStop(); break ; } // at the end of the function, we set gameState again to idle this .gameState = GAME_STATE_IDLE; }, // function to handle - and draw on the canvas - the game when the player drags a row/column handleDrag: function (){ // two different things to do according to drag direction switch ( this .dragDirection){ // horizontal drag case HORIZONTAL_DRAG: // hiding temporary tile this .tempTile.visible = false ; // placing the temporary tile in the proper row this .tempTile.y = this .movingRow * gameOptions.tileSize; // deltaX is the amount of tiles we are moving var deltaX = (Math.floor( this .distX / gameOptions.tileSize) % gameOptions.fieldSize); // deltaX >= 0 means we are moving to the right (or not moving) if (deltaX >= 0) { // temporary tile frame is now the same as the rightmost visible tile this .tempTile.frame = this .tileArray[ this .movingRow][gameOptions.fieldSize - 1 - deltaX].value; } // we are moving to the left else { // temporary tile frame is now the same as the leftmost visible tile deltaX = deltaX * -1 - 1; this .tempTile.frame = this .tileArray[ this .movingRow][deltaX].value; } // looping through all the moving row for ( var i = 0; i < gameOptions.fieldSize; i++){ // adjusting each tile horizontal position this .tileArray[ this .movingRow][i].x = (i * gameOptions.tileSize + this .distX) % (gameOptions.tileSize * gameOptions.fieldSize); // if tile position is less than zero... if ( this .tileArray[ this .movingRow][i].x < 0) { // ... place it on the opposite side of the game field this .tileArray[ this .movingRow][i].x += gameOptions.tileSize * gameOptions.fieldSize; } } // tileX is the amount of pixels we are moving, capped to gameOptions.tileSize var tileX = this .distX % gameOptions.tileSize; // if the amount is greater than zero (moving to the right) if (tileX > 0){ // placing temporary tile before the leftmost tile this .tempTile.x = tileX - gameOptions.tileSize; // showing temportary tile this .tempTile.visible = true ; } // if the amount is less than zero (moving to the left) if (tileX < 0){ // placing temporary tile before the leftmost tile this .tempTile.x = tileX; // showing temportary tile this .tempTile.visible = true ; } break ; // vertical drag, same concept seen in horizontal drag, just applied to Y axis case VERTICAL_DRAG: this .tempTile.visible = false ; this .tempTile.x = this .movingCol * gameOptions.tileSize; var deltaY = (Math.floor( this .distY / gameOptions.tileSize) % gameOptions.fieldSize); if (deltaY >= 0) { this .tempTile.frame = this .tileArray[gameOptions.fieldSize - 1 - deltaY][ this .movingCol].value; } else { deltaY = deltaY * -1 - 1; this .tempTile.frame = this .tileArray[deltaY][ this .movingCol].value; } for ( var i = 0; i < gameOptions.fieldSize; i++){ this .tileArray[i][ this .movingCol].y = (i * gameOptions.tileSize + this .distY) % (gameOptions.tileSize * gameOptions.fieldSize); if ( this .tileArray[i][ this .movingCol].y < 0) { this .tileArray[i][ this .movingCol].y += gameOptions.tileSize * gameOptions.fieldSize; } } var tileY = this .distY % gameOptions.tileSize; if (tileY > 0){ this .tempTile.y = tileY - gameOptions.tileSize; this .tempTile.visible = true ; } if (tileY < 0){ this .tempTile.y = tileY; this .tempTile.visible = true ; } break ; } }, // function to handle - and draw on the canvas - the game when the player stops dragging a row/column handleStop: function (){ // hiding temporary tile this .tempTile.visible = false ; // two different things to do according to drag direction switch ( this .dragDirection){ // horizontal drag case HORIZONTAL_DRAG: // we have to find how many "half tiles" we dragged var shiftAmount = Math.floor( this .distX / (gameOptions.tileSize / 2)); // and now let's see how many tiles we dragged, with a modulo operation because the max amount is fieldSize - 1 shiftAmount = Math.ceil(shiftAmount / 2) % gameOptions.fieldSize; // creation of a temporary array var tempArray = []; // now the idea is to insert in tempArray array the tileArray items in the order they are at the end of the drag // when shiftAmount is greater than 0, we dragged to the right if (shiftAmount > 0){ for ( var i = 0; i < gameOptions.fieldSize; i++){ tempArray[(shiftAmount + i) % gameOptions.fieldSize] = this .tileArray[ this .movingRow][i].value; } } // when shiftAmount is less than zero, we dragged to the left else { shiftAmount *= -1; for ( var i = 0; i < gameOptions.fieldSize; i++){ tempArray[i] = this .tileArray[ this .movingRow][(shiftAmount + i) % gameOptions.fieldSize].value; } } // copying content from tempArray to tileArray and adjust tile position for (i = 0; i < gameOptions.fieldSize; i++){ this .tileArray[ this .movingRow][i].value = tempArray[i]; this .tileArray[ this .movingRow][i].frame = tempArray[i]; this .tileArray[ this .movingRow][i].x = i * gameOptions.tileSize; } break ; // vertical drag follows the same concepts seen in horizontal drag case VERTICAL_DRAG: var shiftAmount = Math.floor( this .distY / (gameOptions.tileSize / 2)); shiftAmount = Math.ceil(shiftAmount / 2) % gameOptions.fieldSize; var tempArray = []; if (shiftAmount > 0){ for ( var i = 0; i < gameOptions.fieldSize; i++){ tempArray[(shiftAmount + i) % gameOptions.fieldSize] = this .tileArray[i][ this .movingCol].value; } } else { shiftAmount *= -1; for ( var i = 0; i < gameOptions.fieldSize; i++){ tempArray[i] = this .tileArray[(shiftAmount + i) % gameOptions.fieldSize][ this .movingCol].value; } } for ( var i = 0; i < gameOptions.fieldSize; i++){ this .tileArray[i][ this .movingCol].value = tempArray[i]; this .tileArray[i][ this .movingCol].frame = tempArray[i]; this .tileArray[i][ this .movingCol].y = i * gameOptions.tileSize; } break ; } // we aren't dragging anymore this .dragDirection = NO_DRAG; }, // function to be triggered when the player moves the mouse/finger moveTile: function (e){ // we are dragging this .gameState = GAME_STATE_DRAG; // determining horizontal and vertical distance between start and current input position this .distX = e.position.x - e.positionDown.x; this .distY = e.position.y - e.positionDown.y; // if we aren't dragging yet... if ( this .dragDirection == NO_DRAG){ // how many pixels are we travelling with our finger/mouse? var distance = e.position.distance(e.positionDown); // more than 5 pixels? ok, that's enough to determine the direction if (distance > 5) { // trigonometry to know drag angle var dragAngle = Math.abs(Math.atan2( this .distY, this .distX)); // if drag angle is between PI/4 and 3PI/4... if ((dragAngle > Math.PI / 4 && dragAngle < 3 * Math.PI / 4)) { // ... we can say it's a vertical drag this .dragDirection = VERTICAL_DRAG; } else { // else it's an horizontal drag this .dragDirection = HORIZONTAL_DRAG; } } } }, // function to be triggered when the player releases the mouse/finger releaseTile: function (){ // we stopped dragging this .gameState = GAME_STATE_STOP; // updating listeners game.input.onDown.add( this .pickTile, this ); game.input.onUp.remove( this .releaseTile, this ); game.input.deleteMoveCallback( this .moveTile, this ); } } |
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.