Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about SameGame game, Game development, HTML5 and Javascript.

If you liked Pandaban, the Sokoban prototype made with Panda 2, you will probably like Samepanda, a Samegame prototype which is the porting of the HTML5 Endless SameGame engine with object pooling made with Phaser. Have a look:
Select a tile with at least another tile of the same color around it, and see what happens. No sprites are destroyed. It’s just object pooling. I like how Panda 2 has its own internal object pooling methods, it’s a bit more intuitive than managing it with arrays. By the way, the code is pretty similar to Phaser version, here it is, still uncommented because I am adding some more features:
game.module(
    "game.main"
)
.body(function(){
    
    var gameOptions = {
        tileSize: 100,     
        fieldSize: {       
            rows: 8,     
            cols: 8      
        },
        colors: ["#ff0000", "#00ff00", "#0000ff", "#ffff00"]
    }
    game.addAsset("tile.png");
    game.createScene("Main",{
        init: function(){
            this.createLevel();
            
        },
        createLevel: function(){
            this.canPick = true;
            this.tilesArray = [];
            this.tileContainer = new game.Container();
            this.tileContainer.x = (game.width - gameOptions.tileSize * gameOptions.fieldSize.cols) / 2;
            this.tileContainer.y = (game.height - gameOptions.tileSize * gameOptions.fieldSize.rows) / 2;
            this.tileContainer.addTo(this.stage);
            for(var i = 0; i < gameOptions.fieldSize.rows; i++){
                this.tilesArray[i] = [];
                for(var j = 0; j < gameOptions.fieldSize.cols; j++){
                    this.addTile(i, j);
                }
            }
            game.pool.create("tiles");
        },
        addTile: function(row, col){
            var tileXPos = col * gameOptions.tileSize + gameOptions.tileSize / 2;  
            var tileYPos = row * gameOptions.tileSize + gameOptions.tileSize / 2;
            var theTile = new game.Sprite("tile.png");
            theTile.position.set(tileXPos, tileYPos);
            theTile.anchor.set(50, 50);
            theTile.width = gameOptions.tileSize;
            theTile.height = gameOptions.tileSize;
            var tileValue = Math.floor(Math.random() * gameOptions.colors.length);
            theTile.tint = gameOptions.colors[tileValue];
            theTile.tintAlpha = 0.6;
            this.tilesArray[row][col] = {
                tileSprite: theTile,
                isEmpty: false,
                coordinate: {
                    x: col,
                    y: row
                    
                },
                value: tileValue
            };
            theTile.addTo(this.tileContainer);
        },
        mousedown: function(x, y){
            if(this.canPick){
                var posX = x - this.tileContainer.x;
                var posY = y - this.tileContainer.y;
                var pickedRow = Math.floor(posY / gameOptions.tileSize);
                var pickedCol = Math.floor(posX / gameOptions.tileSize);
                if(pickedRow >= 0 && pickedCol >= 0 && pickedRow < gameOptions.fieldSize.rows && pickedCol < gameOptions.fieldSize.cols){
                    var pickedTile = this.tilesArray[pickedRow][pickedCol];
                    this.filled = [];
                    this.filled.length = 0;
                    this.floodFill(pickedTile.coordinate, pickedTile.value);
                    if(this.filled.length > 1){
                        this.canPick = false;
                        this.destroyTiles();
                    }
                }
            }
        },
        floodFill: function(p, n){
            if(p.x < 0 || p.y < 0 || p.x >= gameOptions.fieldSize.cols || p.y >= gameOptions.fieldSize.rows){
                return;
            }
            if(!this.tilesArray[p.y][p.x].isEmpty && this.tilesArray[p.y][p.x].value == n && !this.pointInArray(p)){
                this.filled.push(p);
                this.floodFill({
                    x: p.x + 1,
                    y: p.y
                }, n);
                this.floodFill({
                    x: p.x - 1,
                    y: p.y
                }, n);
                this.floodFill({
                    x: p.x,
                    y: p.y + 1
                }, n);
                this.floodFill({
                    x: p.x,
                    y: p.y - 1
                }, n);
            }
        },
        pointInArray: function(p){
            for(var i = 0; i < this.filled.length; i++){
                if(this.filled[i].x == p.x && this.filled[i].y == p.y){
                    return true;
                }
            }
            return false;
        },
        destroyTiles: function(){      
            this.destroyingBlocks = 0;
            for(var i = 0; i < this.filled.length; i++){
                this.destroyingBlocks ++;
                var tween = new game.Tween(this.tilesArray[this.filled[i].y][this.filled[i].x].tileSprite);
                tween.to({
                    alpha: 0
                }, 300);
                tween.start();
                game.pool.put("tiles", this.tilesArray[this.filled[i].y][this.filled[i].x].tileSprite);
                tween.onComplete(function(){ 
                    this.destroyingBlocks --;
                    if(this.destroyingBlocks == 0){
                        this.fillVerticalHoles();     
                    }                                
                }.bind(this));
                this.tilesArray[this.filled[i].y][this.filled[i].x].isEmpty = true;
            }
        },
        fillVerticalHoles: function(){
            this.movingBlocks = 0;
            var filled = false;
            for(var i = gameOptions.fieldSize.rows - 2; i >= 0; i--){
                for(var j = 0; j < gameOptions.fieldSize.cols; j++){
                    if(!this.tilesArray[i][j].isEmpty){
                        var holesBelow = this.countSpacesBelow(i, j);
                        if(holesBelow > 0){
                            filled = true;
                            this.moveDownTile(i, j, i + holesBelow, false);                                                                   
                        }
                    }
                }
                
            }
            if(!filled){
                this.canPick = true;      
            }
            for(i = 0; i < gameOptions.fieldSize.cols; i++){
                var topHoles = this.countSpacesBelow(-1, i);
                for(j = topHoles - 1; j >= 0; j--){
                    var reusedTile = game.pool.get("tiles");
                    reusedTile.y =  (j - topHoles) * gameOptions.tileSize + gameOptions.tileSize / 2;
                    reusedTile.x = i * gameOptions.tileSize + gameOptions.tileSize / 2;
                    reusedTile.alpha = 1;
                    var tileValue = Math.floor(Math.random() * gameOptions.colors.length);
                    reusedTile._destroyTintedTexture();
                    reusedTile.tint = gameOptions.colors[tileValue];
                    this.tilesArray[j][i] = {
                        tileSprite: reusedTile,
                        isEmpty: false,
                        coordinate: {
                            x: i,
                            y: j
                        },
                        value: tileValue
                    }  
                    this.moveDownTile(0, i, j, true);                    
                }
            }
        },
        countSpacesBelow: function(row, col){
            var result = 0;
            for(var i = row + 1; i < gameOptions.fieldSize.rows; i++){
                if(this.tilesArray[i][col].isEmpty){
                    result ++;
                }     
            }
            return result;
        },
        moveDownTile: function(fromRow, fromCol, toRow, justMove){
            if(!justMove){
                var tileToMove = this.tilesArray[fromRow][fromCol].tileSprite;
                var tileValue = this.tilesArray[fromRow][fromCol].value;
                this.tilesArray[toRow][fromCol] = {
                    tileSprite: tileToMove,
                    isEmpty: false,
                    coordinate: {
                        x: fromCol,
                        y: toRow
                    },
                    value: tileValue
                }  
                this.tilesArray[fromRow][fromCol].isEmpty = true;        
            }
            this.movingBlocks ++;
            var distanceToTravel = (toRow * gameOptions.tileSize + gameOptions.tileSize / 2) - this.tilesArray[toRow][fromCol].tileSprite.y
            var tween = new game.Tween(this.tilesArray[toRow][fromCol].tileSprite.position);
            tween.to({
                y: toRow * gameOptions.tileSize + gameOptions.tileSize / 2   
            }, distanceToTravel / 2);
            tween.start();
            tween.onComplete(function(){ 
                this.movingBlocks --;
                if(this.movingBlocks == 0){
                    this.canPick = true;  
                }                                
            }.bind(this));
        },
        tilesInColumn: function(col){
            var result = 0;
            for(var i = 0; i < gameOptions.fieldSize.rows; i++){
                if(!this.tilesArray[i][col].isEmpty){ 
                    result ++;
                }
            }
            return result;
        }
    });
});
Panda 2 is definitively a great tool to build HTML5 games, download the source code and play with it.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.