Project 3. Flipr

I’ll warn you now—this project will be a difficult one and will test your knowledge of ActionScript to the limit! Since the last project, you have learned how to take your projects outside the browser and bring them to the desktop and to mobile devices.

The structure of this project is like the others—you’ll review the project specs, design and kick-off meeting notes, then figure out how to complete the project on your own.

This project adds an unfortunate circumstance—your design comps are flattened files, and there are no assets you can import, meaning you have to re-create them in Flash Pro. D’oh! Although this is a situation that a good team should try to avoid at all costs, it happens, and knowing how to still meet the project requirements is critical.

Up for the challenge? I thought so.

Project Specification: Flipr

Flipr is a casual game for the Android platform and was designed by an external design agency.

The game is based around a grid of 16 tiles. Each tile changes color when tapped with a finger. To complete a level of the game, the user taps the tiles to display different colors until the pattern of colors matches a randomly generated pattern, taking the user to the next level. The user has 120 seconds to complete as many puzzles as possible.

The difficulty level increases as the number of colors in the patterns increase:

• Level 1: 2 colors

• Level 2 and 3: 3 colors

• Level 4 and 5: 4 colors

• Level 6 and 7: 5 colors

• Levels 8 and beyond: 6 colors

When a user starts the game, he has the option of showing the game instructions, and then starting the game, or quitting the application.

When the game timer expires, the user sees a “Game Over” screen and can either play again or quit the app.

Visual Design Review: Flipr

A visual design agency created sample mockups of the screens. Unfortunately, they are compressed JPEGs, so you can use them only as a visual reference.

The first, a splash screen (Figure P3.1), should appear for a brief period when the user starts the app on their device.

Figure P3.1. Flipr title screen

image

After the delay, the user will see the main menu for the game (Figure P3.2), which gives them three options: play the game, see instructions, or quit the app.

Figure P3.2. Controls for the main menu of the Flipr game

image

When the user taps “Instructions,” he will see the game’s instructions displayed on the screen (Figure P3.3).

Figure P3.3. Instructions screen

image

During your review of the Instructions screen, you noticed that the puzzle completion time was wrong, and there was also no way to return to the main controls screen. You note that you’ll need to address this design issue in the final app.

Back on the main controls screen, when the user taps “Start Game,” the game starts with a few elements in it (Figure P3.4).

Figure P3.4. The main game screen

image

First, you see that the game level is in the background of the window and the tile grid is in the center of the screen. At the bottom are progress bar “themometers” showing the percentage match of the secret puzzle code, and the amount of time left over.

You immediately notice that all the tiles in the comp are yellow; variations on the tile colors which display when the player taps a tile (based on the project specification) are not available, so you note that you’ll have to design those yourself. In addition, the “Game Over” screen design is missing, so—sigh—you will need to make that too.

Grr!

Time to meet with the team and kick things off.

Kick-Off Meeting Notes: Flipr

After documenting the lack of designs, you notify the team lead that this project will take longer than expected because of the extra work.

The logic of the game seems to make sense, but determining how to save the pattern and the pattern of the current tiles will need to be managed in an array of some sort.

Since the client is looking only for an Android app, you can save some time in the development ; no need to test iOS-based devices.

Another team member noted that when previewing a previous mobile project, the preview mode used in Flash Professional detects mouse events. A mobile project works based on touch events, so the preview mode can’t decipher between mouse events and touch events. He noted that you can get around this by creating a duplicate event listener for MouseEvent.CLICK and send it to the same event callback function as the TouchEvent.TOUCH_TAP event type. The trick is to change the event object type in the callback function definition from TouchEvent to simply Event, since both of these are descendants of the generic Event class.

There was a brief discussion on how to manage the different levels of your application using event objects. Someone mentioned that at times, the object that is broadcasting a user event is at a deeper scope than expected. To resolve this, use the parent property after an instance name to change the scope to the object’s container. For example, if an object called myObject is within another object called myContainer, the statement myObject.parent would refer to the myContainer object, thus giving it access to myContainer properties and methods.

Another tidbit was that you can use a property called currentCount to track the number of intervals processed of a running timer.

Finally, one team member indicated that on a previous project, she didn’t realize that they could add custom properties to classes that extend MovieClip or Sprite. She suggested that it would be very helpful to provide each instance of a MovieClip or Sprite with custom information that could be used later in the project.

A challenging product—but that’s why you are here, to solve challenging problems and make cool stuff.

So, give this a try.

Solution and Walkthrough: Flipr

Wow...quite a challenge, wasn’t it? In my personal experience, I have encountered projects like this far more often than I would like, but it is a fact of the business of interactive design, and we are all professionals, so pushing through and making it happen is what sets us apart.

Here is one way to solve this challenge. Because there are so many open-ended ways to approach this project, this is just an example—but it may introduce some solutions that you hadn’t considered. The following walkthrough is how I approached the project.

Review of the Flash Professional Project

Since you had to start this project from scratch, you needed to redesign the user interface. In this solution, I created parts of the design in Fireworks and imported them into Flash Professional, while others were drawn directly into Flash Professional and converted into Sprites or MovieClips (Figure P3.5).

Figure P3.5. The splash screen

image

The main project contains a timeline organized into folders for the objects that are used in the game. Each game screen is denoted as a section on the timeline with frame labels to allow you to navigate from screen to screen using ActionScript.

The Library is organized into folders, with most objects saved as Sprites, since they don’t need timelines.

The splash screen displays the game logo in Bodini font, which is also embedded in the project to ensure that it works correctly.

The controls screen (Figure P3.6) contains the same game logo (called the masthead) in the Library, and three buttons, each with a unique design. These have the instance names of startGameButton, instructionsButton, and endGameButton.

Figure P3.6. The controls screen

image

This screen displays static text for the instructions (Figure P3.7). Since there was no button to allow the user to return back to the main menu in the original design, it has been added with the instance name goBackButton.

Figure P3.7. The instructions screen

image

The game screen is where most of the action takes place (Figure P3.8). Here is a single object called gameBoard, which has the gameLevelText text field, a simple drawing object for the game background, and two MovieClips.

Figure P3.8. The main game screen

image

The first MovieClip, perComplete, shows the percentage complete, the other MovieClip is the countdown timer, gameTimer.

Each MovieClip has a timeline that animates the thermometer portion of the control (Figure P3.9).

Figure P3.9. Construction of the percentage complete movie clip

image

Finally, the game over screen (Figure P3.10), for which there wasn’t a design, contains some static text, and two buttons. The first button, startGameButton, restarts the game; the second button, endGameButton, quits the app.

Figure P3.10. The missing game over screen

image

All the objects that are displayed on the Stage have been set as either Export as Bitmap or Cache as Bitmap, located in the Properties panel when the object is selected to improve on-device performance.

That’s the basics, let’s get to the code!

Review of the Document Class

In my project, the code for the project is broken into five classes. Let’s start the walkthrough by going through the flow of the app.

Here is the contents of the Document class:

package
{
    import flash.display.MovieClip;
    import flash.utils.Timer;
    import flash.events.TimerEvent;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    import flash.display.Sprite;
    import flash.events.TouchEvent;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.desktop.NativeApplication;
    public class Flipr extends MovieClip
    {
        public var splashTimer:Timer;
        public function Flipr()
        {
            stop();
            _init();
        }
        private function _init():void
        {
           Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
           _displaySplash();
        }
    /************************************************************
        **SPLASH SCREEN
        */
        private function _displaySplash():void
        {
            splashTimer = new Timer(500);
            splashTimer.addEventListener(TimerEvent.TIMER, _endSplash);
            splashTimer.start();
        }
        private function _endSplash(e:TimerEvent):void
        {

            splashTimer.stop();
            splashTimer.removeEventListener(TimerEvent.TIMER, _endSplash);
            gotoAndStop("controls");
            _setupControls();
        }
        /***********************************************************
        **MAIN CONTROLS
        */
        private function _setupControls():void
        {
            instructionsButton.addEventListener (TouchEvent.TOUCH_TAP, _displayInstructions);
    startGameButton.addEventListener(TouchEvent.TOUCH_TAP, _startGame);
            endGameButton.addEventListener(TouchEvent.TOUCH_TAP, _endGame);
            instructionsButton.addEventListener(MouseEvent.CLICK, _displayInstructions);
            startGameButton.addEventListener(MouseEvent.CLICK, _startGame);
            endGameButton.addEventListener(MouseEvent.CLICK, _quit);
        }
    /***************************************************************
        **INSTRUCTIONS
        */
        private function _displayInstructions(e:Event):void
        {
            gotoAndStop("instructions");
            goBackButton.addEventListener(MouseEvent.CLICK, _removeInstructions);
        }
        private function _removeInstructions(e:Event):void
        {
            gotoAndStop("controls");
            _setupControls();
        }
    /***************************************************************
        **GAME
        */
        private function _startGame(e:Event):void
        {
            gotoAndStop("game");
            gameBoard.newGame();
            gameBoard.addEventListener("gameOver", _endGame);
        }
    /***************************************************************
        **END GAME
        */
        private function _endGame(e:Event):void
        {
            gotoAndStop("gameOver");
            startGameButton.addEventListener(TouchEvent.TOUCH_TAP, _startGame);
            endGameButton.addEventListener(TouchEvent.TOUCH_TAP, _endGame);

            startGameButton.addEventListener(MouseEvent.CLICK, _startGame);
            endGameButton.addEventListener(MouseEvent.CLICK, _quit);
        }
    /***************************************************************
        ** QUIT
        */
        private function _quit(e:Event):void
        {
            NativeApplication.nativeApplication.exit();
        }
    }
}

You start with all the imports for the project—there are a lot. This means you are learning a lot, so pat yourself on the back.

Display the Splash Screen

The first element created in the class is the timer used to move from the splash screen to the main controls screen. Pretty simple opening; let’s move on to the constructor:

public function Flipr()
{
    stop();
    _init();
}

Not much there. First, you need to stop the playback of the main timeline and then start an initialization method.

private function _init():void
{
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    _displaySplash();
}

Here, the input mode for the Multitouch gestures are defined, using the TOUCH_POINT method over GESTURE, since this game doesn’t require any multi-point gestures to work.

There is then a method call to display the splash screen:

private function _displaySplash():void
{
    splashTimer = new Timer(500);
    splashTimer.addEventListener(TimerEvent.TIMER, _endSplash);
    splashTimer.start();
}

Here you define the timer, which displays for a half-second since the splashTimer was set with a 500 millisecond duration, and then adds the event listener for the timer. The timer is then immediately started.

Display the Main Controls

When the timer runs out, it executes the _endSplash callback method:

private function _endSplash(e:TimerEvent):void
{
    splashTimer.stop();
    splashTimer.removeEventListener(TimerEvent.TIMER, _endSplash);
    gotoAndStop("controls");
    _setupControls();
}

Here, the timer is stopped, and the event listener is removed. I always try to remove listeners when they aren’t needed anymore as a best practice. I find that when you start creating much larger projects, having “unremoved” listeners can impact performance. The playhead is moved to the “controls” frame label, and the event listeners for the various buttons are added using the _setupControls method:

private function _setupControls():void
{
    instructionsButton.addEventListener(TouchEvent.TOUCH_TAP, _displayInstructions);
    startGameButton.addEventListener(TouchEvent.TOUCH_TAP, _startGame);
    endGameButton.addEventListener(TouchEvent.TOUCH_TAP, _endGame);
    instructionsButton.addEventListener(MouseEvent.CLICK, _displayInstructions);
    startGameButton.addEventListener(MouseEvent.CLICK, _startGame);
    endGameButton.addEventListener(MouseEvent.CLICK, _quit);
}

Here, the event listeners for the three buttons (instructions, start, stop) are created. Based on the recommendations from the kick-off meeting, there are two sets. This is to allow testing in preview mode with the mouse, as well as on-device tapping with touch gestures. The first is using TouchEvent.TOUCH_TAP. Then a matching set using MouseEvent.CLICK with identical callback functions is used to register the mouse event when previewing the product from Flash Professional and not a physical device.

The instructionsButton, when clicked or tapped, executes the _displayInstructions method:

private function _displayInstructions(e:Event):void
{
    gotoAndStop("instructions");
    goBackButton.addEventListener(MouseEvent.CLICK, _removeInstructions);
}

From there, the playhead is moved to the label “instructions” and the event listener is created for the goBackButton object. When clicked, the instructions need to go away and return to the main controls, this is done with the function _removeInstructions:

private function _removeInstructions(e:Event):void
{
    gotoAndStop("controls");
    _setupControls();
}

This sets the playhead back to “controls” and then reruns _setupControls. To display the buttons and make sure that they are set up correctly.

From the main controls screen, if the endGameButton is pressed, the _quit method runs:

private function _quit(e:Event):void
{
    NativeApplication.nativeApplication.exit();
}

Now you tell the native application to exit and return to the launcher.

If the user clicks the startGameButton, you get things going by running the _startGame method:

private function _startGame(e:Event):void
{
    gotoAndStop("game");
    gameBoard.newGame();
    gameBoard.addEventListener("gameOver", _endGame);
}

Create the Game Logic

This is deceptively simple, because all the game functionality has been passed to the GameBoard class. But first, you move the playhead to the “game” frame label, and then tell gameBoard to execute a public method named newGame. You also listen to a custom gameOver event that will be broadcasted from gameBoard, which then runs the _endGame method.

Let’s look at the code in the GameBoard class that houses the logic and interaction for the game.

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.events.TouchEvent;
    import flash.utils.Timer;
    import flash.events.TimerEvent;

    public class GameBoard extends Sprite
    {
        // Create arrays for tile objects, values of tiles,
        // and the puzzle solution
        // Array of Tile MovieClips
        var tileArray:Array = new Array();
        // Array of the current color shown
        var tileValueArray:Array = new Array();
        // Array of the solution pattern
        var tilePuzzleArray:Array = new Array();
        // General game variables
        // Current level of the game
        var currentLevel:uint;
        // Total correct tiles matching pattern
        var totalRight:uint;
        // Number of tile varieties available in the level
        var tileVariety:uint;
        // Create game timer
        // Game timer
        var gameClock:Timer;
        public function GameBoard()
        {
            _init();
        }
        // Initialize game
        private function _init():void
        {
            // Create the tile grid
            for (var i:uint = 0; i < 16; i++)
            {
                var newTile:Tile = new Tile();
                newTile.cacheAsBitmap = true;
                newTile.x = (i % 4) * 110 + 10;
                newTile.y = Math.floor(i/4) * 110 + 10;
                newTile.idNo = i;
                newTile.addEventListener(MouseEvent.CLICK, _clickTile);
                newTile.addEventListener(TouchEvent.TOUCH_TAP, _clickTile);
                addChild(newTile);
                tileArray.push(newTile);
                tileValueArray.push(0);
            }
            // General score display updater
            addEventListener(Event.ENTER_FRAME, _updateDisplay);
            // Configure the game clock and listeners
            gameClock = new Timer(1000,120);
            gameClock.addEventListener(TimerEvent.TIMER, _timerSecond);
            gameClock.addEventListener(TimerEvent.TIMER_COMPLETE, _gameOver);
        }
        // Event callback for timer interval
        private function _timerSecond(e:TimerEvent):void
        {
            gameTimer.setTimer(gameClock.currentCount);
        }
        // Event callback for timer completion, or game over
        private function _gameOver(e:TimerEvent):void
        {
            // Dispatch a custom event
            this.dispatchEvent(new Event("gameOver"));
        }
        // Event callback for clicking on a tile
        private function _clickTile(e:Event):void
        {
            // Access the event object's target's parent,
            // or the MovieClip they clicked on
            var clickedTile:uint = e.target.parent.idNo;
            // Increment the tile value
            tileValueArray[clickedTile]++;
            // Determine if tile value is outside of the variety
            // range and adjust if so
            if (tileValueArray[clickedTile] >= tileVariety)
            {
                tileValueArray[clickedTile] = 0;
            }
            // Update the display of the clicked tile
            tileArray[clickedTile].displayTile (tileValueArray[clickedTile]);
            // Rescore the game
            _scoreGame();
        }
        // Public function called from the main application to start
        // the game
        public function newGame():void
        {
            currentLevel = 1;
            _setupGame();
        }
        // Private function that sets up a game from within the
        // class, reused when reaching new levels
        private function _setupGame():void
        {
            // Reset the amount correct
            totalRight = 0;
            // Clear the puzzle solution array
            tilePuzzleArray = [];
            // Determine the tile variety count based on the current
            // level
            if (currentLevel < 2)
            {
                tileVariety = 2;
            }
            else if (currentLevel < 4 )
            {
                tileVariety = 3;
            }
            else if (currentLevel < 6 )
            {
                tileVariety = 4;
            }
            else if (currentLevel < 8 )
            {
                tileVariety = 5;
            }
            else
            {
                tileVariety = 6;
            }
            // Create the puzzle solution
            for (var i:uint = 0; i < 16; i++)
            {
                var newValue:uint = Math.random() * tileVariety;
                tilePuzzleArray.push(newValue);
            }
            // Create the initial random set of tiles
            for (var i:uint = 0; i < 16; i++)
            {
                var setRandomTile:uint = Math.random() * tileVariety;
                tileArray[i].displayTile(setRandomTile);
                tileValueArray[i] = setRandomTile;
            }
            // Score the game with the initial set of values
            _scoreGame();
            // Start the game clock
            gameClock.start();
        }
        // Score the game based on the puzzle solution and currently
        // displayed tiles
        private function _scoreGame():void
        {
            // Reset the score
            totalRight = 0;
            // Process through all the tiles and score
            for (var i:uint = 0; i < 16; i++)
            {
                if (tilePuzzleArray[i] == tileValueArray[i])
                {
                    totalRight++;
                }
            }
            // Test if game is won, if so, increase level and
            // restart
            if (totalRight == 16)
            {
                gameClock.stop();
                currentLevel++;
                _setupGame();
            } 
        }
        // Event callback for entering frame to update the score and
        // timer indicators
        private function _updateDisplay(e:Event):void
        {
            perComplete.setComplete(totalRight);
            gameLevelText.text = String(currentLevel);
        }
    }
}

This code is the meat of the project. The Document class is moving the user around the various screens. Let’s start with variables that are in the class:

// Create arrays for tile objects, values of tiles, and the puzzle
// solution
// Array of Tile MovieClips
var tileArray:Array = new Array();
// Array of the current color shown
var tileValueArray:Array = new Array();
// Array of the solution pattern
var tilePuzzleArray:Array = new Array();
// General game variables
// Current level of the game
var currentLevel:uint;
// Total correct tiles matching pattern
var totalRight:uint;
// Number of tile varieties available in the level
var tileVariety:uint;
// Create game timer
// Game timer
var gameClock:Timer;

Because there is so much going on here, heavy use of comments is recommended; otherwise, you will easily forget what is going on.

First there are two arrays that are created. The first is an array of MovieClips that will be created to build the game board. The second are the values of the tiles that are displayed on the game board so you know what colors are currently being displayed. The last array will hold the secret puzzle code that the user needs to match to advance to the next level.

The next set of variables holds the current level of the game, the total number of tiles that match the secret pattern, and a variable that will tell the game engine how many color variations there are in the current level that will be calculated based on the rules of the technical specification.

Finally, a timer for the game clock is created.

The constructor executes an initialization method that starts to set up the user interface of the game, the game board, the rules of the game, and the interface controls:

// Initialize game
private function _init():void
{
    // Create the tile grid
    for (var i:uint = 0; i < 16; i++)
    {
        var newTile:Tile = new Tile();
        newTile.cacheAsBitmap = true;
        newTile.x = (i % 4) * 110 + 10;
        newTile.y = Math.floor(i/4) * 110 + 10;
        newTile.idNo = i;
        newTile.addEventListener(MouseEvent.CLICK, _clickTile);
        newTile.addEventListener(TouchEvent.TOUCH_TAP, _clickTile);
        addChild(newTile);
        tileArray.push(newTile);
        tileValueArray.push(0);
    }
    // General score display updater
    addEventListener(Event.ENTER_FRAME, _updateDisplay);
    // Configure the game clock and listeners
    gameClock = new Timer(1000,120);
    gameClock.addEventListener(TimerEvent.TIMER, _timerSecond);
    gameClock.addEventListener(TimerEvent.TIMER_COMPLETE, _gameOver);
}

Create the Game Board

I like to create layout mathematically if possible. I find that it leads to more adaptable layouts that can be adjusted easily with a couple tweaks rather than forcing me to go into the FLA and adjust the layout visually. The tile grid is created using a for loop that creates a new instance of the Tile class, sets the cacheAsBitmap property to true, and positions it on the screen.

The math for this is a little tricky. The x property is calculated based on a modulo operation. Since the grid is four by four, you can use that to your advantage to build the game board. For reference, look at Figure P3.11.

Figure P3.11. The game board showing index numbers and modulo calculations

image

If the index number for each item was applied with a modulo operation using 4 (the number of items in a row), you can get a number you can then multiply with the width of the tile, offset based on the inset margin of the container.

For example, tile number 12, when using modulo with the value 4, returns 0 since 12 is evenly divisible by 4. 0 multiplied by the width of the tile (my design used a width of 110 pixels) becomes 0, and then you add a padding of 10 to center in the grid. Another example is tile number 10. In this case the module operation returns 2, which when multiplied by the width gives 220, plus the padding, gives an x coordinate of 230.

Pretty cool, huh? Let’s look at y next.

For the y property, you need to do something a bit different. By using simple division, you can determine if a number, which divided by 4 and removing the decimal, sits on a specific row in the grid (Figure P3.12).

Figure P3.12. The game board showing index numbers and results to calculate y

image

Let’s take tile 1 as an example first. If you take the index number, divide by 4, and drop the decimal, you end up with 0. Which, you guessed it, can be multiplied with the height of the object with a padding offset. Another example is tile 14—when divided by 4 and the decimal dropped results in 3, which can then be positioned using the same calculation.

This example is obviously putting your math and layout skills to the test. An alternative here could be to position the objects on the screen and then add them to an array, which is similar to the method used in the DiceOut project, but this method offers a lot more flexibility—what if you needed to have a five by five grid instead? With just the modification of a few values, that can easily be arranged.

With the positioning finished, the tile is given a custom property called idNo. This is used to store which tile will need to be accessed when you click or tap it and which tile it is in the sequence.

User events are then listened to in the next line: one for click and the other for tap.

The tile is added to the display stack and then pushed to the array so you can loop through them easily later in the project. An initial value of 0 is assigned to the value array, just for good measure.

Set Up the Score and Timer Displays

Next, you create an event listener that will fire with each frame. This is used to update the display of the game timer and percentage complete. Immediately following that, the game clock is configured to run for 120 seconds with a 1 second interval. Matching functions for the TIMER and TIMER_COMPLETE events are created. The first will execute _timerSecond:

private function _timerSecond(e:TimerEvent):void
{
    gameTimer.setTimer(gameClock.currentCount);
}

Here, the gameTimer MovieClip is then accessed, running a public setTimer method, sending the current interval count of the timer to the object. The game timer is another class called GameTimer:

package
{
    import flash.display.MovieClip;
    public class GameTimer extends MovieClip
    {
        public function GameTimer()
        {
            stop();
            resetTimer();
        }
        public function resetTimer():void
        {
            gotoAndStop(1);
        }
        public function setTimer(newTime:uint):void
        {
            var timeLeft:uint = 120 - newTime;
            timeLeftText.text = timeLeft + "s";
            gotoAndStop(newTime);
        }
    }
}

This class has a resetTimer method that sets the playhead to the first frame. setTimer takes the interval count provided and calculates the frame that the playhead should be positioned at, and then updates the text field with the total number of seconds left. Since the interval count goes up with each interval and you are displaying the timer as counting down, the number needs to be modified to create the countdown effect. For example, when the timer starts, it has an interval count of 0, but the display shows 120 seconds remain.

private function _gameOver(e:TimerEvent):void
{
    // Dispatch a custom event
    this.dispatchEvent(new Event("gameOver"));
}

When the timer is complete, the _gameOver method runs, which dispatches the custom event “gameOver” that the main Document class captures and sends the user to the game over screen.

Set Up the Player Controls and Game Rules

Whew! Now everything is staged, but the game itself needs to be created. If you remember, this is done in the public newGame method that is called from the Document class. You are going to skip the _clickTile method for now—but you’ll get to it soon.

// Public function called from the main application to start the
// game
public function newGame():void
{
    currentLevel = 1;
    _setupGame();
}
// Private function that sets up a game from within the class,
// reused when reaching new levels
private function _setupGame():void
{
    // Reset the amount correct
    totalRight = 0;
    // Clear the puzzle solution array
    tilePuzzleArray = [];
    // Determine the tile variety count based on the current level
    if (currentLevel < 2)
    {
        tileVariety = 2;
    }
    else if (currentLevel < 4 )
    {
        tileVariety = 3;
    }
    else if (currentLevel < 6 )
    {
        tileVariety = 4;
    }
    else if (currentLevel < 8 )
    {
        tileVariety = 5;
    }
    else
    {
        tileVariety = 6;
    }
    // Create the puzzle solution
    for (var i:uint = 0; i < 16; i++)
    {
    var newValue:uint = Math.random() * tileVariety;
        tilePuzzleArray.push(newValue);
    }
    // Create the initial random set of tiles
    for (var i:uint = 0; i < 16; i++)
    {
        var setRandomTile:uint = Math.random() * tileVariety;
        tileArray[i].displayTile(setRandomTile);
        tileValueArray[i] = setRandomTile;
    }
    // Score the game with the initial set of values
    _scoreGame();
    // Start the game clock
    gameClock.start();
}

The public newGame method sets the currentLevel variable to 1, and then runs the private _setupGame method. Inside of _setupGame, two variables are reset: totalRight and tilePizzleArray, which contains the puzzle solution.

The rules of the specification are used to calculate the number of the possible tile varieties for the tileVariety variable.

A for loop is used to create the puzzle solution. The value of the tile is determined using a random number multiplied by the tileVariety variable, providing additional variety options as the game level increases. This value is then pushed to the tilePuzzleArray.

To randomize the board, the same calculation is used to set the display of the tiles on the board and to then provide the tileValueArray with the matching number that can be used to determine the puzzle is complete.

Each tile is an instance of the Tile class that contains a little bit of functionality:

package
{
    import flash.display.MovieClip;
    public class Tile extends MovieClip
    {
        public var idNo:uint;
        public function Tile()
        {
           _init();
        }
        private function _init():void
        {
            stop();
        }
        public function displayTile(tileNo:uint):void
        {
            gotoAndStop(tileNo+1);
        }
    }
}

The Tile MovieClip displays a different colored tile on each frame. The displayTile method is used to take the value number created to show which tile color is displayed, which starts with 0, and convert that to an appropriate value for the frame numbers, which start at 1.

With the puzzle solution set and the initial tiles configured, the game is initially scored based on the random starting configuration using the _scoreGame method:

private function _scoreGame():void
{

    // Reset the score
    totalRight = 0;
    // Process through all the tiles and score
    for (var i:uint = 0; i < 16; i++)
    {
        if (tilePuzzleArray[i] == tileValueArray[i])
        {
            totalRight++;
        }
    }
    // Test if game is won, if so, increase level and restart
    if (totalRight == 16)
    {

        gameClock.stop();
        currentLevel++;
       _setupGame();
    }
}

Here the totalRight variable is reset, and you loop through the various arrays. First, you ask if the element of the puzzle solution is equal to that of the tile’s displayed value. If it is, the totalRight variable is incremented by 1.

After the array loop finishes you ask if the total right is equal to 16, meaning that all the tiles match the secret pattern. If so, gameClock is stopped, the current level is incremented 1, and you start all over again with _setupGame.

What happens when you click or tap a tile? This is the method you skipped earlier—it is named _clickTile:

private function _clickTile(e:Event):void
{
    var clickedTile:uint = e.target.parent.idNo;
    // Access the event object's target's parent,
    // or the MovieClip they clicked on
    // Increment the tile value
    tileValueArray[clickedTile]++;
    // Determine if tile value is outside of the variety range and
    // adjust if so
    if (tileValueArray[clickedTile] >= tileVariety)
    {
        tileValueArray[clickedTile] = 0;
    }
    // Update the display of the clicked tile
    tileArray[clickedTile].displayTile(tileValueArray[clickedTile]);
    // Rescore the game
     _scoreGame();
}

You create a variable within the method that stores the value of the idNo of the item that was clicked. When using the event callback object, the target in this example is not the tile itself, but the sprite that displays the various colored variations. By using the parent property, you can access the right object and get the idNo value.

When you tap the tile, you want to bump up the tile’s color to the next color value, which is done by incrementing the tile value array using the clickedTile property to select the right element. To make sure that the color of the tile doesn’t fall outside the variety of colors determined for the current level, an if statement is used to test the current value against the tile variety count. If it exceeds it, it is reset back to 0.

Finally, the tile itself is adjusted to display the right color, and the game is rescored (Figure P3.13).

Figure P3.13. The fruits of your labor

image

Wrapping Up

Amazing to think that this game is using only the ActionScript that you have learned in this book. While there are some intermediate concepts or approaches used in this example, it illustrates how far you can take ActionScript just based on the skills you have learned so far.

But this is just the start of your adventure with ActionScript. I encourage you to dive deeper and create amazing web sites, desktop applications, or mobile apps using what you have learned in this book.

Oh, and if you ever meet me in person, say hello, or contact me on Twitter under the handle @sfdesigner and show me what you have been able to create with ActionScript. I’m always amazed by the creative projects that people build using ActionScript.

Congratulations! You have a lot to be proud of.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.96.232