Make a game like Lumines with Flash - part 3
Now it's time to make bricks disappear if they group in a 2x2 square.
Read part 1 and 2 if you did not already.
It's not difficult to check if bricks form a 2x2 square... and it's not difficult to remove them from the stage.
The tricky part is making bricks fill the empty spaces left by disappeared bricks and managing combos (fallen bricks can make another 2x2 square, generating more falling bricks, and so on).
I preferred not to have an onEnterFrame on every brick to check if it's over an empty space and eventually move it, so I created another array called bricks_in_field saving the depth of the brick in that position, so when I remove some bricks I just have to put in the moveable_bricks the depth found in the bricks_in_field array and the "engine" does the rest.
Here it is the actionscript:
-
// declaring some setup variables
-
// number of horizontal cells
-
grid_width = 16;
-
// number of vertical cells
-
grid_height = 10;
-
// size of the cell
-
tile_size = 30;
-
// offset in pixels fron the left side of the stage
-
x_offset = 10;
-
// offset in pixels from the top side of the stage
-
y_offset = 10;
-
// number of different colors that can be displayed in a brick
-
different_colors = 3;
-
// boolean values saying if I should wait for the left (or right, up...) key to be released
-
// this is used to make the player move bricks tapping arrow keys instead of just pressing them
-
wait_left = false;
-
wait_right = false;
-
wait_up = false;
-
wait_down = false;
-
// flag indicating if blocks are falling
-
falling = false;
-
// array containing the game field data
-
field = new Array();
-
// array containing bricks position
-
bricks_in_field = new Array();
-
// array containing the bricks I can move
-
moveable_bricks = new Array();
-
// initializing and drawing the play field and the bricks position fields
-
for (x=0; x<grid_width; x++) {
-
field[x] = new Array();
-
bricks_in_field[x] = new Array();
-
for (y=0; y<grid_height; y++) {
-
field[x][y] = 0;
-
bricks_in_field[x][y] = 0;
-
// look how I det ermine cells position according to tile size and offsets
-
cell = _root.attachMovie("brick", "brick_"+_root.getNextHighestDepth(), _root.getNextHighestDepth(), {_x:x*tile_size+x_offset, _y:(y+2)*tile_size+y_offset});
-
// first frame = empty cell
-
cell.gotoAndStop(1);
-
}
-
}
-
// calling the function that will place four bricks on the stage
-
place_bricks();
-
// main function, to be executed at every frame
-
_root.onEnterFrame = function() {
-
// if blocks aren't falling
-
if (!falling) {
-
// checking if a block is the upper left one of a four block square
-
for (x=0; x<grid_width; x++) {
-
for (y=0; y<grid_height; y++) {
-
if (field[x][y] != 0 and field[x][y] == field[x+1][y] and field[x][y] == field[x+1][y+1] and field[x][y] == field[x][y+1]) {
-
// physically removing the bricks
-
_root["brick_"+bricks_in_field[x][y+1]].removeMovieClip();
-
_root["brick_"+bricks_in_field[x][y+2]].removeMovieClip();
-
_root["brick_"+bricks_in_field[x+1][y+1]].removeMovieClip();
-
_root["brick_"+bricks_in_field[x+1][y+2]].removeMovieClip();
-
// updating field array
-
field[x][y] = 0;
-
field[x][y+1] = 0;
-
field[x+1][y+1] = 0;
-
field[x+1][y] = 0;
-
// checking upper bricks to make them fall
-
start_y = y-1;
-
while (field[x][start_y]>0) {
-
moveable_bricks.push(bricks_in_field[x][start_y+1]);
-
start_y--;
-
}
-
start_y = y-1;
-
while (field[x+1][start_y]>0) {
-
moveable_bricks.push(bricks_in_field[x+1][start_y+1]);
-
start_y--;
-
}
-
falling = true;
-
}
-
}
-
}
-
// place new bricks only if no bricks are falling
-
if (!falling and moveable_bricks.length == 0) {
-
place_bricks();
-
}
-
// this is how I detect if a key was tapped:
-
// when it's pressed, I wait for it to be released (in this case: not pressed)
-
// thanks to the wait_<direction> variable
-
if (Key.isDown(Key.LEFT)) {
-
wait_left = true;
-
} else {
-
if (wait_left) {
-
// if the left key has been tapped, move the four bricks on the left
-
for (x=0; x<4; x++) {
-
// this "if" is used to determine if the bricks are still inside the game field
-
if ((_root["brick_"+moveable_bricks[x]]._x-x_offset)/tile_size-_root["brick_"+moveable_bricks[x]].pos%2>0) {
-
_root["brick_"+moveable_bricks[x]]._x -= tile_size;
-
}
-
}
-
// reset variable, now I must wait again for a key to be pressed
-
wait_left = false;
-
}
-
}
-
// same routine for the right key
-
if (Key.isDown(Key.RIGHT)) {
-
wait_right = true;
-
} else {
-
if (wait_right) {
-
for (x=0; x<4; x++) {
-
if ((_root["brick_"+moveable_bricks[x]]._x-x_offset)/tile_size-_root["brick_"+moveable_bricks[x]].pos%2<14) {
-
_root["brick_"+moveable_bricks[x]]._x += tile_size;
-
}
-
}
-
wait_right = false;
-
}
-
}
-
// when the DOWN arrow is pressed, I must rotate the bricks clockwise
-
// block 0: moves to the right and becomes block 1
-
// block 1: moves down and becomes block 3
-
// block 2: moves up and becomes block 0
-
// block 3: moves to the left and becomes block 2
-
if (Key.isDown(Key.DOWN)) {
-
wait_down = true;
-
} else {
-
if (wait_down) {
-
for (x=0; x<4; x++) {
-
switch (_root["brick_"+moveable_bricks[x]].pos) {
-
case 0 :
-
_root["brick_"+moveable_bricks[x]].pos = 1;
-
_root["brick_"+moveable_bricks[x]]._x += tile_size;
-
break;
-
case 1 :
-
_root["brick_"+moveable_bricks[x]].pos = 3;
-
_root["brick_"+moveable_bricks[x]]._y += tile_size;
-
break;
-
case 2 :
-
_root["brick_"+moveable_bricks[x]].pos = 0;
-
_root["brick_"+moveable_bricks[x]]._y -= tile_size;
-
break;
-
case 3 :
-
_root["brick_"+moveable_bricks[x]].pos = 2;
-
_root["brick_"+moveable_bricks[x]]._x -= tile_size;
-
break;
-
}
-
}
-
wait_down = false;
-
}
-
}
-
// when the UP arrow is pressed, I must rotate the bricks counter-clockwise
-
// block 0: moves down the right and becomes block 2
-
// block 1: moves to the left and becomes block 0
-
// block 2: moves to the right and becomes block 3
-
// block 3: moves up and becomes block 1
-
if (Key.isDown(Key.UP)) {
-
wait_up = true;
-
} else {
-
if (wait_up) {
-
for (x=0; x<4; x++) {
-
switch (_root["brick_"+moveable_bricks[x]].pos) {
-
case 0 :
-
_root["brick_"+moveable_bricks[x]].pos = 2;
-
_root["brick_"+moveable_bricks[x]]._y += tile_size;
-
break;
-
case 1 :
-
_root["brick_"+moveable_bricks[x]].pos = 0;
-
_root["brick_"+moveable_bricks[x]]._x -= tile_size;
-
break;
-
case 2 :
-
_root["brick_"+moveable_bricks[x]].pos = 3;
-
_root["brick_"+moveable_bricks[x]]._x += tile_size;
-
break;
-
case 3 :
-
_root["brick_"+moveable_bricks[x]].pos = 1;
-
_root["brick_"+moveable_bricks[x]]._y -= tile_size;
-
break;
-
}
-
}
-
wait_up = false;
-
}
-
}
-
// when SPACE key is pressed, I don't wait for its release but I detect the key at once
-
if (Key.isDown(Key.SPACE)) {
-
// letìs made bricks fall...
-
falling = true;
-
}
-
} else {
-
//if the blocks are falling
-
// blocks_landed variable will count the number of bricks that already touched the ground
-
// or that cannot fall anymore because they are over other bricks
-
blocks_landed = 0;
-
for (x=0; x<moveable_bricks.length; x++) {
-
// calculating x and y position of the brick in the array
-
x_pos = (_root["brick_"+moveable_bricks[x]]._x-x_offset)/tile_size;
-
y_pos = (_root["brick_"+moveable_bricks[x]]._y-y_offset)/tile_size-1;
-
// determining if the block can fall. There are two conditions:
-
// 1: its position is less than zero. It means the block is outside the grid, in its starting position
-
// 2: its position is lower than the grid height and the tile under the block is empty
-
if ((y_pos<0) or ((y_pos<grid_height) and (field[x_pos][y_pos] == 0))) {
-
// updating field array
-
field[x_pos][y_pos] = _root["brick_"+moveable_bricks[x]]._currentframe;
-
field[x_pos][y_pos-1] = 0;
-
// physically moving the brick
-
_root["brick_"+moveable_bricks[x]]._y += tile_size;
-
} else {
-
// if the brick cannot move down, increase the number of blocks landed
-
blocks_landed++;
-
}
-
}
-
// if all four bricks landed successfully...
-
if (blocks_landed == moveable_bricks.length) {
-
for (x=0; x<moveable_bricks.length; x++) {
-
x_pos = (_root["brick_"+moveable_bricks[x]]._x-x_offset)/tile_size;
-
y_pos = (_root["brick_"+moveable_bricks[x]]._y-y_offset)/tile_size-1;
-
// saving in the brick array the name of the brick landed on (x_pos,y_pos)
-
bricks_in_field[x_pos][y_pos] = moveable_bricks[x];
-
}
-
// reset variable to false
-
falling = false;
-
// reset moveable_bricks array
-
moveable_bricks = new Array();
-
}
-
}
-
};
-
// function to place four bricks on the stage
-
function place_bricks() {
-
// placing the four bricks
-
for (x=0; x<4; x++) {
-
// again, look how I determine bricks position according to tile size and offsets
-
brk = _root.attachMovie("brick", "brick_"+_root.getNextHighestDepth(), _root.getNextHighestDepth(), {_x:(grid_width/2+(x%2))*tile_size+x_offset, _y:y_offset+tile_size*Math.floor(x/2)});
-
// setting a random color for the brick (frames 2 to different_colors+1)
-
brk.gotoAndStop(Math.floor(Math.random()*different_colors)+2);
-
// saving brick position
-
// 0: up left
-
// 1: up right
-
// 2: bottom left
-
// 3: bottom right
-
brk.pos = x;
-
// saving the depth of the brick into the moveable_bricks array
-
// if you look how did I assign brick names, you'll see that I can determine a brick name
-
// starting from its depth. It's simply "brick_"+<the_depth>
-
moveable_bricks[x] = brk.getDepth();
-
}
-
}
And this is the result:
Now wait for the next step to handle timing events.
Tell me what do you think about this post. I'll write better and better entries.
They can be easily customized to meet the unique requirements of your project.
3 Responses to “Make a game like Lumines with Flash - part 3”
Leave a Reply

(11 votes, average: 4 out of 5)
cool ;)
Fantastic tutorial, are there still plans to release a fourth part? Please do!
Cool
I am waiting for next steps, i hope you release it soon.