New tile based platform engine – part 10 – optimization, doors n’ keys

Here we are with some optimization, as well as a feature some people requested: doors and keys.

Let’s start with the first optimization step.

Optimization

See how does the script at step 9 determine if a tile is walkable (player can pass through it):

if ((top_right != 0 and top_right != 5 and top_right != 6 and top_right != 7) or (bottom_right != 0 and bottom_right != 5 and bottom_right != 6 and bottom_right != 7)) {

I have to check if all values are different from 0, 5, 6 and 7 because 0 = empty, 5 = cloud, 6 = ladder and 7 = trampoline.

Nothing difficult at this point, but let’s imagine I am going to add another walkable tile… I should update the and formula by adding the new tile type.

The more walkable tiles I add, the more the probability of making errors or forgetting something.

So I created an array with all walkable tiles this way:

walkable_tiles = Array(0, 5, 6, 7);

Then the old code becomes

if (!is_walkable(top_right) or !is_walkable(bottom_right)) {

Where is_walkable is basically an in_array function (not included in AS2!!)

1
2
3
4
5
6
7
8
9
10
function is_walkable(tile) {
	walkable = false;
	for (x=0; x<walkable_tiles.length; x++) {
		if (tile == walkable_tiles[x]) {
			walkable = true;
			break;
		}
	}
	return (walkable);
}

Doors and keys

A door is a unwalkable tile that becomes a walkable one when the player takes its key.

Every key is an array made this way:

[key x pos, key y pos, door x pos, door y pos]

Meaning that the key is located at (key x pos, key y pos) and once collected opens the door at (door x pos, door y pos).

Let’s see the source:

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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
walkable_tiles = Array(0, 5, 6, 7);
tile_size = 20;
ground_acceleration = 1;
ground_friction = 0.8;
air_acceleration = 0.5;
air_friction = 0.7;
ice_acceleration = 0.15;
ice_friction = 0.95;
treadmill_speed = 2;
max_speed = 3;
xspeed = 0;
yspeed = 0;
falling = false;
gravity = 0.5;
jump_speed = 6;
climbing = false;
climb_speed = 0.8;
level = new Array();
enemy = new Array();
coin = new Array();
key = new Array();
level[0] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
level[1] = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
level[2] = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 1, 0, 0, 0, 0, 1];
level[3] = [1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1];
level[4] = [1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1];
level[5] = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1];
level[6] = [1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 1, 0, 0, 0, 0, 0, 1];
level[7] = [1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 1];
level[8] = [1, 1, 1, 1, 0, 9, 0, 0, 0, 5, 5, 0, 0, 0, 7, 0, 0, 6, 0, 0, 0, 0, 7, 0, 1];
level[9] = [1, 1, 1, 1, 1, 1, 1, 2, 2, 8, 8, 8, 8, 1, 1, 3, 3, 1, 4, 4, 1, 8, 1, 8, 1];
player = [4, 8];
enemy[0] = [10, 3, -2];
enemy[1] = [3, 3, -2];
coin[0] = [2, 2];
coin[1] = [23, 4];
key[0] = [1, 5, 5, 8];
function create_level(l) {
	_root.createEmptyMovieClip("level_container", 1);
	level_height = l.length;
	level_width = l[0].length;
	for (y=0; y<level_height; y++) {
		for (x=0; x<level_width; x++) {
			if (l[y][x] != 0) {
				t = level_container.attachMovie("tile", "t"+y+"_"+x, _root.level_container.getNextHighestDepth(), {_x:x*tile_size, _y:y*tile_size});
				t.gotoAndStop(l[y][x]);
			}
		}
	}
	place_player();
	for (x=0; x<coin.length; x++) {
		coin_mc = level_container.attachMovie("coin", "coin_"+_root.level_container.getNextHighestDepth(), _root.level_container.getNextHighestDepth(), {_x:coin[x][0]*tile_size+tile_size/2, _y:coin[x][1]*tile_size+tile_size/2+1});
		coin_mc.onEnterFrame = function() {
			if (this.hitTest(level_container.hero._x, level_container.hero._y, true)) {
				this.removeMovieClip();
			}
		};
	}
	for (x=0; x<key.length; x++) {
		key_mc = level_container.attachMovie("key", "key"+_root.level_container.getNextHighestDepth(), _root.level_container.getNextHighestDepth(), {_x:key[x][0]*tile_size+tile_size/2, _y:key[x][1]*tile_size+tile_size/2+1});
		key_mc.ind = x;
		key_mc.onEnterFrame = function() {
			if (this.hitTest(level_container.hero._x, level_container.hero._y, true)) {
				open_x = [key[this.ind][2]];
				open_y = [key[this.ind][3]];
				level[open_y][open_x] = 0;
				_root.level_container["t"+open_y+"_"+open_x].removeMovieClip();
				this.removeMovieClip();
			}
		};
	}
	for (x=0; x<enemy.length; x++) {
		foe = level_container.attachMovie("patrol", "patrol_"+_root.level_container.getNextHighestDepth(), _root.level_container.getNextHighestDepth(), {_x:enemy[x][0]*tile_size+tile_size/2, _y:enemy[x][1]*tile_size+tile_size/2+1});
		foe.speed = enemy[x][2];
		foe.onEnterFrame = function() {
			this.x_pos = this._x;
			this.y_pos = this._y;
			this.x_pos += this.speed;
			this.left_foot_x = Math.floor((this.x_pos-6)/tile_size);
			this.right_foot_x = Math.floor((this.x_pos+5)/tile_size);
			this.foot_y = Math.floor((this.y_pos+9)/tile_size);
			this.bottom = Math.floor((this.y_pos+8)/tile_size);
			this.left_foot = level[this.foot_y][this.left_foot_x];
			this.right_foot = level[this.foot_y][this.right_foot_x];
			this.left = level[this.bottom][this.left_foot_x];
			this.right = level[this.bottom][this.right_foot_x];
			if (this.left_foot != 0 and this.right_foot != 0 and this.left == 0 and this.right == 0) {
				this._x = this.x_pos;
			} else {
				this.speed *= -1;
			}
		};
	}
}
create_level(level);
_root.onEnterFrame = function() {
	ground_under_feet();
	walking = false;
	climbing = false;
	if (Key.isDown(Key.LEFT)) {
		xspeed -= speed;
		walking = true;
	}
	if (Key.isDown(Key.RIGHT)) {
		xspeed += speed;
		walking = true;
	}
	if (Key.isDown(Key.UP)) {
		get_edges();
		if (top_right == 6 or bottom_right == 6 or top_left == 6 or bottom_left == 6) {
			jumping = false;
			falling = false;
			climbing = true;
			climbdir = -1;
		}
	}
	if (Key.isDown(Key.DOWN)) {
		get_edges();
		if ((over == "ladder") or (top_right == 6 or bottom_right == 6 or top_left == 6 or bottom_left == 6)) {
			jumping = false;
			falling = false;
			climbing = true;
			climbdir = 1;
		}
	}
	if (Key.isDown(Key.SPACE)) {
		get_edges();
		if (!falling and !jumping) {
			jumping = true;
			yspeed = -jump_speed;
		}
	}
	if (!walking) {
		xspeed *= friction;
		if (Math.abs(xspeed)<0.5) {
			xspeed = 0;
		}
	}
	if (xspeed>max_speed) {
		xspeed = max_speed;
	}
	if (xspeed<max_speed*-1) {
		xspeed = max_speed*-1;
	}
	if (falling or jumping) {
		yspeed += gravity;
	}
	if (climbing) {
		yspeed = climb_speed*climbdir;
	}
	if (!falling and !jumping and !climbing) {
		yspeed = 0;
	}
	xspeed += bonus_speed;
	check_collisions();
	level_container.hero._x = x_pos;
	level_container.hero._y = y_pos;
	xspeed -= bonus_speed;
};
function ground_under_feet() {
	bonus_speed = 0;
	left_foot_x = Math.floor((x_pos-6)/tile_size);
	right_foot_x = Math.floor((x_pos+5)/tile_size);
	foot_y = Math.floor((y_pos+9)/tile_size);
	left_foot = level[foot_y][left_foot_x];
	right_foot = level[foot_y][right_foot_x];
	if (left_foot != 0) {
		current_tile = left_foot;
	} else {
		current_tile = right_foot;
	}
	switch (current_tile) {
	case 0 :
		over = "air";
		speed = air_acceleration;
		friction = air_friction;
		falling = true;
		break;
	case 1 :
		over = "ground";
		speed = ground_acceleration;
		friction = ground_friction;
		break;
	case 2 :
		over = "ice";
		speed = ice_acceleration;
		friction = ice_friction;
		break;
	case 3 :
		over = "treadmill";
		speed = ground_acceleration;
		friction = ground_friction;
		bonus_speed = -treadmill_speed;
		break;
	case 4 :
		over = "treadmill";
		speed = ground_acceleration;
		friction = ground_friction;
		bonus_speed = treadmill_speed;
		break;
	case 5 :
		over = "cloud";
		speed = ground_acceleration;
		friction = ground_friction;
		break;
	case 6 :
		over = "ladder";
		speed = ground_acceleration;
		friction = ground_friction;
		break;
	case 7 :
		over = "trampoline";
		speed = ground_acceleration;
		friction = ground_friction;
		break;
	case 8 :
		over = "spikes";
		if (left_foot == 8 and right_foot == 8) {
			place_player();
		}
	}
}
function check_collisions() {
	y_pos += yspeed;
	get_edges();
	// collision to the bottom 
	if (yspeed>0) {
		if ((bottom_right != 0 and bottom_right != 6) or (bottom_left != 0 and bottom_left != 6)) {
			// not a cloud...
			if (bottom_right != 5 and bottom_left != 5) {
				// a trampoline
				if ((bottom_right == 7 or bottom_left == 7) and (Math.abs(yspeed)>1)) {
					yspeed = yspeed*-1;
					jumping = true;
					falling = true;
				} else {
					y_pos = bottom*tile_size-9;
					yspeed = 0;
					falling = false;
					jumping = false;
				}
			} else {
				//cloud
				if (prev_bottom<bottom) {
					y_pos = bottom*tile_size-9;
					yspeed = 0;
					falling = false;
					jumping = false;
				}
			}
		}
	}
	// collision to the top                                                                
	if (yspeed<0) {
		if ((top_right != 0 and top_right != 5 and top_right != 6) or (top_left != 0 and top_left != 5 and top_left != 6)) {
			y_pos = bottom*tile_size+1+8;
			yspeed = 0;
			falling = false;
			jumping = false;
		}
	}
	x_pos += xspeed;
	get_edges();
	// collision to the left           
	if (xspeed<0) {
		if (!is_walkable(top_left) or !is_walkable(bottom_left)) {
			x_pos = (left+1)*tile_size+6;
			xspeed = 0;
		}
	}
	// collision to the right                                                                                       
	if (xspeed>0) {
		if (!is_walkable(top_right) or !is_walkable(bottom_right)) {
			x_pos = right*tile_size-6;
			xspeed = 0;
		}
	}
	prev_bottom = bottom;
}
function get_edges() {
	// right edge
	right = Math.floor((x_pos+5)/tile_size);
	// left edge   
	left = Math.floor((x_pos-6)/tile_size);
	// bottom edge
	bottom = Math.floor((y_pos+8)/tile_size);
	// top edge
	top = Math.floor((y_pos-9)/tile_size);
	// adjacent tiles
	top_right = level[top][right];
	top_left = level[top][left];
	bottom_left = level[bottom][left];
	bottom_right = level[bottom][right];
}
function place_player() {
	level_container.hero.removeMovieClip();
	x_pos = player[0]*tile_size+tile_size/2;
	y_pos = player[1]*tile_size+tile_size/2+1;
	level_container.attachMovie("hero", "hero", _root.level_container.getNextHighestDepth(), {_x:x_pos, _y:y_pos});
}
function is_walkable(tile) {
	walkable = false;
	for (x=0; x<walkable_tiles.length; x++) {
		if (tile == walkable_tiles[x]) {
			walkable = true;
			break;
		}
	}
	return (walkable);
}

And this is the game:

Download the source code and give feedback.

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (12 votes, average: 5.00 out of 5)
Loading ... Loading ...
If you found this post useful, please consider a small donation.
» 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.

20 Responses to “New tile based platform engine – part 10 – optimization, doors n’ keys”

  1. Doggy on October 13th, 2008 4:02 pm

    Coming along quite nicely as always. Keep it up!

  2. dix huit on October 13th, 2008 6:22 pm

    Love it.

    By the way, how’s the WordPress arcade plugin coming along?

  3. Jerry on October 13th, 2008 6:39 pm

    Great work as always.

  4. Daniel Rodriguez on October 13th, 2008 7:54 pm

    Really nice, this is getting better and better, a tile I just cant know how to do is slopes, may be you can help me with that. Thanks

  5. oliver_l1 on October 13th, 2008 8:06 pm

    Hello,
    just found another great resource for tile based games.and this is as3 only:-)

    http://www.tonypa.pri.ee/tbw/as3/start.html

  6. Questo on October 13th, 2008 10:54 pm

    I am having problems with aligning my character. when I have him to the dimensions said in the tutorials and have him centered on both the y axis and the x axis half of the hero’s body is sunk into the floors and around half of his body goes through walls. When I have him aligned in his mc so the hittests look correct the hittests with coins and other objects are off what am I doing wrong?

  7. Arxanas on October 14th, 2008 1:09 am

    Awesomeness!!! Could you make it possible to open a door if you have a certain amount of keys? And if you die, maybe the coins would go back to their original places.

    Great work as always!!

  8. Houen on October 14th, 2008 2:30 am

    Great job!
    One thing, there is a bug (or feature) – if you jump off the top of the ladder part and onto the bouncy block, and head back towards the latter, you’ll go right thorugh the ladder tile…

  9. lior on October 14th, 2008 9:50 am

    Hey, emanuele, why did you stop writing the “artillery” game toutorials?
    im stuck!!!!!! i relly need them!!!!
    (sorry for my english…)
    tnx!

  10. Robin on October 14th, 2008 12:21 pm

    I made an AS3 port of your latest tutorial! I don’t know if you have any interest in posting it?

  11. Marcos on October 14th, 2008 3:22 pm

    Hi Emanuele,

    I guess there is a bug on the ladder tile. If you stand on the top of the ladder and press jump, the char will fall from the ladder. I don’t know if this is proposital but in my opinion the char should only step down if the down key is pressed.

    Keep the awesome work!

  12. Seeker on October 14th, 2008 8:49 pm

    I love where this is going, the game engine is brilliant! Especially with the translations into AS3 for future-proofing! Its performance is so efficient; their is not much horse-power needed to run it – a flash-lite potential for the mobile phone!

    Since the roaming bots were introduced, what screamed in my head was their collision triggers. Plus elavators that transports the player up or across certain areas as well as its trampalines. (Oops, another challange ^-^).

    Also whilst looking upon this and studying Tonypa’s tile-based study as well; scrolling the environment across the screen across with the player’s excess momentum can be a possibility. Also jumping from one arena to another…

    I’d love to work on this engine and create as much gameplay mechanics too and cool imagery!

  13. Javier Lázaro on October 14th, 2008 10:58 pm

    I am always looking for ways to optimize code. Thanks.

  14. fraser on October 15th, 2008 5:58 am

    i hae a problem im trying to edit the level but after i have i try to run the game but it comes up with this error of a part of the code that i havent done anything with

    line 160 Statement block must be terminated by ‘}’ function ground_under_feet() {

    and

    line 315 Syntax error. }

  15. Atoria on October 16th, 2008 6:50 pm

    This is an amazing platform engine and heavily customizable. There are only a couple things I would still need:

    1) Collision detection with patrols to trigger an event
    2) Multiple levels via warp/door

    I will start to get cracking on this stuff, great tutorial.

  16. Thunder on October 17th, 2008 12:45 am

    Great job. You have awsome tutorials.
    I was wondering how to implement different levels.
    I tried using a switch function but It didn’t turn out right. Maybe you should show how to implement an exit portal and different levels next. Keep up the good work.

  17. Marius on October 19th, 2008 3:18 pm

    Awsome tutorial, thanks a lot! I’d like to see moving plattforms and elevators…

  18. brandon on October 19th, 2008 3:30 pm

    im only 13 but im an intellegent 13 year old thank you for the action script and the tutuorial it helped alot

Leave a Reply




Trackbacks

  1. New tile based platform engine - AS3 version updated to step 10 PLUS scrolling : Emanuele Feronato on October 14th, 2008 1:25 pm

    [...] you are looking for AS3 version of step 10, here it is… made by Robin vd Vleuten from the Netherlands with scrolling included! Hello [...]

  2. labs.VETTIGHEID » Blog Archive » Platform Tile Game with Box2D: First Attempt! on August 19th, 2009 2:07 pm

    [...] in flash-games seems to be the next hype, so I couldn’t stay behind. A while ago, I found Emanuele Feronato’s tutorials on tile-based platform games in AS2 and decided to convert them to AS3. But the engine was still not what I wanted, it was fancier if [...]

flash games company