How to create an HTML5 swap and match-3 game engine like the one used in Spellfall iOS game using Phaser

Read all posts about "" game

You already know there is an universe full of match-3 games out there, and each one has its way to combine and match symbols in order to do something the game requires.

Normally, at each move – no matter how you actually move – you should do at least a match.

Moreover, behind that “no matter how you actually move”, there always is a rule swap tiles.

This is not the case of Spellfall from Backflip Studios where you can swap any tile you want with any other tile you want, no matter where they are, and no matter if you make a match.

Definitively the simplest way to approach a match-3 game although the game has a lot of other features which make it quite difficult.

So, what I did today was writing a quick Phaser HTML5 demo of this kind of swapping tiles just like in the original game.

This is what you are going to create, also playable from this link:

Just drag a tile onto another tile to swap them.

There isn’t that much to say about the code, as it is even simpler than the HTML5 Drag and Match engine made with Phaser I published last month.

Here I am just using tweens and group swapping to keep some tiles always in front of the stage.

The fully commented source code is here:

<!doctype html>
<html>
	<head>
    		<script src="phaser.min.js"></script>
    		<style>
    			body{margin:0}
    		</style>
    		<script type="text/javascript">
			window.onload = function() {
				
				var game = new Phaser.Game(300,300,Phaser.CANVAS,"",{preload:onPreload, create:onCreate, update:onUpdate});				
				
				// you can change these values to customize the game
				
				var tileSize = 50;				// tile size, in pixels
				var fieldSize = 6;     			// number of tiles per row/column
				var tileTypes = 6;				// different kind of tiles allowed
				var pickedZoom = 1.1;              // zoom ratio to highlight picked tile
				
				// variables used by game engine
				
				var dragging = false;			// are we dragging?
				var movingRow;					// row of the moving tile
				var movingCol;					// col of the moving tile
				var tileArray = [];				// array with all game tiles
                    var tileGroup; 				// group containing all tiles
                    var movingTileGroup;               // group containing the moving tile
                  
				// when preloading, load the spritesheet with all tiles
				function onPreload() {
					game.load.spritesheet("tiles","tiles.png",tileSize,tileSize);
				}
				
				// the game has been created
				function onCreate() {
					// show the game in full screen
					game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
                    	game.scale.setScreenSize();
                         // add groups. movingTileGroup needs to be above tileGroup so moving tiles
                         // will always have an higher z index and will always stay on top of the game
                         tileGroup = game.add.group();
                         movingTileGroup = game.add.group();
                    	// game field generation, all tiles initially added to "tileGroup" tile
					for(i=0;i<fieldSize;i++){
						tileArray[i]=[];
						for(j=0;j<fieldSize;j++){
							var randomTile = Math.floor(Math.random()*tileTypes)
							theTile=game.add.sprite(j*tileSize+tileSize/2,i*tileSize+tileSize/2,"tiles");
							theTile.frame = randomTile;
							theTile.anchor.setTo(0.5,0.5);
							tileArray[i][j]=theTile;
                                   tileGroup.add(theTile);	
						}
					}
					// listener for input down
					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 picked row and column 
					movingRow = Math.floor(startY/tileSize);
					movingCol = Math.floor(startX/tileSize);
					// move the tile to the upper group, so it will surely be at top of the stage
					movingTileGroup.add(tileArray[movingRow][movingCol]);
					// zoom the tile
					tileArray[movingRow][movingCol].width=tileSize*pickedZoom;
					tileArray[movingRow][movingCol].height=tileSize*pickedZoom;
					// now dragging is allowed
					dragging = true
					// update listeners
					game.input.onDown.remove(pickTile, this);
					game.input.onUp.add(releaseTile, this);
				}
				
				// a tile has been released
				function releaseTile(){
					// remove the listener
					game.input.onUp.remove(releaseTile, this);
					// return the tile to its originary group
					tileGroup.add(tileArray[movingRow][movingCol]);
					// determine landing row and column
					var landingRow = Math.floor(tileArray[movingRow][movingCol].y/tileSize);
					var landingCol = Math.floor(tileArray[movingRow][movingCol].x/tileSize);
					// reset the moving tile to its original size
					tileArray[movingRow][movingCol].width = tileSize;
					tileArray[movingRow][movingCol].height = tileSize;
					// swap tiles, both visually and in tileArray array...
					tileArray[movingRow][movingCol].x=landingCol*tileSize+tileSize/2;
					tileArray[movingRow][movingCol].y=landingRow*tileSize+tileSize/2;
					// ...but only if there moving and landing tiles are different!!
					if(movingRow!=landingRow || movingCol!=landingCol){
						// place the tile to move on the upper group
						movingTileGroup.add(tileArray[landingRow][landingCol]);
						// destination tile will move to start tile with a tween
						var tileTween = game.add.tween(tileArray[landingRow][landingCol]);
						tileTween.to({
							x:movingCol*tileSize+tileSize/2,
							y:movingRow*tileSize+tileSize/2
						},800,Phaser.Easing.Cubic.Out,true); // <- this "true" means "start immediatly"
						tileTween.onComplete.add(function(){
							tileGroup.add(tileArray[landingRow][landingCol]);
						 	// once the tween is completed update tileArray array
							game.input.onDown.add(pickTile, this);
							var temp = tileArray[landingRow][landingCol];
							tileArray[landingRow][landingCol] = tileArray[movingRow][movingCol];
							tileArray[movingRow][movingCol] = temp;
						});
					}
					else {
						// otherwise just let the player be able to swap another tile
						game.input.onDown.add(pickTile, this);
					}
					// we aren't dragging anymore
					dragging = false;
				}
				
				// the game is being updated
				function onUpdate() {
					// if we are dragging a tile
					if(dragging){
						// check x and y distance from starting to current input location
						distX = game.input.worldX-startX;
                    		distY = game.input.worldY-startY;
                    		// move the tile
                              tileArray[movingRow][movingCol].x=movingCol*tileSize+tileSize/2+distX;
                              tileArray[movingRow][movingCol].y=movingRow*tileSize+tileSize/2+distY;
					}
	    			}
	    		};
		</script>
    </head>
    <body>
    </body>
</html>

And obviously you can download the entire project.

I will add some gameplay to this prototype this week, stay tuned.

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

214 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers