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;
}
});
});
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.