Tiling the Maze
In this section, you'll build the game environmentthe tree mazedynamically by using ActionScript. There are many advantages to using this approach. Tiling your game field means reusing the same square graphic, which is usually 20x20 pixels, to build a map that will serve as your environment throughout the game. This is an efficient process that enables you to keep your file size small. You can also get creative and use different tiles and layering, but this depends on the complexity and sophistication of your game.
This project uses two tiles to demonstrate how this concept works. The following figure shows an example of a reusable, tile-based background. You can find these tiles in the Bonus folder in the Library.
Open the Tree Maze scene. You'll notice that there is nothing on the Stage other than the Score text. The Timeline has two layers: scripts and score. The scripts layer has a folder that contains all the code neatly organized in five layers: initialize, sounds, functions, actions, and listener.
Go to the first frame of the initialize layer. Open the Actions panel and type this script:
Listing 5.2
//Initializing variables _global.Tilewidth = 30; //Tile width _global.Tileheight = 30; //Tile height var mazeDepth:Number = 1000; //Starting instance level var score:Number = 0; //Starting score var winScore:Number = 0; //End score (defined in map Array) var obstacle:Number = 2; //Variable to indicate which elements are solid var speed:Number = Tilewidth/3; //Variable to indicate player speed
This script declares seven variables that are used to build the maze: Tilewidth, Tileheight, mazeDepth, score, winScore, obstacle, and speed. Speed is defined as Tilewidth/3. If you want the character to move faster, try dividing the Tilewidth by 2 instead of 3.
NOTE
Identifying a variable as _global will make it visible to every Timeline in the movie. When using _global, do not use the variable declaration (var =) syntax.
You're using _global here because constants in Flash refer to built-in values such as those associated with the Key and Math classes. By using var to declare Timeline variables and strictly typing them, you are prevented from assigning the wrong type value to the variable.
Go to the actions layer. Add this script in the first frame:
Listing 5.3
//Create a container movie clip named 'cell' and make it the movie's root createEmptyMovieClip("cell", 1); cell._x = Tilewidth; cell._y = Tileheight; cell._lockroot;
Here you create an empty movie clip through ActionScript to act as a container and define it as the root for the duration of this movie by using the _lockroot property.
Assigning cell's x and y values to variables allows you to change the size of the maze by modifying the values associated with Tilewidth and Tileheight.
Go to the functions layer. Enter this script in Frame 1:
Listing 5.4
/* Resource Functions */ //The addSprite function attaches instances of the sprite name to the cell container. function addSprite(sprite, mc_name, x, y) { cell.attachMovie(sprite, mc_name, mazeDepth); cell[mc_name]._x = x*Tilewidth; cell[mc_name]._y = y*Tileheight; }
The function called addSprite takes four parameters: sprite, mc_name, x, and y. This function places all cells on the Stage, gives them a name, and assigns their _x and _y properties by multiplying x and y by the Tilewidth to position the movie clip. Depending on the order it is called will depend on what appears on top. In this version, puffin appears below the balloons. That can be changed either by placing the character on the Stage first or by changing the attachMovie parameter to mazeDepth++.
-
Now you are ready to create the map grid. In the actions layer, add this code to your script:
Listing 5.5
_global.map = [[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2], [2,1,0,0,0,2,0,2,0,0,0,0,0,0,2], [2,0,2,2,0,2,0,2,0,2,2,2,2,0,2], [2,0,2,0,0,0,0,0,0,0,0,0,2,0,2], [2,0,0,0,0,2,0,2,0,2,0,0,0,0,2], [2,0,2,2,0,2,2,2,2,2,0,2,2,0,2], [2,0,0,2,0,0,0,2,0,0,0,2,0,0,2], [2,2,2,2,0,2,2,0,2,2,0,2,2,2,2], [2,0,0,2,0,0,0,0,0,0,0,2,0,0,2], [2,0,2,2,0,2,2,2,2,2,0,2,0,0,2], [2,0,0,0,0,2,0,2,0,2,0,0,0,0,2], [2,0,2,0,0,0,0,0,0,0,0,0,2,0,2], [2,0,2,0,2,2,0,2,0,2,2,2,0,0,2], [2,0,0,0,0,2,0,2,0,2,2,0,0,0,2], [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]];
Using an array, you map the maze grid cell by cell, where each number identifies a game element. This map array defines where trees, balloons, and the starting character are. These values will be used to place the character, make obstacles register as solid, and calculate the winning score. In other words, this is the "heart of the game."
You now need to add these functions. Go to the function layer and enter this script right after the addSprite() function:
Right-click/Ctrl-click on the tile movie clip in the Library and assign the following Linkage properties:
Identifier: tile
Check: Export for ActionScript and Export in First Frame
Listing 5.6
//The to_tile function recalculates the x and y values passed to it based on their position relative to a tile on the stage. function to_tile(x, y) { var tileX = Math.floor(x/Tilewidth); var tileY = Math.floor(y/Tileheight); return {x:tileX, y:tileY}; } //The get_tile function, conversely, gets the identity of the tile located at the x and y positions. //(returning a value of either 0, 1 or 2) function get_tile(x, y) { var tileX = Math.floor(x/Tilewidth); var tileY = Math.floor(y/Tileheight); return (map[tileY][tileX]); }
The function to_tile() will be used to determine which tile the player's character is currently in. The function get_tile() will be used for collision detection with solid tiles (in this case, trees) to prevent the user from walking through them.
Go back to Frame 1 of the functions layer. Add a nested loop to populate the rows and columns of the maze grid. Then type this script:
Listing 5.7
function initTiles() { //Define the height and width of the maze by getting the length of each array element. var height:Number = map.length; var width:Number = map[0].length; //This set of nested for loops builds the maze, looping through the array and placing tiles on the stage, then //assigning them values based on their position and jumping to the proper display frame (either a balloon or a tree). for (y=0; y<height; y++) { for (x=0; x<width; x++) { var mc_name = "t"+x+"_"+y; addSprite("tile", mc_name, x, y); cell[mc_name].p = to_tile(cell[mc_name]._x, cell[mc_name]._y); cell[mc_name].gotoAndStop(map[y][x]+1); if (map[y][x] == 0) { winScore++; } if (map[y][x] == 1) { charLoc = [y, x]; } } } }
The initTiles() function has two local variables named height and width, which are scoped to this function. The mc_name variable is created so that Flash does not have to calculate the values for the name multiple times during the loop.
The outer loop runs for as long as y<height of the maze (the number of rows in the map array) is true. The inner loop executes for as long as x<width (the number of columns in the map array) of maze.
The nested loop is dynamically creating the names of the tiles t0_0, t0_1 through the total size of your array, which is t14_14 in this case. (Remember: Although there are 15 rows and columns, the array begins at 0,0.) After a name is created, you create an instance of the tile movie clip inside the cell container sitting on the Stage.
Next there is a pair of if statements to check the current array value. If the array value is a balloon (0), the winScore variable will be incremented. This is the one you will check against to see if the player has gotten all of the balloons. If the array value is the player (1), you will generate a variable to use when we place the character in the maze.
The following figure shows what your code should look like at this point.
Save your work.