Applications for Tile Engines in Flash Game Design
How tile engines simplify the game design process and how to apply them to different game types.
We found this awesome author thanks to FlashGameLicense.com, the place to buy and sell Flash games!
Final Result Preview
Let's take a look at the final result we will be working towards:
Move your mouse to make the little square walk towards it, and click to make it jump. Hit space to change tile currently underneath the cursor.
Introduction: What are Tiles?
The simplest way to describe tiles is to show you images from games that have used them in the past. This is a portion of a screenshot from The Legend of Zelda:

When you look at older games, you can see that a lot of the art in the game is reused. The same tree appears twice, the same rocks appear twice, the wild grass is there three times on the screen and the fence is seen four times. And all the art is perfectly aligned to a grid. Each one of the little squares in the grid I drew over this screenshot is a tile.
Instead of making each level individually from scratch, the people who designed the Legend of Zelda decided to put the game on a grid. Then, they attached premade art to that grid. This method has a lot of benefits in game design.
Step 1: What Benefits do Tiles Have?
Most beginners who use Flash don't know much about tile based methods and what benefits they give designers. Many Flash games out today use an art based method. They draw each background and object by hand, which can be time consuming. Then the hitTest()
functions in Flash are used for collision detection. There isn't anything inherently wrong with doing this, but in many situations tiles trump the art based method.
As previously said, drawing things over and over can be time consuming. Tiles allow you to quickly build levels, and they take some of the strain of level design away from whoever is designing art for the game. Tiles also have the capacity to increase game speed. The hitTest()
method isn't very effecient. It checks to make sure that the vectors of two objects aren't touching, and detecting collisions this way takes a long time. One or two hitTests won't slow your game down, but when you need to hundreds of collision detections a second, the tile method is much faster. With tiles you only need to check points against a grid. This is less accurate (you are no longer checking the individual pixels or lines of the shapes against each other), but in many situations checking collisions against a grid works just fine.
Step 2: Understanding 2D Arrays
Arrays are pretty simple objects in Actionscript. Let's begin by making our Document class, (which will be called Main) and creating a 2D array.
1 |
|
2 |
package { |
3 |
import flash.display.Sprite; |
4 |
import flash.display.MovieClip; |
5 |
public class Main extends MovieClip { |
6 |
public var gameLay:Array = [[1,1,1,1,1,1,1,1,1,1], |
7 |
[1,0,0,0,0,0,0,0,0,1], |
8 |
[1,0,0,0,0,0,0,0,0,1], |
9 |
[1,0,0,0,0,1,0,0,0,1], |
10 |
[1,1,1,1,1,1,1,1,1,1]] |
11 |
public function Main():void { |
12 |
if (stage) { |
13 |
init(); |
14 |
} else { |
15 |
addEventListener(Event.ADDED_TO_STAGE,init); |
16 |
}
|
17 |
}
|
18 |
private function init(e:Event=null):void { |
19 |
}
|
20 |
}
|
21 |
}
|
(If you're not sure how to use a document class, check out this quick introduction to them. You should also take a look at Dru Kepple's AS3 101:Arrays if you need a clearer idea of what arrays are.)
We use the init()
method to make sure that the constructor doesn't run before the document class is instantiated. I just find it to be good form when coding.
While a 1D array is just a line of data, a 2D array is an array which contains multiple arrays. This allows us to make gameLay our "map". When we start generating tiles on the screen, they will appear in the way that gameLay looks.
Step 3: Understanding Tile Data
The 2D array is going to be our "map". But right now, our map only has two numbers in it: 1 and 0. When we begin generating visuals, the 1's will become solid ground (black squares in the demo above), and the 0's will be empty space. For this tutorial, we only need to have three different numbers to talk about the ground. (Solid, empty, and we'll introduce a third later on to store the player's position.) If we go back and look at the Zelda screenshot, you can see that there has to be at least 10+ tiles in that one screen alone.
Now the obvious solution would be to add more numbers, but there are more things to think about. Overall in the Zelda game, there has to be at least 100 more tiles then what's shown on the screen at that one point in time. What professional designers do is decide to change the way data is interpreted, rather then have unique data for each and every object. They change tilesets rather then changing each tile - so a 7 might mean grass in one area of the game, and water in another. We won't go too in depth with this at the moment, but it's something to think about.
Step 4: Creating the Basic Tile
Open Flash and start a new AS3 document - make sure that the document class is set to Main. Also make sure that the FLA and class are in the same folder. But that's something you should know about before starting this tutorial.
Okay, so your document should be 600x300. When we look back, gameLay which is acting as our "map" is 10x5. If we want each tile to be the same size, and the tiles to fill up the screen entirely, we need to make each tile (600/10) Width and (300/5) Height. So 60x60px is how big we need each of our tiles to be.
Let's make a big black square that's 60x60 and give it a class name of Tiles (with a capital T, unlike the screenshot). It's important to orient it to the TOP LEFT of the MovieClip (see the little black square in "Registration"):



After you make the MovieClip, make sure that the first frame of Tiles is blank, and that the second frame contains the black square.
Step 5: Generating Tile Graphics
Okay, at this point we have the tile map data we made in Step 2. Now we need to go through and interpret that data, and spit out something that we can use to put the tiles on screen. To make it simpler we're going to build a function that will add tiles to the screen based on the data in the array. This function belongs in Main, the document class:
1 |
|
2 |
public function buildLevel(s:Array){ |
3 |
if(getChildByName("tileHolder")!=null){ |
4 |
removeChild(getChildByName("tileHolder")); |
5 |
}
|
6 |
var tiles:MovieClip = new MovieClip(); |
7 |
tiles.name = "tileHolder"; |
8 |
for(var i=0; i<s[0].length;i++){ |
9 |
for(var o=0; o<s.length;o++){ |
10 |
var currentTile:Tiles = new Tiles(); |
11 |
currentTile.x = i*60; |
12 |
currentTile.y = o*60; |
13 |
currentTile.name = i+"-"+o; |
14 |
tiles.addChild(currentTile); |
15 |
currentTile.gotoAndStop(int(s[o][i])+1); |
16 |
}
|
17 |
}
|
18 |
addChild(tiles); |
19 |
}
|
We used a nested loop to go through each array element. An empty movie clip, called "tiles" in code but given a name of "tileHolder", is used to hold the tiles so we can remove them from the screen or move them easily. The currentTile is added to this tiles clip. We tell currentTile to go to the frame specified by the array element. Because we're using 0,1 in our tile data, we need to add 1 to the number in the actual array to make sure that the tiles are stopping at frames 1 and 2. Now even though we built this function, we still need to call it for it to work.
1 |
|
2 |
private function init(e:Event=null):void { |
3 |
buildLevel(gameLay); |
4 |
}
|



And here is how it should look when we export it. Any changes you make to the gameLay() array are reflected in what happens when you export. If we had more elements, we could easily tell the buildLevel function to change what to spawn based on what popped up in the array.
Have a go at changing the layout of the level by modifying the gameLay array.
Step 6: Destroying Tile Graphics
At points, you'll need to remove all the tiles on screen. Even though changing things on screen won't change the tile data itself, we still need to remove the graphics. Because we wrapped our graphics in our tiles clip, we can remove them easily. In fact, the function above removes the tiles itself.
1 |
|
2 |
if(getChildByName("tileHolder")!=null){ |
3 |
removeChild(getChildByName("tileHolder")); |
4 |
}
|
5 |
var tiles:MovieClip = new MovieClip(); |
6 |
tiles.name = "tileHolder"; |
This is a snippet from the buildLevel() function, which you've already written. I gave tiles the name "tileHolder" because it's the easier way to reference it in the display list. The buildLevel function checks to see if there is already an instance of the "tileHolder" onscreen, and if it's there, it removes it. Then it goes on to make another tiles clip to hold the next round of graphics. We could split this into two different functions, one for destruction and one for level creation, but for now it's best to keep things compact.
Step 7: Create and Export Player Graphics
Okay, so we have the tile graphics in place and we can change the data and see it. But how do we get something to interact with the envirnoment? First we need to make a player clip. We're going to make it 10x10, and export it with a class name of "player".



Feel free to draw something other than a box!
Step 7: Creating Player Class
We need to make a class for our player. We'll keep the functions blank for now and expand on them later. If you look you'll see we haven't created an ENTER_FRAME event. I find that it's best to make only one ENTER_FRAME, and call functions on other objects for what we want to be ENTER_FRAME.
1 |
|
2 |
package { |
3 |
import flash.events.Event; |
4 |
import flash.display.MovieClip; |
5 |
import flash.events.MouseEvent; |
6 |
public class player extends MovieClip { |
7 |
public var gameLay:Array = new Array(); |
8 |
public var ySpeed:Number = 0; |
9 |
public var xSpeed:Number = 5; |
10 |
public var friction:Number = .7; |
11 |
public var grav:Number = .5; |
12 |
public function player():void { |
13 |
if (stage) { |
14 |
init(); |
15 |
} else { |
16 |
addEventListener(Event.ADDED_TO_STAGE,init); |
17 |
}
|
18 |
}
|
19 |
public function getTile(xs,ys){ |
20 |
return gameLay[Math.floor(ys/60)][Math.floor(xs/60)] |
21 |
}
|
22 |
public function onFrameEnters():void { |
23 |
}
|
24 |
public function jump(e:MouseEvent){ |
25 |
}
|
26 |
private function init(e:Event=null):void { |
27 |
stage.addEventListener(MouseEvent.CLICK,jump); |
28 |
}
|
29 |
}
|
30 |
}
|
We'll break this down in the following steps.
Step 8: Understanding Platformer Physics
Before we make our player interact with the enviroment, I think we should go over how platformers work first. In most platformers, a player jumps up, and then comes down. When they touch the ground they lose speed and are pushed out of the ground. To illustrate this with psuedocode:
1 |
|
2 |
while(player.isCollidingWith(ground)){ |
3 |
player.y--; |
4 |
player.ySpeed = 0; |
5 |
}
|
6 |
ySpeed+=gravity; //accelerate downwards |
7 |
if(player.press(jumpButton)){ |
8 |
ySpeed = -10; |
9 |
}
|
It's a simplified way to look at platformers, but it gets you the basics. I haven't really covered platformers in this tutorial so I'm not going any deeper into the subject. This is all the logic we need to make our player work.
Step 9: Checking Position of Tiles
Because all the visual elements of our game are based on arrays, we can use the arrays to check collisions rather then relying on the Flash hitTest functions. We're going to create a function of our own to handle collisions based on our tile data:
1 |
|
2 |
public function getTile(xs,ys){ |
3 |
return gameLay[Math.floor(ys/60)][Math.floor(xs/60)] |
4 |
}
|
That's our function, taken from the player class, and it really is that simple. The function accepts two parameters: xs for x variables and ys for y variables. Because our tiles are 60x60, if we divide the x and y variables by 60, and then round them down, we get the "position" of the x and y variables in relation to the array. The function then returns whatever tile value is in that position in the array. We can use this function like we would use hitTest now, except this function is much quicker and doesn't rely on visuals. Let's go ahead and integrate it into the game.
Step 10: Integrating getTile Function
So now we have a function, getTile, that can get tile values for us. And we know that any time a 1 pops up in the array that it is going to be solid ground. Let's take a look at how the player class looks when we integrate the getTile function and add platforming physics.
1 |
|
2 |
package { |
3 |
import flash.events.Event; |
4 |
import flash.display.MovieClip; |
5 |
import flash.events.MouseEvent; |
6 |
public class player extends MovieClip { |
7 |
public var gameLay:Array = new Array(); |
8 |
public var ySpeed:Number = 0; |
9 |
public var xSpeed:Number = 5; |
10 |
public var friction:Number = .7; |
11 |
public var grav:Number = .5; |
12 |
|
13 |
public function getTile(xs,ys){ |
14 |
return gameLay[Math.floor(ys/60)][Math.floor(xs/60)] |
15 |
}
|
16 |
|
17 |
public function onFrameEnters():void { //we'll call this in Main's ENTER_FRAME handler |
18 |
xSpeed*=friction; //slow the player down due to friction |
19 |
ySpeed+=grav; //accelerate downwards |
20 |
x+=xSpeed; |
21 |
y+=ySpeed; |
22 |
|
23 |
//here we are moving the player left or right based on the mouse position
|
24 |
if(stage.mouseX>x+10){ |
25 |
xSpeed = 6 |
26 |
}
|
27 |
if(stage.mouseX<x-10){ |
28 |
xSpeed = -6 |
29 |
}
|
30 |
|
31 |
//next group of while statements are equivalent, together, to our pseudocode's
|
32 |
//"while(player.isCollidingWith(ground))"
|
33 |
while(getTile(x,y) == 1){ |
34 |
y-=.5; |
35 |
ySpeed = 0; |
36 |
}
|
37 |
while(getTile(x,y-10) == 1){ |
38 |
y+=.5; |
39 |
ySpeed = 0; |
40 |
}
|
41 |
while(getTile(x+5,y-5) == 1){ |
42 |
x-=.5; |
43 |
}
|
44 |
while(getTile(x-5,y-5) == 1){ |
45 |
x+=.5; |
46 |
}
|
47 |
}
|
48 |
|
49 |
//when the player clicks, the character jumps
|
50 |
public function jump(e:MouseEvent){ |
51 |
if(getTile(x,y+2) == 1){ |
52 |
ySpeed = -10; |
53 |
}
|
54 |
}
|
55 |
|
56 |
public function player():void { |
57 |
if (stage) { |
58 |
init(); |
59 |
} else { |
60 |
addEventListener(Event.ADDED_TO_STAGE,init); |
61 |
}
|
62 |
}
|
63 |
private function init(e:Event=null):void { |
64 |
stage.addEventListener(MouseEvent.CLICK,jump); |
65 |
}
|
66 |
}
|
67 |
}
|
We use getTile in areas where we would use hitTests. We have walls integrated into the platformer engine as well. This handles all our platformer physics, and we only need to touch the player class one more time. We can pass the gameLay array to the player through the document class.
Step 11: Guarding Against Faulty Checks
What happens if the player goes out of the playing area? The tile data is only 10x5, which translates to the size of the screen. If our getTile function gets anything above 600 or below 0 for an x value, it's going to throw an error because that area doesn't exist in the tile data. (601/60) = 10.1111, round it down to 10. Values in the array are 0-9, because arrays start counting from 0.
We need to make sure that anytime something is out of the playing bounds, it'll get registered as a solid ground. we can do this easily by changing up the getTile function.
1 |
|
2 |
public function getTile(xs,ys){ |
3 |
if(ys>0 && xs>0 && (gameLay[0].length)*60>xs && (gameLay.length)*60>ys){ |
4 |
return gameLay[Math.floor(ys/60)][Math.floor(xs/60)]; |
5 |
}else{ |
6 |
return 1; //tiles that are out of bounds are treated as solid ground |
7 |
}
|
This makes sure that everything that is outside of the bounds of the array will register as solid ground. Now you could pass an array full of 0's to the player, and the engine would be able to handle itself.
Step 12: Placing Character on Screen
So we have our character all coded, but when we stick him on the screen he does nothing. This is because we haven't actually called any of the functions necessary for him to move. We can add an ENTER_FRAME event handler to our document class though, and use that:
1 |
|
2 |
private function init(e:Event=null):void { |
3 |
buildLevel(gameLay); |
4 |
addEventListener(Event.ENTER_FRAME, onFrameEnter); |
5 |
var p1:player = new player(); |
6 |
p1.name = "p1" |
7 |
p1.x = 300 |
8 |
p1.y = 100 |
9 |
p1.gameLay = gameLay; |
10 |
addChild(p1); |
11 |
}
|
12 |
public function onFrameEnter(e:Event=null):void { |
13 |
for(var i:int = numChildren-1; i >= 0; i--){ |
14 |
if(getChildAt(i) is player){ |
15 |
MovieClip(getChildAt(i)).onFrameEnters(); |
16 |
}
|
17 |
}
|
18 |
}
|
This can be a lot to take in, so let's look at the init() function first. In the init() function, we add an ENTER_FRAME event handler that calls the onFrameEnter() function every frame. On top of that, we're adding the player to the frame by using the init() function. We place the player in the middle of the screen. In addition to that, we make the player's gameLay (which is the map) the same as the gameLay that the document class is using.
In the onFrameEnter() function, we have a loop. In the loop, we go through each and every child in the display list. Each child in the display list for a certain object has a certain index number, based on when it was added to the stage and other factors. The index numbers for children will never exceed the amount of children an object has. Since index numbers start at 0, we initialize our loop at numChildren-1. When then make sure that i
does not go below 0 and go on with our loop.
Because we loop through every child on the stage, we can check and see if certain children are of a certain class. After that, we can call functions on them, like we did for player. This method may seem complex, but once you understand it and set it up it saves quite a lot of system resources over making multiple ENTER_FRAME functions. If we had more classes, we could add them to the if
statement to ensure that every object we wanted to was touched.
Step 13: Placement Using Tile Data
What happens if we want to have the player put on stage based on a number in gameLay, rather then placing it using straight x and y values? Well, we can just add a value in the array that handles where the player will spawn, then change Main's buildLevel() function to handle this:
1 |
|
2 |
public function buildLevel(s:Array){ |
3 |
if(getChildByName("tileHolder")!=null){ |
4 |
removeChild(getChildByName("tileHolder")); |
5 |
}
|
6 |
var tiles:MovieClip = new MovieClip(); |
7 |
tiles.name = "tileHolder"; |
8 |
for(var i=0; i<s[0].length;i++){ |
9 |
for(var o=0; o<s.length;o++){ |
10 |
if(int(s[o][i]) == 2){ |
11 |
MovieClip(getChildByName("p1")).x = 30+(i*60); |
12 |
MovieClip(getChildByName("p1")).y = 30+(o*60); |
13 |
}else{ |
14 |
var currentTile:Tiles = new Tiles(); |
15 |
currentTile.x = i*60 |
16 |
currentTile.y = o*60 |
17 |
currentTile.name = i+"-"+o |
18 |
tiles.addChild(currentTile); |
19 |
currentTile.gotoAndStop(int(s[o][i])+1); |
20 |
}
|
21 |
}
|
22 |
}
|
23 |
addChild(tiles); |
24 |
}
|
Now, if we were to put a 2 in the gameLay array, it would put the player in that position in the array. We need to remember that it won't put the player directly in the center of the tile as we want, so we need to add a few modifier numbers to put it in the center (hence adding 30 to the x and y coords).
Step 14: Understanding 3D Arrays
Many times when you're making a game, you want to have more then one level. What happens if we want our player to be able to switch between levels? We're going to have to hold multiple levels in data now, and one of the simplest ways to do that is a 3D array.
If a 1D array is a line, and a 2D array is a square, then a 3D array would be a cube. A 3D array can hold multiple 2D arrays, and because our level is a 2D array, we can use a 3D array to hold multiple maps. There are plenty of ways to hold multiple levels full of tile data, one of my favorites being XML, but 3D arrays are the simplest. We'll need to change a few of our functions though:
1 |
|
2 |
public var gameLay:Array = [[[1,1,1,1,1,1,1,1,1,1], |
3 |
[1,0,0,0,0,0,0,0,0,1], |
4 |
[1,0,0,0,2,0,0,0,0,1], |
5 |
[1,0,0,0,0,1,0,0,0,1], |
6 |
[1,1,1,1,1,1,1,1,1,1]], |
7 |
|
8 |
[[1,1,1,1,1,1,1,1,1,1], |
9 |
[1,0,1,1,1,0,0,0,0,1], |
10 |
[1,0,0,0,2,0,0,0,0,1], |
11 |
[1,0,0,0,0,1,0,0,0,1], |
12 |
[1,1,1,1,1,1,1,1,1,1]]]; |
13 |
|
14 |
private function init(e:Event=null):void { |
15 |
buildLevel(gameLay[0]); //grab the first 2D array from the 3D array |
16 |
addEventListener(Event.ENTER_FRAME, onFrameEnter); |
17 |
var p1:player = new player(); |
18 |
p1.name = "p1" |
19 |
p1.x = 300 |
20 |
p1.y = 100 |
21 |
p1.gameLay = gameLay[0]; //grab the first 2D array from the 3D array |
22 |
addChild(p1); |
23 |
}
|
Now we have a 3-dimensional array that contains two different levels. If we want to switch between levels, it's a simple process to do so: we just use gameLay[1] instead of gameLay[0].
Step 15: Switching Levels on the Fly
The way we've built our tile system so far, it won't take much to switch levels on the fly. Because our buildLevel function handles all the backend work, changing levels is as simple as this:
1 |
|
2 |
buildLevel(gameLay[0]); |
3 |
buildLevel(gameLay[1]); |
How you apply this method to switching levels is up to you. For the moment, we are going to go back to a single 2D array for this.
Step 16: Understanding Multiple Uses
For the moment we have this tile engine set up to handle a platformer, but there are many different uses for engines like these.
When you look at some professionally developed games, you can see tile engine applications. As we already demonstrated with Zelda, many games use tile engines.



From left to right, the games shown are Enough Plumbers, Bejeweled, and Time Fukc. Each game shows a grid, which means tiles were used to create the games. Once you fully understand how to manipulate tile data, you can start to perform complex checks for patterns like those done in Bejeweled for puzzle games, which is our next topic.
Step 17: Simple Puzzle Game
Let's dump the idea of a platformer for a moment, and imagine we want to make a puzzle game. Something simple. Let's say that we want to make it so that if all the tiles on the screen are black, you win. You'll be able to add or remove a block based on the position of the mouse, and pressing space.
There are two steps to doing this. We need to make the function that allows us to change the blocks, then we need to write a function to check and see if all the blocks on the screen are there. When we say if the blocks are "there", that means that tile is black.
1 |
|
2 |
package { |
3 |
import flash.display.Sprite; |
4 |
import flash.events.Event; |
5 |
import flash.display.MovieClip; |
6 |
import flash.events.KeyboardEvent; |
7 |
import Tiles; |
8 |
import player; |
9 |
public class Main extends MovieClip { |
10 |
public var gameLay:Array = [[1,1,1,1,1,1,1,1,1,1], |
11 |
[1,0,0,0,0,0,0,0,0,1], |
12 |
[1,0,0,0,0,0,0,0,0,1], |
13 |
[1,0,0,0,0,1,0,0,0,1], |
14 |
[1,1,1,1,1,1,1,1,1,1]] |
15 |
public function getTile(xs,ys){ |
16 |
return gameLay[Math.floor(ys/60)][Math.floor(xs/60)] |
17 |
}
|
18 |
public function onFrameEnter(e:Event=null):void { |
19 |
for(var i:int = numChildren-1; i >= 0; i--){ |
20 |
if(getChildAt(i) is player){ |
21 |
MovieClip(getChildAt(i)).onFrameEnters(); |
22 |
}
|
23 |
}
|
24 |
|
25 |
}
|
26 |
|
27 |
public function Main():void { |
28 |
if (stage) { |
29 |
init(); |
30 |
} else { |
31 |
addEventListener(Event.ADDED_TO_STAGE,init); |
32 |
|
33 |
}
|
34 |
}
|
35 |
|
36 |
private function init(e:Event=null):void { |
37 |
buildLevel(gameLay); |
38 |
stage.addEventListener(KeyboardEvent.KEY_DOWN,clicks); |
39 |
addEventListener(Event.ENTER_FRAME, onFrameEnter); |
40 |
}
|
41 |
|
42 |
public function buildLevel(s:Array){ |
43 |
if(getChildByName("tileHolder")!=null){ |
44 |
removeChild(getChildByName("tileHolder")); |
45 |
}
|
46 |
var tiles:MovieClip = new MovieClip(); |
47 |
tiles.name = "tileHolder"; |
48 |
for(var i=0; i<s[0].length;i++){ |
49 |
for(var o=0; o<s.length;o++){ |
50 |
var currentTile:Tiles = new Tiles(); |
51 |
currentTile.x = i*60 |
52 |
currentTile.y = o*60 |
53 |
currentTile.name = i+"-"+o |
54 |
tiles.addChild(currentTile); |
55 |
currentTile.gotoAndStop(int(s[o][i])+1); |
56 |
}
|
57 |
}
|
58 |
addChild(tiles); |
59 |
}
|
60 |
|
61 |
private function clicks(e:KeyboardEvent):void { |
62 |
if(e.keyCode== 32){ |
63 |
if(getTile(stage.mouseX, stage.mouseY)==1){ |
64 |
gameLay[Math.floor(stage.mouseY/60)][Math.floor(stage.mouseX/60)] = 0; |
65 |
MovieClip(MovieClip(getChildByName("tileHolder")).getChildByName(Math.floor(stage.mouseX/60)+"-"+Math.floor(stage.mouseY/60))).gotoAndStop(1); |
66 |
}else{ |
67 |
gameLay[Math.floor(stage.mouseY/60)][Math.floor(stage.mouseX/60)] = 1; |
68 |
MovieClip(MovieClip(getChildByName("tileHolder")).getChildByName(Math.floor(stage.mouseX/60)+"-"+Math.floor(stage.mouseY/60))).gotoAndStop(2); |
69 |
}
|
70 |
}
|
71 |
var foundZero:Boolean = false; |
72 |
for(var i=0; i<gameLay[0].length;i++){ |
73 |
for(var o:int = 0; o < gameLay.length; o++){ |
74 |
if(int(gameLay[o][i]) == 0){ |
75 |
foundZero = true; |
76 |
}
|
77 |
}
|
78 |
}
|
79 |
if(!foundZero){ |
80 |
trace("won"); |
81 |
}
|
82 |
}
|
83 |
}
|
84 |
}
|
For the sake of clarity, this is the entire document class needed to create the game that we were discussing. Notice how similar it is to our platformer code. In the init()
function, we've added an event listener for key presses; it triggers the clicks()
function, which handles changing the tiles themselves. Let's break this new function down.
We first check to make sure that the key being pressed is space, by checking the keyCode. Because we're checking the position of the tiles using the mouse and the getTile function, all we need to do is check and see if whatever tile is at that point in the map is a one or a zero to find out what to change it to.
We then change the data itself, and change the visual display of the tiles to reflect the data. It takes a lot to target the tiles individually, but because we named each tile based on their description, we can access them using the getChildByName method.
Once we're done changing the tiles, we need to check and see if every tile on the screen is black. We do that by running through the data, and checking to see if any zeroes pop up. If no zeroes do pop up, we know that everything is a 1 (because our data only uses 1s and 0s).
So there we have it. A very simple puzzle game that uses tile data. If we wanted, we could do complex checks, much like bejeweled or other such puzzle games to find out certain combinations of tiles.
Step 18: Combining Elements from Both Games
What if we wanted a platformer where you could created and destroy ground easily using the space bar? We could do that easily by just combining the two different games we made, in a simple manner.
Step 19: Platformer
Let's create a platformer that allows you to create and destroy data. Here's the code:
1 |
|
2 |
package { |
3 |
import flash.display.Sprite; |
4 |
import flash.events.Event; |
5 |
import flash.display.MovieClip; |
6 |
import flash.events.KeyboardEvent; |
7 |
import Tiles; |
8 |
import player; |
9 |
public class Main extends MovieClip { |
10 |
public var gameLay:Array = [[1,1,1,1,1,1,1,1,1,1], |
11 |
[1,0,0,0,0,0,0,0,0,1], |
12 |
[1,0,0,0,0,0,0,0,0,1], |
13 |
[1,0,0,0,0,1,0,0,0,1], |
14 |
[1,1,1,1,1,1,1,1,1,1]] |
15 |
|
16 |
public function getTile(xs,ys){ |
17 |
return gameLay[Math.floor(ys/60)][Math.floor(xs/60)] |
18 |
}
|
19 |
|
20 |
public function onFrameEnter(e:Event=null):void { |
21 |
for(var i:int = numChildren-1; i >= 0; i--){ |
22 |
if(getChildAt(i) is player){ |
23 |
MovieClip(getChildAt(i)).onFrameEnters(); |
24 |
}
|
25 |
}
|
26 |
|
27 |
}
|
28 |
|
29 |
public function Main():void { |
30 |
if (stage) { |
31 |
init(); |
32 |
} else { |
33 |
addEventListener(Event.ADDED_TO_STAGE,init); |
34 |
|
35 |
}
|
36 |
}
|
37 |
|
38 |
private function init(e:Event=null):void { |
39 |
buildLevel(gameLay); |
40 |
stage.addEventListener(KeyboardEvent.KEY_DOWN,clicks); |
41 |
addEventListener(Event.ENTER_FRAME, onFrameEnter); |
42 |
var p1:player = new player(); |
43 |
p1.name = "p1" |
44 |
p1.x = 300 |
45 |
p1.y = 100 |
46 |
p1.gameLay = gameLay; |
47 |
addChild(p1); |
48 |
}
|
49 |
|
50 |
public function buildLevel(s:Array){ |
51 |
if(getChildByName("tileHolder")!=null){ |
52 |
removeChild(getChildByName("tileHolder")); |
53 |
}
|
54 |
var tiles:MovieClip = new MovieClip(); |
55 |
tiles.name = "tileHolder"; |
56 |
for(var i=0; i<s[0].length;i++){ |
57 |
for(var o=0; o<s.length;o++){ |
58 |
var currentTile:Tiles = new Tiles(); |
59 |
currentTile.x = i*60 |
60 |
currentTile.y = o*60 |
61 |
currentTile.name = i+"-"+o |
62 |
tiles.addChild(currentTile); |
63 |
currentTile.gotoAndStop(int(s[o][i])+1); |
64 |
}
|
65 |
}
|
66 |
addChild(tiles); |
67 |
}
|
68 |
|
69 |
private function clicks(e:KeyboardEvent):void { |
70 |
if(e.keyCode == 32){ |
71 |
if(!(Math.floor(stage.mouseX/60) == Math.floor(MovieClip(getChildByName('p1')).x/60)&&Math.floor(stage.mouseY/60) == Math.floor((MovieClip(getChildByName('p1')).y-10)/60))){ |
72 |
if(getTile(stage.mouseX, stage.mouseY)==1){ |
73 |
gameLay[Math.floor(stage.mouseY/60)][Math.floor(stage.mouseX/60)] = 0 ; |
74 |
MovieClip(MovieClip(getChildByName("tileHolder")).getChildByName(Math.floor(stage.mouseX/60)+"-"+Math.floor(stage.mouseY/60))).gotoAndStop(1); |
75 |
}else{ |
76 |
gameLay[Math.floor(stage.mouseY/60)][Math.floor(stage.mouseX/60)] = 1 ; |
77 |
MovieClip(MovieClip(getChildByName("tileHolder")).getChildByName(Math.floor(stage.mouseX/60)+"-"+Math.floor(stage.mouseY/60))).gotoAndStop(2); |
78 |
}
|
79 |
}
|
80 |
}
|
81 |
}
|
82 |
}
|
83 |
}
|
We add the player back into the game, and change up the clicks() function. We change it to make sure that tiles that contain the player cannot be filled in. By making these simple changes we can meld the two engines together to make an engine that works well.
Step 20: Endless Uses
As stated before, tile engines have a plethora of ways to be used. Puzzle games, platformers. You can create multiple layers of tiles to create backgrounds, and once you begin creating complex games you'll have to make your own map editors to handle and create the tile data for your games. The best way to learn is to experiment. Write your own tile engines, figure out what works best for your games and applications and find out what you need to do to get what you want to work to work.
Thanks for reading!