Make a game like Lumines with Flash – part 3

Now it’s time to make bricks disappear if they group in a 2×2 square.

Read part 1 and 2 if you did not already.

It’s not difficult to check if bricks form a 2×2 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 2×2 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:

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
// 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.

Download the source.

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (12 votes, average: 4.08 out of 5)
Loading ... Loading ...
Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.
Be my fan on Facebook and follow me on Twitter! Exclusive content for my Facebook fans and Twitter followers

This post has 3 comments

  1. Massimo M.

    on July 15, 2008 at 4:33 pm

    cool ;)

  2. Martin

    on August 8, 2008 at 6:59 pm

    Fantastic tutorial, are there still plans to release a fourth part? Please do!

  3. Nitis

    on September 1, 2008 at 6:34 am

    Cool

    I am waiting for next steps, i hope you release it soon.