Flash Game Development by Example

5 (1 reviews total)
By Emanuele Feronato
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Concentration

About this book

You can't call yourself a Flash game developer unless you know how to build certain essential games, and can quickly use the skills and techniques that make them up.

Flash Game Development by Example is an ultra-fast paced game development course. Learn step-by-step how to build 10 classic games. Each game introduces new game development skills, techniques, and concepts. By the end of the book you will have built ten complete games – and have the skills you need to design and build your own game ideas.

The book starts with simple well known puzzle games: Concentration and Minesweeper. After learning the basics of game design you’ll introduce AI with a four-in-a-row game. Then as you build your own versions of old arcade games such as Snake, Tetris, and Astro Panic. The book ends with a collection of modern casual classics.

Publication date:
March 2011
Publisher
Packt
Pages
328
ISBN
9781849690904

 

Chapter 1. Concentration

Concentration is a memory game you can play even without a computer, just with a deck of cards. Shuffle the cards, lay them face down on a table and at each turn choose and flip any two cards with faces up.

If they match (both cards are Aces, Twos, Threes, and so on), remove them from the table. If not, lay them face down again and pick another couple of cards. The game is completed when, due to successful matches, all cards have been removed from the table.

Concentration can be played as a solitaire or by any number of players. In this case the winner is the one who removed the most cards.

In this chapter you will create a complete Concentration game from scratch, with a step-by-step approach, learning these basics:

  • Creating a Flash document

  • Working with packages, classes, and functions

  • Printing text

  • Commenting your code

  • Creating and managing variables and constants

  • Creating and managing arrays

  • Generating and rounding random numbers to simulate the shuffle of a deck of cards

  • Repeating the execution of code a given amount of times with the for loop

  • Creating Movie Clips to be added with AS3 and interacting with them on the fly

  • Handling mouse clicks

  • Dealing with timers

It's a lot of stuff, but don't worry as the whole process is easier than you can imagine.

 

Defining game design


Once you start thinking about programming a game, you are already making it. You are in pre-production stage.

During this process, gameplay as well as storyline and environment begin to take shape. Before starting to code or even turning on the computer, it's very important to define the game design. This is the step in which you will decide how the game will work, the rules and the goals of the game, as well as the amount of options and features to include.

I know you just want to start coding, but underestimating the importance of game design is a very common error. Usually we think we have everything in mind, and we want to start coding at once. Moreover, a game like Concentration looks really simple, with just one basic rule (selected cards match/don't match) and, last but not least, we just have to copy an existing game, so why not start typing right now?

Even a basic project like a Concentration remake may give you some troubles if you skip an accurate game design. Here are a few questions you probably would not ask yourself about the game you are about to make:

  • How many players can take part in the game?

  • How many cards will be placed on the table?

  • I don't have a deck of cards. Do I have to buy one and scan all the cards?

  • Are card images protected by copyright?

  • Where can I find free card images?

  • Which resolution should I use to clearly display all cards?

  • Who will play my game?

  • What difficulty levels can the player choose?

  • Will there be any background music or sound effects?

Don't hesitate to ask yourself as many questions as you can. The more decisions you take now, the easier the game will be to make.

Making changes to basic mechanics when the game is on an advanced development stage can dramatically increase developing time. A good game design won't ensure you that you will never have to rewrite some parts of the code, but it reduces the probability of you having to do it.

Anyway, be realistic and know your limits. Questions like "Do I have to use a physics engine to add realism to card flipping, maybe introducing wind or different air resistance" are welcome since you don't want to start doing this and then realize it's not needed, but avoid thinking about features you know you aren't able to add or you will quickly turn a game you are about to publish into a game you'll never make.

At the end of this process, you must have at least a set of basic rules to define how a playable prototype should work.

So here are the decisions I made for the game we will create:

  • To be played in solitaire mode.

  • The game is intended to be played on a web browser by young children.

  • Twenty cards placed on the table. Being for young children, a complete deck of cards could be too difficult.

  • Rather than the classic deck of cards, we'll use tiles with primitive colored shapes on them, such as a red circle, a green square and so on. This will let us draw the graphics on our own, without needing a card deck.

  • Player will select the cards with a mouse click.

Defining the audience of a game is very important when you are about to fine-tune the game. Being a game for young children, we'll add some educational content in it. Parents love when their children play and learn at the same time.

 

Setting stage size, frame rate, and background color


You are about to create a Flash game, and like all Flash movies, it will have its stage size (width and height in pixels), frame rate (the number of frames per second) and a background color.

Note

The area where you will add the content to be viewed is called the stage. Any content outside the stage will not be visible when playing the game.

The higher the size and the frame rate, the more CPU-intensive will be the game. But it's not just a CPU-based issue: you also have to set the size according to the device your game is designed to be played in. If you plan to design a Concentration game for smartphones, then a 1280x1024 size is probably a bad choice, because they don't support that kind of resolution.

Although we have decided to create a game to be played in browsers we should still put some effort into thinking about what size it should be.

Flash games are mostly played on famous game portals such as Kongregate (www.kongregate.com) or Armor Games (www.armorgames.com), that give players a wide choice of quality games. Since the games are embedded in web pages, they must fit in a pre-built layout, so you can't make your game as wide and tall as you want because most portals won't just pick it up and you won't be able to see your game being played by thousands of players.

As you can see from the picture, the game is not the only content of the page, but it's carefully embedded in a complex layout. There may be login forms, advertising, chat rooms, and so on.

A common error is thinking the bigger the game size, the better the graphics and the more information you can show. A good designer can make everything fit in small resolutions. A PSP console has a 480x272 resolution and a Nintendo DS has a 256x384 resolution split in two. Both consoles have awesome games.

Note

Play some successful games in various Flash game portals, and you'll see the most used sizes are 550x400 and 640x480. The former is the size we'll use for the game.

Run Adobe Flash and create a new file (File | New) then from New Document window select Actionscript 3.0.

Once we create a document the first thing we should do is set its properties. Open Properties window (Window | Properties) and you'll probably see stage size is already 550x400, because it's Flash's default movie size. Click Edit button to see Document Settings window. If you don't already have these values by default, set width to 550px, height to 400px, background color to #FFFFFF (white) and frame rate to 24. A higher frame rate means smoother animations, but also a higher CPU consumption. In this game we don't use animations, so I just left the frame rate to its default value.

You will also need to define the Document Class. Call it Main and you will probably see this alert:

Don't worry: Flash is warning you just set the main document class for the current movie, but it couldn't find a file with such class. Warm up your fingers, because it's time to code.

Now your Properties window should look like this:

The white area in the background is the stage itself.

Your Flash document is now ready to turn into a Concentration game.

Save the file (File | Save) and name it as concentration.fla then let's code Main class.

 

Welcome to Concentration ("Hello World")


At this time we just want to make sure things are working, so we are only writing some text. It's the first script of our first project, so it's a huge step anyway.

Without closing concentration.fla, create a new file and from New Document window select ActionScript 3.0 Class.

You should be brought to an empty text file. If you are using Flash CS5 you'll get a box asking for the class name. Type in Main, then delete the default script in the text file and start coding:

package {
  // importing classes
  import flash.display.Sprite;
  // end of importing classes
  public class Main extends Sprite {
    public function Main() {
      trace("Welcome to Concentration");
    }
  }
}

Save the file as Main.as in the same path where you saved concentration.fla.

At this time the content of your project folder should look like this:

As you can see, Main is repeated a lot, from the name of the document class to the name of the file you just saved.

Now it's time to test the movie (Control | Test Movie). You will see the blank stage but in the output window (Window | Output) you will see:

Welcome to Concentration

You just made your first class work. At this time you may think AS3 may not be the best language for game development as it took eight lines to do what can be easily done in other languages, such as PHP or Javascript, in just a single line. But you didn't just write "Welcome to Concentration". You defined the package, the class, and the main function of the game in just eight lines. It sounds different, doesn't it?

Let's see how it works:

package indicates that the following block of code (everything between { and }) is a package of classes and functions.

package usually is followed by a name such as package com.packagename to ensure class name uniqueness in large libraries programmers want to distribute. Since the creation of libraries for distribution is not the topic of this book, just remember to add package { to the first line and close it with } in the last line.

import flash.display.Sprite;

Imports Sprite built-in class for later use. This class allows us to display graphics. flash.display.Sprite means we are importing Sprite class from flash.display package.

public class Main extends Sprite { ...}

This defines the main class of this file (called Main). extends means the class will be built based upon Sprite class. Basically we are adding new functionalities to Sprite class. This class must be set as public so don't worry about it at the moment. You have no choice.

Note

Throughout the book you will find a lot of "three points" (…). They mean the rest of the code has not been changed.

Once the class has been defined, we have to create the constructor. It's a function that is called when a new class is created, in this case when the project is run. The constructor must have the same name of the class.

public function Main() {...} 

Defines the constructor function of the class. This must be set as public as well.

trace() will show any value you pass in the output window when the movie is executed in the Flash environment. It will become your best friend when it's time to debug. This time, displaying "Welcome to Concentration" in the output window, it will let you know everything worked fine with your class.

Congratulations. You just learned how to:

  • Decide which size your game should be.

  • Create and set up a Flash movie.

  • Code, test, and debug a working class.

At this time you had a brief introduction to classes, constructors, and functions, but that was enough to let you create and set up a Flash movie, as well as testing and printing text on the debug window.

Also notice there are comments around the code. Commenting the code is almost as important as coding itself, because good comments explain what your script is supposed to do and can help to remind you what the code is meant to be doing, especially when you aren't working on the script for a while. Also, during this book, you'll be asked to insert or modify parts of scripts identified by comments (that is "delete everything between // here and // there") so it's recommended you use the same comments you find in the book.

You can comment your code with either single line or block comments.

A single line comment starts with two slashes //, and lasts until the end of the line. The compiler will ignore everything after the //.

trace("Welcome") // I am an inline comment

A block comment starts with /* marker and ends with */ marker. The compiler will ignore everything between the markers.

/* I am
a multi-line
block comment */

Now it's time to start the real development of the game.

 

Creating the tiles


As said, we won't use a standard card deck, but tiles with basic shapes on them. We can place any number of tiles, as long as it's an even number, because any tile must have its match. So, if you want to play with ten symbols, you must have 20 tiles in game.

That's exactly what we are going to do. We will create twenty tiles, each one represented by a number from 0 to 9. Since there are two tiles for each value, we will have two zeros, two ones, two twos, and so on until two nines.

Now you may wonder: why are we representing ten tiles with numbers from 0 to 9? Wouldn't it be better to use the classic 1-10 range? Obviously representing numbers from 1 to 10 seems more meaningful, but keep in mind when you code you should always start counting from zero.

You may also wonder why we are defining tile values with numbers when we decided to use shapes. Think about a Flash game as if it were a movie. In a movie, you see what the director wants you to see. But there is a lot of stuff you will never see, although it is part of the show. Let's take a car chase: you see two cars running fast along a freeway because the director wanted you to see them. What you don't see are cameras, microphones, mixers, storyboards, safety belts, make-up artists, and so on. You only see what the camera filmed.

A game works in the same way; the player will see what happens on the stage, but he won't see what happens behind the 'scenes', and now we are working behind the scene.

Change Main function this way:

public function Main() {
  // variables and constants
  const NUMBER_OF_TILES:uint=20;
  var tiles:Array=new Array();
  // end of variables and constants
  // tiles creation loop
  for (var i:uint=0; i<NUMBER_OF_TILES; i++) {
    tiles.push(Math.floor(i/2));
  }
  trace("My tiles: "+tiles);
  // end of tiles creation loop
}

Test the movie and in the output window trace(tiles)will print:

My tiles: 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9

Let's see what we have done:

First, we created a constant called NUMBER_OF_TILES.

In AS3, you can declare constants and variables. A constant represents a value that will never change during the script. A real world example of a constant is the number of minutes in an hour. No matter how you are using minutes in your code, you will always have 60 minutes in an hour. A variable holds information that may change during the execution of the script. Referring to previous example, the amount of minutes I play Flash games each day changes according to the amount of my spare time.

Since the number of tiles will never change during the game, we defined it as a constant. But we need to give a better definition to our constant. We know NUMBER_OF_TILES is a number that can only be positive. That is, an unsigned integer.

AS3 provides three ways to define a number.

  • int—represents an integer that can be positive or negative, called signed integer.

  • uint—an unsigned integer that is used to represent numbers that can only be positive.

  • Number—(uppercase "N") is used to represent whole and fractional numbers, no matter if positive or negative. You can use it if you are unsure about int/uint, anyway you should always know which values could be stored in a variable.

As you can see, I named the constant NUMBER_OF_TILES, but I could have named it A or EXTERNAL_TEMPERATURE. You can give variables and constants any name you want, but naming them with descriptive words will help you to remember their role in your script.

Also, the name is ALLCAPS. There's nothing special in NUMBER_OF_TILES constant to be written ALLCAPS, it's just a convention to quickly distinguish constants from variables. In this book, all constants will have ALLCAPS names.

Now we need a way to manage all tiles. There is no easier way to store an ordered set of data than using arrays.

Note

Think about an array as a container holding any number of individual values in a single variable. Any individual value (called element) will have a unique index to allow an easy access.

An array representation of a normal deck of 52 cards would be this one:

Note in AS3, as with many other programming languages, array indexes start from zero. This is why we earlier talked about the cards 0 to 9.

So we declared an Array variable called tiles.

var tiles:Array=new Array(); 

This will create an array with no items in it. But we are about to populate it.

Notice constant name is uppercase while variables is lowercase. This is not mandatory, but it's recommended to write names in a way that allows you to easily recognize a variable from a constant. The scripts on this book will follow this rule.

As said earlier, the tiles will contain shapes easily recognized by children. But we won't populate the tiles array with red squares or green circles. What if tomorrow we needed to replace red squares with angry ducks?

We are working behind the scenes so let's just fill it with a pair of numbers from zero up to (but not including) NUMBER_OF_TILES/2. This way we can easily associate any symbol with any number without rewriting a single line of code.

for (var i:uint=0; i<NUMBER_OF_TILES; i++) {...}

This is a for loop. It's used to repeat the same code a given number of times. Let's see in detail how it works:

var i:uint=0; is simply declaring a new unsigned integer variable called i and assigning it the starting value of 0.

i<NUMBER_OF_TILES; means the loop will reiterate as long as the value of i is less than the NUMBER_OF_TILES value.

i++; means i is increased by 1 at the end of each iteration. The same thing can be done with i=i+1 or i+=1.

It's easy to see that everything in the for block will be executed twenty times, since we defined NUMBER_OF_TILES as 20.

tiles.push(Math.floor(i/2));

This is how we populate the array. push() method adds an element to the end of the array, while Math.floor()method returns the floor of the expression passed as parameter. In programming languages, you get the floor of a number when you round it down.

Note

Any action that the object can perform is called a method. Methods in AS3 are called with objectname.method(arguments).

So at every for iteration a new element is added at the end of tiles array. This element contains the floor of i/2, that will be 0 for i=0 (0/2 is 0) and i=1 (1/2 is 0.5 and the closest number below that is 0), 1 for i=2 and i= 3, and so on.

 

Adding randomness: shuffling the tiles


Now we managed to have a numeric representation of the tiles, but it's very easy to guess the content of each tile. We already know 0th and 1st tile values are 0, 2nd and 3rd are equal to 1, and so on.

We have to add randomness to the game by shuffling the tiles. Randomness is very important in games. Except for some genres such as adventures and puzzles that require the player to retry the same identical level until he solves a specific problem, randomness adds variety to games that would instead offer the same, boring, challenge. Just think about a card solitaire in which you know where each card is. Or a minesweeper game where you know the position of each mine. That's why the generation of random scenarios is a very important game feature.

A series of numbers is truly random if it is completely unpredictable. In other words, if we have absolutely no way of knowing what the next number is in a series of numbers, then the series is completely random. Since computers are 100% predictable, generating true random numbers is not an easy task. Applications like online casino software and security- programs (that is password generators, data encryption, and more) demand the highest randomness possible. When programming games, we can take it easy.

Every programming language has its own function to generate random numbers, and that's enough for game development.

There are a lot of routines to shuffle an array, but we are using a modern variant of the Fisher–Yates shuffle algorithm because it's very similar to randomly picking cards from a deck one after another until there are no more left.

A real world representation modern Fisher-Yates shuffle algorithm works this way:

  1. Align all tiles from left-to-right.

  2. Place a coin on the rightmost tile.

  3. Swap the tile with the coin with a random tile chosen among the ones to its left, but the coin doesn't move.

  4. Move the coin one card left.

  5. Repeat from step 3 until the coin is on the leftmost card.

Hopefully you get the idea that the tiles are array elements and the coin is array index:

Just after // end of tiles creation loop add the following code:

// shuffling loop
var swap,tmp:uint;
for (i=NUMBER_OF_TILES-1; i>0; i--) {
  swap=Math.floor(Math.random()*i);
  tmp=tiles[i];
  tiles[i]=tiles[swap];
  tiles[swap]=tmp;
}
trace("My shuffled tiles: "+tiles);
// end of shuffling loop

Now test the movie and you will get:

My tiles: 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9

My shuffled tiles: 1,6,2,5,7,3,0,8,3,2,0,9,9,8,4,7,6,4,1,5

The second sequence of numbers will change every time you execute the script because it's the representation of the shuffled array.

Let's see how we got this result:

var swap,tmp:uint;

We need two more variables, both of them unsigned integers. Notice you can declare more variables of the same type in a single line. This time the variables don't have an initial value, because they'll get their values during the script. I also said generally it's good to give explanatory names to variables, but here, as we're only using them very locally we can give them nice and short names that are easy to type and quick to read.

for (i=NUMBER_OF_TILES-1; i>0; i--) { ... }

This is another for loop. Do you see some differences between this loop and the one used to create the tiles? Let's compare them. This was the previous loop:

for (var i:uint=0; i<NUMBER_OF_TILES; i++) { ... } // previous loop

In the second loop we don't need to declare the i variable because it has already been declared in the first one. So we are just using i=value instead of var i:type=value.

Another difference is that the first loop increases the variable at every iteration, while the second one decreases it.

Note

Double check your for loops to ensure they will end after a finite number of iterations. This is a correct for loop for(i=0;i<1000000;i++){ ... } because it will end after a million iterations, and this is a wrong loop for(i=0;i>=0;i++){ ... } because it will never end. A loop that never ends is called an infinite loop and will crash your application.

swap=Math.floor(Math.random()*i);

We already know what Math.floor()method does. It's time to meet another Math method. Math.random() method returns a random number between 0 and 1 with 1 excluded.

This is likely to be a number with many decimal places, like 0.4567443452 so we can use it to get random numbers for high values as well. For example, if you want a random number between 0 (included) and 5 (excluded) you can just call Math.random()*5. If you want a random number between 5 (included) and 10 (excluded) you will call Math.random*5+5. In our loop we want an integer number between 0 (included) and i (excluded), so Math.floor(Math.random()*i) is exactly what we need.

At this time, swap contains an integer number between 0 (included) and i (excluded). It's time to apply Fisher-Yates shuffle algorithm and swap the content of the i-th element of the array with the swap-th one.

To do this we need a temporary variable (called tmp) to save the content of the i-th array element before overwriting it with the content of the swap-th element. Then we can overwrite the content of the swap-th element with the value we just saved.

You can think of this as like swapping apples between hands. As you cannot hold two apples in one hand to do the swap you need the help of a third hand (your tmp variable) to hold one of the apples while you swap hands for the first.

Back to our script, the swapping process can be described with this picture:

At the end of the for loop, your array will be shuffled according to the Fisher-Yates algorithm.

The Concentration core is ready. You've just managed to:

  • Declare and use variables and constants.

  • Handle arrays to store information.

  • Use loops to reiterate sequences of code.

  • Work with numbers using mathematical functions.

Take a short break, in a moment your graphics skills will be proven.

 

Placing the tiles on stage


Until now, we just have a numeric representation of the shuffled tiles. There is nothing the player can see or interact with. We need to draw the tiles.

Stop working (at the moment) at Main.as and select concentration.fla file from the upper left tabs (you should see the blank stage) and create a new symbol (Insert | New Symbol...). You will be taken to Create New Symbol window.

Fill the fields in this way:

  • Name (the name you want to give to the object): tile_movieclip.

  • Type (it can be Movie Clip, Button, or Graphic): Movie Clip (it should be the default value).

  • Folder: Library root (it should be the default value).

  • Export for ActionScript (defines if the symbol can be dynamically created using ActionScript or not): checked.

  • Export in frame 1 (used to automatically export the symbol if you don't place it on the Stage): checked (it should automatically be checked when you check Export for Actionscript).

  • Class (symbol's class): tile_movieclip (it should be prefilled using the name you gave the symbol).

  • Base Class (the class your symbol will extend): flash.display.MovieClip (it should automatically appear when you check Export for Actionscript).

Press OK.

You'll probably get the same warning as before. Ignore it. I said "probably" because you could have removed alerts or changed default values.

If you do not provide a class for your exported symbol, Flash will create the class for you with this content:

package {
  import flash.display.MovieClip;
  public class movieclip_name extends MovieClip {
    public function movieclip_name() {
    }
  }
}

That is a class doing nothing. When you create a new symbol, Flash just warns you it will create this basic class if you won't make your own.

To create the tiles, draw 10 distinct shapes in the first 10 frames of your symbol, and the back of the tile in the 11th frame. You are free to draw them as you want, but I suggest you make them as 90 pixels squares with registration point (starting x and y position) at 0, because these are the properties of the tiles used in this chapter's examples. At least, they should all be the same size.

Also notice the first frame is 1 while the first tile value is 0. You will need to remember this when you make tiles flip.

You should be familiar with Flash timeline and drawing tools. If you have not been using Flash for a long time, don't worry. Basic drawing and timeline management haven't changed that much since the very first Flash version. If you don't know how to draw objects in Flash, refer to the official documentation.

Once you are satisfied, it's time to place the tiles on the stage: change the block of code delimited by comment //variables and constants this way:

// variables and constants
const NUMBER_OF_TILES:uint=20;
const TILES_PER_ROW:uint=5;
var tiles:Array=new Array();
var tile:tile_movieclip;
// end of variables and constants

Here we need a new constant called TILES_PER_ROW. It will store the number of tiles to be displayed in a row. Setting it to 5 means we want four rows of five tiles. If we set it to 4, we will have five rows made of four tiles. This way you can modify the game layout by simply changing a value.

tile is a variable of tile_movieclip type.

Now we have to use the new variable and constant to place tiles on the stage, so add after // end of shuffling loop comment a new for loop (with a couple of new comments):

// tile placing loop
for (i=0; i<NUMBER_OF_TILES; i++) {
  tile=new tile_movieclip();
  addChild(tile);
  tile.cardType=tiles[i];
  tile.x=5+(tile.width+5)*(i%TILES_PER_ROW);
  tile.y=5+(tile.height+5)*(Math.floor(i/TILES_PER_ROW));
  tile.gotoAndStop(NUMBER_OF_TILES/2+1);
}
// end of tile placing loop

Test the movie and you'll see something like this:

The gray square with a "?" is the 11th frame of the tile_movieclip symbol.

Let's see how we made it possible:

At every for loop iteration the script places a tile on the stage.

tile=new tile_movieclip();

Creates a new tile_movieclip object.

addChild(tile);

addChild() adds an object to the Display List. It's the list that contains all visible Flash content. To make an object capable of appearing on the stage, it must be inserted in the Display List. A Display List object is called DisplayObject.

Note

Although Display List contains all visible objects, you may not be able to see some of them, for instance if they are outside the stage, or if they are behind other objects, or even because they have been told to hide.

tile.cardType=tiles[i];

Once a tile is added, you have to store somewhere its real value. We made it just by adding a property called cardType which contains the value of tiles array i-th element.

tile.x=5+(tile.width+5)*(i%TILES_PER_ROW);
tile.y=5+(tile.height+5)*(Math.floor(i/TILES_PER_ROW));

Just place the tile to be part of a grid.

Notice the presence of the modulo (%) operator. Modulo calculates the remainder of the first operator divided by the second operator. So 5%3 will give 2, because 2 is the remainder of 5 divided by 3.

Also notice the properties involved in this process:

  • x: x coordinate of the DisplayObject, in pixels from the left edge of the stage

  • y: y coordinate of the DisplayObject, in pixels from the top edge of the stage

  • width: the width of the DisplayObject, in pixels

  • height: the height of the DisplayObject, in pixels

In our example, tile number zero is the upper left one, followed by tile number one at its right, then tile number two, and so on until tile number twenty, at the bottom-right of the stage.

The recurring 5 number is the spacing between tiles. Why don't you try to define it as a constant? It would be a good exercise at this time.

tile.gotoAndStop(NUMBER_OF_TILES/2+1);

As we said, the last frame of tile_movieclip object contains the tile graphics when facing down. gotoAndStop(n) tells tile DisplayObject to go to n-th frame and stop. In our case, it's showing the 20/2+1 = 11th frame, that is the tile facing down.

 

Picking tiles


We said we are going to pick tiles with a mouse click. To manage mouse events such as clicks, movements, and rollovers, AS3 provides a dedicated class called MouseEvent. The first thing we need to do is to import this new class.

Import it before main class declaration, in the code delimited by // importing classes just like you imported Sprite class:

// importing classes
import flash.display.Sprite;
   import flash.events.MouseEvent;
// end of importing classes

MouseEvent class is contained in the flash.events package, that's why I had to import another package.

Now you are ready to handle mouse events. Modify the tile placing loop (the code between // tile placing loop and // end of tile placing loop) this way:

// tile placing loop
for (i:uint=0; i<NUMBER_OF_TILES; i++) {
  tile = new tile_movieclip();
  addChild(tile);
  tile.cardType=tiles[i];
  tile.x=5+(tile.width+5)*(i%TILES_PER_ROW);
  tile.y=5+(tile.height+5)*(Math.floor(i/TILES_PER_ROW));
  tile.gotoAndStop(NUMBER_OF_TILES/2+1);
     tile.buttonMode = true;
     tile.addEventListener(MouseEvent.CLICK,onTileClicked);
}
// end of tile placing loop

If you remember the previous example, you will see when you hover a tile there isn't anything that lets you know you can click on it. People are used to seeing a hand cursor when over some content they can click.

tile.buttonMode = true;

Setting buttonMode property to true will make the tile behave like a button, showing the hand pointer when the mouse is over it.

tile.addEventListener(MouseEvent.CLICK,onTileClicked);

A tile has to wait for the player to click on it. That's why we are using an event listener. An event is an occurrence of any type, and a listener may be described as a duty given to an entity, that patiently waits for the event to happen. Once it happens, the entity will do an assigned task.

Note

In the real world, imagine a light bulb (the entity) to have its own life. Its duty is to wait (listening) for someone to turn on the switch button. Once such an event occurs, the bulb does the assigned task: making electric current pass through its filament, heating it until it produces the light.

Back to our Concentration game, each tile waits for the mouse to click over it, and once it happens, executes the onTileClicked function. In this function, we will code everything that must happen when the player clicked on a tile.

Add this function inside the Main class but outside Main function, this way:

package {
  // importing classes
  import flash.display.Sprite;
  import flash.events.MouseEvent;
  // end of importing classes
  public class Main extends Sprite {
    public function Main(){
      ...  
    }
        private function onTileClicked(e:MouseEvent) {
          trace("you picked a "+e.currentTarget.cardType);
          e.currentTarget.gotoAndStop(e.currentTarget.cardType+1);
        }
  } 
}

First, notice function name onTileClicked is the same as the second parameter in the event listener. We also have a MouseEvent argument called e which will give us useful information about the entity that generated the event. In this case we'll use it to know which tile triggered the click event.

onTileClicked function is declared as private because it's meant to be used only by Main class.

Note

Fully explaining the difference between public and private functions is beyond the scope of this book, anyway keep in mind you will use public when you want the function to be called from classes outside the class in which it has been declared, and private when you want the function to be used only by the class in which it has been declared.

currentTarget property returns us the object that is actively processing the event. In our case, the tile the player just clicked.

Do you remember cardType property you set for each tile? You can access it through e.currentTarget.cardType. Not only do you know the hidden value that lies in the front of the card, but you can flip the card showing its content.

A simple gotoAndStop(e.currentTarget.cardType+1)method tells the tile to show the cardType+1-th frame. The +1 should remind you that the frames start from 1 while tile values start from 0.

Test the movie and try to pick various tiles. You can pick all tiles and see their content both in the game and in the debug window.

The "graphic engine" is ready. At this time, you discovered how to:

Add and manage DisplayObjects on the stage on the fly.

Handle mouse click events.

That's everything the player is supposed to do: picking tiles.

 

Checking for matching tiles


It's time to let the player know whether he picked two matching tiles or not. Let's think about Concentration like a turn-based game; at every turn the player can pick no more than two cards before knowing if he has got a matching pick.

So we are going to add the following features:

  • Don't let the player pick the same tile twice in a turn.

  • Once he picked the second tile, check if selected tiles match.

  • If they match, remove them from stage.

  • If they do not match, turn them back again.

The idea is quite simple as we'll be using an array to store picked tiles. Once the array contains two elements (two picked tiles), we'll see if tile values match.

So we need to declare a new array to be available in all main class functions and make the NUMBER_OF_TILES constant available too. We'll be using both variables in onTileClicked function, so we need to make them available throughout the entire class.

Note

If a variable is declared inside a function, it's called function level variable or local variable and it's available only inside the function. If a variable is declared in the class, then it is called class level variable or instance variable and it will be available in the whole class, all functions included.

Modify class variable declaration and variables and constants block this way:

package {
  // importing classes
  import flash.display.Sprite;
  import flash.events.MouseEvent;
  // end of importing classes
  public class Main extends Sprite {
     private var pickedTiles:Array = new Array();
     private const NUMBER_OF_TILES:uint=20;
    public function Main() {
      // variables and constants
      // no more NUMBER_OF_TILES here
      const TILES_PER_ROW:uint=5;
      var tiles:Array=new Array();
      var tile:tile_movieclip;
      // end of variables and constants
      ...
    }
  }
}

Nothing special as you can see, just remember to remove NUMBER_OF_TILES declaration from Main function or you will get an error as you can only define a variable once.

Now NUMBER_OF_TILES can be accessed throughout the whole class. As with onTileClicked function, we need to decide if we want to make it available to all classes that try to retrieve its value, or only by the class in which it has been defined (Main).

We want the latter case, so we set it to private. If we wanted the first case, we should have used public.

Now let's heavily rewrite onTileClicked function. Delete the existing one and write:

private function onTileClicked(e:MouseEvent) {
  var picked:tile_movieclip=e.currentTarget as tile_movieclip;
  trace("you picked a "+e.currentTarget.cardType);
  // checking if the current tile has already been picked
  if (pickedTiles.indexOf(picked)==-1) {
    pickedTiles.push(picked);
    picked.gotoAndStop(picked.cardType+1);
  }
  // end checking if the current tile has already been picked
  // checking if we picked 2 tiles
  if (pickedTiles.length==2) {
    if (pickedTiles[0].cardType==pickedTiles[1].cardType) {
      // tiles match!!
      trace("tiles match!!!!");
      pickedTiles[0].removeEventListener(MouseEvent.CLICK,onTileClicked);
      pickedTiles[1].removeEventListener(MouseEvent.CLICK,onTileClicked);
      removeChild(pickedTiles[0]);
      removeChild(pickedTiles[1]);
    } else {
      // tiles do not match
      trace("tiles do not match");
      pickedTiles[0].gotoAndStop(NUMBER_OF_TILES/2+1);
      pickedTiles[1].gotoAndStop(NUMBER_OF_TILES/2+1);
    }
    pickedTiles = new Array();
  }
  // end checking if we picked 2 tiles
}

First, we store the current picked tile in a variable called picked. Then we need to know if the current tile is the one the player just clicked.

if (pickedTiles.indexOf(picked)==-1) { ... }

pickedTiles is the array designed to store all picked tiles. So we need to check if the current tile (picked) is already in the array.

Remember I am using the three points (...) to indicate the block of code inside braces isn't changed or relevant at this time.

indexOf method searches for an item in an array and returns the index position of the item, or -1 if the item does not exist. So to ensure the picked tile is not the one the player just picked, we need to check if indexOf(picked) method of pickedTiles array is equal to -1.

The if statement allows execution of a block of code if a certain condition is true, and optionally can execute another block of code if the condition is false.

if (condition){
  // execute if condition is true
}
else {
  // execute if condition is false
}

Once we checked it's a new tile, we store it in pickedTiles array and show the tile's content.

We still don't know if this was the first or the second picked tile.

if (pickedTiles.length==2) {...}

This line counts the number of elements (picked tiles) in pickedTiles array thanks to length property that returns the number of elements in the array, and compares it with two.

Notice the difference between = and ==. The former assigns a value, the latter tests two expressions for equality.

If the condition is true, it means the player just picked the second tile and it's time to check if selected tiles match. If pickedTiles has two elements, the first will have index = 0 and the second index = 1, so to check if the content of picked tiles is the same, it is just necessary to add another if statement:

if (pickedTiles[0].cardType==pickedTiles[1].cardType) { ... }

that simply compares the cardType attribute of both array elements.

If they match, it's time to remove the tiles for good. Before doing it, we have to tell picked tiles not to listen anymore for mouse clicks. All in all, they are about to be removed so why make them do useless tasks?

pickedTiles[0].removeEventListener(MouseEvent.CLICK,onTileClicked);
pickedTiles[1].removeEventListener(MouseEvent.CLICK,onTileClicked);

Remove the mouse click listener. Notice it has the same syntax as addEventListener: same event, same function to be executed.

Note

Removing listeners when they are no longer needed is not just a good habit, but an imperative thing to do when working with complex scripts that could slow down the execution if a lot of listeners are waiting to be triggered.

As both click listeners have been removed, it's time to remove tiles object themselves.

removeChild(pickedTiles[0]);
removeChild(pickedTiles[1]);

removeChild() removes the DisplayObject from the Display List in the same way addChild() added it.

And the operations to do in case of success are over.

Now it's time to see what to do when selected tiles do not match.

pickedTiles[0].gotoAndStop(NUMBER_OF_TILES/2+1);
pickedTiles[1].gotoAndStop(NUMBER_OF_TILES/2+1);

That's why we had to define NUMBER_OF_TILES as a class level variable. It must be accessible from onTileClicked function.

pickedTiles = new Array();

The last thing to do, whether the tiles match or not, is to clear the pickedTiles array to let the player pick two more tiles. Constructing it again will make you have a brand new empty array.

Test the game and you won't be able to see the second tile. But if you look at trace() outputs you will see that it works. When it says tiles match, they are removed. When it says they don't match, they turn covered. So what's wrong with the second tile?

Do you remember a game is like a movie? Everything behind the stage works correctly, but there is still to work at what players will see.

Let's suppose we have a bullet time mode, the Concentration game would look like this:

The player is not able to see everything the script is doing. At frame 0 the script places the tiles, and the player is able to view the tiles. At frame i, the script uncovers a tile and the player is able to view the uncovered tile. At frame j, the script uncovers a tile, then sees picked tiles do not match, and covers them again. The player now just sees all covered tiles. Obviously it's not just the player, but what Flash shows to the screen.

 

Making the player see what happened


To get a playable game you just need to wait a second after the player picked the second tile before removing/covering them.

This can be done by adding a timer in the game. Timer class included in flash.utils package will let us use timers, and TimerEvent class included in flash.events handles timer events.

Let's start importing the classes and declaring a new variable. Change your script until Main function looks like this:

package {
  // importing classes
  import flash.display.Sprite;
  import flash.events.MouseEvent;
  import flash.events.TimerEvent;
  import flash.utils.Timer;
  // end of importing classes
  public class Main extends Sprite {
    private var pickedTiles:Array = new Array();
    private const NUMBER_OF_TILES:uint=20;
    private var pauseGame:Timer;
    public function Main() {
      ...
    }
  }
}

We just imported the two time-related classes in our package and created a new Timer variable called pauseGame. It will come into play when the player selects the second tile, so modify the block that checks if we picked two tiles this way:

// checking if we picked 2 tiles
if (pickedTiles.length==2) {
  
pauseGame=new Timer(1000,1);
  pauseGame.start();
  if (pickedTiles[0].cardType==pickedTiles[1].cardType) {
    // tiles match!!
    trace("tiles match!!!!");
    
pauseGame.addEventListener(TimerEvent.TIMER_COMPLETE,removeTiles);
  } else {
    // tiles do not match
    trace("tiles do not match");
    
pauseGame.addEventListener(TimerEvent.TIMER_COMPLETE,resetTiles);
  }
  
// no more pickedTiles = new Array();
}
// end checking if we picked 2 tiles

Once we know the player just picked the second tile, it's time to wait for one second.

pauseGame=new Timer(1000,1);

Let's initialize the timer with the constructor, which is the function that generates it. The first parameter defines the delay between timer events, in milliseconds, while the second one specifies the number of repetitions. In this case, pauseGame will wait for 1 second only once.

Again, you can use a constant, to store the number of milliseconds. I am not using it because it should be clear how to use variables and constants and I want to focus on new features.

pauseGame.start();

To make the timer start, use start() method.

When the timer reaches 1 second, it will dispatch a TimerEvent.TIMER_COMPLETE event. So we have to make pauseGame listen for such an event.

pauseGame.addEventListener(TimerEvent.TIMER_COMPLETE,removeTiles);

and

pauseGame.addEventListener(TimerEvent.TIMER_COMPLETE,resetTiles); 

Will make the program wait for the Timer object to complete its delay (one second) and then call removeTiles or resetTiles function.

These functions will just handle the removing and the resetting of tiles in the same way we did before. Add the functions inside Main class but outside Main function, just as you did with onTileClicked function:

private function removeTiles(e:TimerEvent) {
  pauseGame.removeEventListener(TimerEvent.TIMER_COMPLETE,removeTiles);
  pickedTiles[0].removeEventListener(MouseEvent.CLICK,onTileClicked);
  pickedTiles[1].removeEventListener(MouseEvent.CLICK,onTileClicked);
  removeChild(pickedTiles[0]);
  removeChild(pickedTiles[1]);
  pickedTiles = new Array();
}

As you can see the function just removes the listeners and the tiles, just as before.

private function resetTiles(e:TimerEvent) {
  pauseGame.removeEventListener(TimerEvent.TIMER_COMPLETE,resetTiles);
  pickedTiles[0].gotoAndStop(NUMBER_OF_TILES/2+1);
  pickedTiles[1].gotoAndStop(NUMBER_OF_TILES/2+1);
  pickedTiles = new Array();
}

and this one just covers the tiles again.

Notice how both functions remove TimerEvent listener and clear pickedTiles array by initializing it again. Also, such array is no longer cleared in the block where we checked if we picked 2 tiles block. Why not? Because it would clear the picked tiles array before the script knows which tiles to remove/cover, as it happens after a second.

Run the program: it works! You can see the second tile for 1 second before the script decides what to do. Your Concentration game is finished!

No, it's not.

Try to quickly pick three or four tiles. You can, because nobody told the script to ignore clicks when it's waiting the second necessary to show you the tile you just picked. So you can quickly take a look at more than two cards during a single turn. That's cheating.

We can see more than two tiles if we quickly select a bunch of them.

Believe it or not, although the game is not finished yet, you have learned everything you need to create a basic Concentration prototype. You just saw how to:

  • Execute different blocks of code according to a specific condition

  • Remove DisplayObject from the stage

  • Use timers to make the game wait

Let's make life impossible for those die hard cheaters!

 

Preventing the player from cheating


Players will always try to cheat. When making a game, don't expect people to respect any policy of playing.

We must prevent the player from continuing to pick tiles when the script is waiting to let him see the second tile he picked.

We need another instance variable, of a new type. Change class level variables and constants by coding this way:

// class level variables and constants
private var pickedTiles:Array = new Array();  
private const NUMBER_OF_TILES:uint=20;
private var pauseGame:Timer;
private var canPick:Boolean=true;
// end of class level variables and constants

Boolean variables can only have a true or false value. canPick variable will decide whether the player can pick another tile or not. Initially, it's true because the player can pick a tile when the game begins.

Now change the onTileClicked function this way:

private function onTileClicked(e:MouseEvent) {
  if(canPick){
    var picked:tile_movieclip=e.currentTarget as tile_movieclip;
    trace("you picked a "+e.currentTarget.cardType);
    // checking if the current tile has already been picked
    if (pickedTiles.indexOf(picked)==-1) {
      pickedTiles.push(picked);
      picked.gotoAndStop(picked.cardType+1);
    }
    // end checking if the current tile has already been picked
    // checking if we picked 2 tiles
    if (pickedTiles.length==2) {
      
  canPick=false;
      pauseGame=new Timer(1000,1);
      pauseGame.start();
      if (pickedTiles[0].cardType==pickedTiles[1].cardType) {
        // tiles match!!
        trace("tiles match!!!!");
      pauseGame.addEventListener(TimerEvent.TIMER_COMPLETE,removeTiles);
      } else {
        // tiles do not match
        trace("tiles do not match");
        pauseGame.addEventListener(TimerEvent.TIMER_COMPLETE,resetTiles);
      }
      // no more pickedTiles = new Array();
    }
    // end checking if we picked 2 tiles
  
}
}

The entire function is executed only if the player can pick a tile. And that's right. When the player picked the second tile, simply set canPick value to false and you're done. The player cannot pick anymore.

The last thing to complete the game is letting the player be able to pick tiles again once the game has covered/removed the tiles.

Change removeTiles function this way:

private function removeTiles(e:TimerEvent) {
pauseGame.removeEventListener(TimerEvent.TIMER_COMPLETE,removeTiles);
pickedTiles[0].removeEventListener(MouseEvent.CLICK,onTileClicked);
pickedTiles[1].removeEventListener(MouseEvent.CLICK,onTileClicked);
  removeChild(pickedTiles[0]);
  removeChild(pickedTiles[1]);
  pickedTiles = new Array();
  canPick = true;
}

And do the same with resetTiles function:

private function resetTiles(e:TimerEvent) {
  pauseGame.removeEventListener(TimerEvent.TIMER_COMPLETE,resetTiles);
  pickedTiles[0].gotoAndStop(NUMBER_OF_TILES/2+1);
  pickedTiles[1].gotoAndStop(NUMBER_OF_TILES/2+1);
  pickedTiles = new Array();
  canPick = true;
}

Simply set canPick value to false and again enable the player to pick tiles.

Test the movie. No more cheating!

Now we could just sit and play, but we want more.

 

Fine-tuning the game: adding educational content


At the beginning of this chapter I said this was going to be an educational game. It's time to fine-tune the game and add educational content.

Note

Polishing your game is a critical process, as it makes the difference between a great game and "just another game". Once you have a playable prototype like our Concentration game, it's time to fuel up your creativity and try to distinguish it from the masses.

What if there were no more duplicate tiles with the same shape but, for instance, a tile with a green circle and a tile with a "Green Circle" text? Children would need to remember both tiles' positions and their meaning.

How can we add this feature without rewriting too much code? In two simple steps:

  1. Create 20 distinct tiles with values from 0 to 19.

  2. Let the script know matching tiles are 0 and 1, 2 and 3, 4 and 5, and so on.

This is the final code, stripped of all comments and trace() outputs. There isn't any new concept, so you should be able to understand what it does by yourself.

Main function:

public function Main() {
  const TILES_PER_ROW:uint=5;
  var tiles:Array=new Array();
  var tile:tile_movieclip;
  for (var i:uint=0; i<NUMBER_OF_TILES; i++) {
    tiles.push(i);
  }
  var swap,tmp:uint;
  for (i=NUMBER_OF_TILES-1; i>0; i--) {
    swap=Math.floor(Math.random()*i);
    tmp=tiles[i];
    tiles[i]=tiles[swap];
    tiles[swap]=tmp;
  }
  for (i=0; i<NUMBER_OF_TILES; i++) {
    tile=new tile_movieclip();
    addChild(tile);
    tile.cardType=tiles[i];
    tile.x=5+(tile.width+5)*(i%TILES_PER_ROW);
    tile.y=5+(tile.height+5)*(Math.floor(i/TILES_PER_ROW));
    tile.gotoAndStop(NUMBER_OF_TILES+1);
    tile.buttonMode=true;
    tile.addEventListener(MouseEvent.CLICK,onTileClicked);
  }
}

This is onTileClicked function

private function onTileClicked(e:MouseEvent) {
  if(canPick){
    var picked:tile_movieclip=e.currentTarget as tile_movieclip;
    if (pickedTiles.indexOf(picked)==-1) {
      pickedTiles.push(picked);
      picked.gotoAndStop(picked.cardType+1);
    }
    if (pickedTiles.length==2) {
      canPick=false;
      pauseGame=new Timer(1000,1);
      pauseGame.start();
      if (Math.floor(pickedTiles[0].cardType/2)== Math.floor(pickedTiles[1].cardType/2)) { 
      pauseGame.addEventListener(TimerEvent.TIMER_COMPLETE,removeTiles);
      } else {
      pauseGame.addEventListener(TimerEvent.TIMER_COMPLETE,resetTiles);
      }
    }
  }
}

and this is resetTiles function

private function resetTiles(e:TimerEvent) {
  pauseGame.removeEventListener(TimerEvent.TIMER_COMPLETE,resetTiles);
  pickedTiles[0].gotoAndStop(NUMBER_OF_TILES+1);
  pickedTiles[1].gotoAndStop(NUMBER_OF_TILES+1);
  pickedTiles = new Array();
  canPick = true;
}

The other functions and declarations remain unchanged.

And this is an example of a matching pair:

Purple square picture is tile 18 and "purple square" text is tile 19. They match.

Your Concentration game is now complete and ready to be played.

 

Summary


Concentration, while being an easy game to make and play, opened the path to the world of programming games. Now you are able to set up a Flash project to make a game, work with DisplayObjects, interact with basic data types such as variables and arrays and manage mouse and timer listeners.

 

Where to go now


Test your AS3 skills adding new features to the game. I am giving you two suggestions:

  1. Detect when the player completed the game. You can easily do it by creating a new instance variable that counts the successful matches and checks if they are equal to the total number of tiles / 2.

  2. Count how many tries the player is making using another instance variable that you'll increment every time the player picks the second tile. Print these values in the Output window.

About the Author

  • Emanuele Feronato

    Emanuele Feronato has been studying programming languages since the early 1980s, with a particular interest in game development. He has taught online programming for European Social Fund (ESF), and then founded a web development company in Italy.

    As a game developer, Emanuele developed Flash games sponsored by the biggest game portals and his games have been played more than 90 million times. He now ports most of them on mobile platforms and develops HTML5 games, which have been featured in the most important mobile web markets, such as Amazon.

    As a writer, he has worked as a technical reviewer for Packt Publishing and published the books Flash Game Development by Example and Box2D for Flash Games.

    His blog, www.emanueleferonato.com, is one of the most visited blogs about indie game development.

    Browse publications by this author

Latest Reviews

(1 reviews total)
This book is a great help and helped me in many aspects.
Book Title
Access this book, plus 7,500 other titles for FREE
Access now