10 Aug 2011

Chapter 2: Structure

Hi. 

This is the second chapter of the tutorial. If you missed the first or you want to start here, head back to the first chapter and grab the files.

In this chapter you will learn about:

  • Application architecture
  • Return types.
  • Knowing when the player uses the mouse.
  • Custom events. 
  • Custom functions.

Ok, so we managed to make a paddle that is controlled by the player.  That's great, but before adding an opponent and the ball, we should start giving our game a good structure. 

This is one of the first things you should do when starting a new project. Don't think of it as a game, think of it as a big mechanism with a lot of parts, each dealing with particular stuff and taking care of specific processes. 

So... currently, our project looks like this:

Main
  |
  |____ Paddle


So... just after starting the game, a paddle appears on the stage. Doesn't sound good, right? It's like going to see a game and suddently being in the middle of the field. No, you have to go to the stadium, go to your seat and then watch the players entering the field. 


Let's give our project a new structure:


Main
 |
 |____ Main Menu
                   |
                   |______ Pong Game
                                          |
                                          |_____ Paddle


We'll start by making a new class called 'MainMenu' which will extend the base class Sprite.  Add a listener for the ADDED_TO_STAGE event and make an empty function for it.


By now you should be able to do this easily. If you still have problems with that, I'd suggest going back and practicing the first chapter again. 


Anyway, we'll make that new class and start building a menu.
Type this as your MainMenu.as and save it:

package {
        import flash.events.Event;
        import flash.display.Sprite;
        import flash.text.TextField;
        import flash.events.MouseEvent;
    

public class MainMenu extends Sprite{
    private var pongButton:Sprite;
 
 
public function MainMenu():void {        
        addEventListener(Event.ADDED_TO_STAGE, go);        
        }


    private function go(e:Event):void {
        removeEventListener(Event.ADDED_TO_STAGE, go);
        pongButton = item("Play", 20, 30, launchGame, 0xFF0000);
        addChild(pongButton);
        }
        
    private function launchGame(e:MouseEvent):void {
                
        }
        
    private function item(buttonText:String, X:int, Y:int, Funct:Function, txtColor:uint = 0xFFFFFF):Sprite {
        var item:Sprite = new Sprite();
        item.graphics.beginFill(0);
        item.graphics.lineStyle(1, txtColor, .5);
        item.graphics.drawRect(0, 0, 250, 30);
        var myText:TextField = new TextField();
        myText.selectable = false;
        myText.width = 250;
        myText.height = 30;
        item.addChild(myText);
        myText.autoSize = "center";
        myText.text = buttonText;
        myText.textColor = txtColor;
        item.addEventListener(MouseEvent.CLICK, Funct);
        item.x = X;
        item.y = Y; 
        return item;
                }               
        }
}

You must be wondering what did we make. 
Well, let's go step by step; we made a new class and a variable called 'pongButton', you already understand that.


The new thing is that item thing, let's review it:
pongButton = item("Play", 20, 30, launchGame, 0xFF0000);
  
What are we doing there?
See that item function below? Check the part between (  )
(buttonText:String, X:int, Y:int, Funct:Function, txtColor:uint = 0xFFFFFF)

Those are known as arguments. As you see, the 'item' function we just made takes 5 arguments:
buttonText, X, Y, Funct and txtColor. 
txtColor is an optional argument because there's a value already assigned to it (0xFFFFFF), the rest of the arguments are required and we will get an error if we don't pass the arguments when calling the function.


Basically, each argument has a name and a type. When calling the function, you must pass as much arguments as it requires and these arguments must be of the type declared on the function. 


One last thing, see the :Sprite after the parentheses? That's the return type of the function. The rest of our functions have been using ':void', which is the keyword used to tell that the function returns nothing. 


In this case, the function returns a Sprite, so when we said
 pongButton = item... we didn't mean that the pongButton variable will reference a Function (that would throw an error, because pongButton is of type Sprite), but that it will hold a reference of whatever the 'item' function returns. 


And what does it return?  Let's see each part of the function:
We introduced things that may seem new to you:
The graphics property of some objects is used to manipulate the display of it, by drawing and filling stuff with different colors. pretty useful for making graphics (why import a black rectangle when I can make my application draw one?).

One of the things that this function does is to draw a rectangle (drawRect) and then add a TextField (a text box) to it. Then it types some text inside of it and moves the resulting rectangle/textfield to its new position. 

All of this (the text and its color, the position) is based on what we passed as arguments for the function.
Also notice that one of the arguments is a function. That function will be triggered whenever we click the button, that's why we created that 'launchGame' function that is empty at the moment. 

Also note that we imported flash.events.MouseEvent, which is required for the application to detect mouse usage.

This may seem a bit complicated when you're starting, but don't be afraid; custom functions are one of the most powerful tools you'll find when designing applications. You can download the files at the end of this tutorial. As in the first tutorial, they are full of comments to help you understand it better. 

Anyway, that was a huge text all, huh? 
Time to take a break... let's leave that class alone and create a new one where we will move our code from the Main class:

package {
        import flash.display.Sprite;
        import flash.events.Event;

public class PongGame extends Sprite{
                private var paddle:Paddle; 
   public function PongGame():void {
        addEventListener(Event.ADDED_TO_STAGE, go);
                }


   private function go(e:Event):void {
        removeEventListener(Event.ADDED_TO_STAGE, go);
        paddle = new Paddle();
        addChild(paddle); 
            }         
       }
}


Note that both lines that involve the 'paddle' were taken from the Main class, so make sure to remove them from the Main if you haven't done so. 


Save PongGame.as and run a test. You should see nothing but you game's background. 
We moved the code that adds the paddle to the screen and placed it on the newly created PongGame class. 


Now head back to the Main class and move the two remaining lines below the '//entry point' and place them inside of a new function called 'buildMenu':

                private function buildMenu():void {
                        menu = new MainMenu();
                        addChild(menu);       
                }



Instead of adding the paddle right away, we're adding an instance of the MainMenu class. 


Now, from our MainMenu, we need a way to tell our game that we want to remove the menu and add the game when the player clicks the "Play" button. 


To do this, we're gonna use custom events. 
Custom events are another powerful tool because they allow us to make our game listen to anything we want. 


Remember we've made our game listen for keyboard and mouse usage? But what if we wanted it to listen for... let's say, when a player reaches certain area?  I doubt there's a listener for that.
Well, that's where our custom events come to play. 


Create a new class like this: 

package {
        import flash.events.Event;
    

public class CustomEvents extends Event{
  
public static const LAUNCH_GAME:String = "launch_game";
 
public function CustomEvents(e:String):void {
        super(e);
        }
    }
}



 This class extends the base class 'Event', and basically will create an event that event listeners can listen to. 
The good thing about this is that we can make an event for pretty much anything.

In this example, we declared a public and static (accesible from anywhere) constant named LAUNCH_GAME that stores a string. 

Let's put this in practice. Head to your main class and below the line 
'addChild(menu);', add the following one:

menu.addEventListener(CustomEvents.LAUNCH_GAME, startGame, false, 0, true); 

As you can see, this is almost the same as when we listen for another event, like the mouse ones. 
We use the addEventListener method and as the first two arguments, we pass the event type (CustomEvents.LAUNCH_GAME in this case) followed the function that will run when the event happens. 


So, just as our MouseEvent.CLICK listeners checks if the player clicked the object with the mouse, our CustomEvents.LAUNCH_GAME listener will check if the player wants to start the game, but there's a problem: Whenever the player clicks, an event of type MouseEvent.CLICK is automatically dispatched, but there are no built-in functions for our custom events, so we must dispatch them manually. 


Go to the MainMenu class and add this to the launchGame function:

dispatchEvent(new CustomEvents(CustomEvents.LAUNCH_GAME));

Now everything's ready... when the player clicks the "Play" button, it will dispatch our LAUNCH_GAME event, which is being expected by the Main class. 


Now let's go back to the Main.
First add a new variable:

                private var game:PongGame;

and make that startGame funtion:


                private function startGame(e:CustomEvents):void {
                        removeChild(menu);
                        menu.removeEventListener(CustomEvents.LAUNCH_GAME, startGame);
                        menu = null;  
                        game = new PongGame(); 
                        addChild(game);
                }

We removed the menu, its reference and listener (we'll cover that later) and created a new instance of the game, then moved it to the screen. 

Save and run a test. You should be able to see the paddle and move it after clicking the "Play" button. 

To finish, add a die function to the MainMenu. 
First add a listener for REMOVED_FROM_STAGE. Add it to the MainMenu constructor, below the ADDED_TO_STAGE listener:

        addEventListener(Event.REMOVED_FROM_STAGE, die);

And make the function:

private function die(e:Event):void {
        removeEventListener(Event.REMOVED_FROM_STAGE, die);
        pongButton.removeEventListener(MouseEvent.CLICK, launchGame);
        removeChild(pongButton);
        pongButton = null;                                
         }


And that's it. It took a bit of work and all we did was to give a better structure to our application, but believe me, it's worth the investment. Also, you learned about custom functions and custom events, two really powerful tools. ☺

I hope you enjoyed this chapter. 

 DOWNLOAD THE FILES: Click here


Suggested practices

Before moving on to the next chapter, it's suggested that you do the following exercises:

  • On your practice application, create a button that dispatches a custom event which triggers a custom function.

Chapter 1                                                  Chapter 3

7 comments:

  1. Anonymous7.4.12

    Fantastic tutorial, thank you.

    I'm finding that once I have clicked the Play button, in order to move the paddle I have to click the game area. Any ideas- a player quirk, or have I done something wrong?

    ReplyDelete
    Replies
    1. Anonymous3.8.12

      You didn't make a mistake(or at least I think not), the same thing happened to me, that's just a minor detail that doesn't appear in this part of the tutorial(I haven't checked the next part).
      You can fix that just by adding the following two lines to the startGame function in the Main class:

      stage.stageFocusRect = false;
      stage.focus = game;

      The second line is to change the focus of the stage onto the PongGame, which is what you did when you clicked the screen to be able to move the paddle. If you wrote only that line, the problem would be fixed but you would see a yellow rectangle surrounding your paddle. The first line disables this yellow rectangle, fixing the problem that appears by trying to fix the first problem ;). I guess that by the time that I've answered you've already found out about this, but I hope this is useful for anyone else who finds himself in the same situation.

      PS: Senekis, I love this tutorial and I hope you can continue it soon, although I'm sure you're a very busy man. Keep up the good work! I believe that I speak on behalf of all beginners like me when I say that your work is very much appreciated.

      Igna Oyi, from Buenos Aires.

      Delete
  2. Anonymous29.5.13

    missing where you declared variables and no idea where you initialize the build menu

    ReplyDelete
  3. Anonymous5.10.13

    yes. Missing the buildmenu(). you have to add buildmenu() below the // entry point. Hope this help

    ReplyDelete
  4. Anonymous16.4.14

    I typed the code exactly and went over it to check for errors a few times but i cant seem to find any. All i get when i run the code though is a white screen what do i do?

    ReplyDelete
    Replies
    1. Anonymous22.4.14

      You have to define the var menu you also have to call the function buildmenu() as the commnt above says. If you cant figure it out here:
      private var game:PongGame;
      private var menu:MainMenu; /* this beneath PongGame*/

      and

      private function init(e:Event = null):void
      {
      removeEventListener(Event.ADDED_TO_STAGE, init);
      // entry point
      buildMenu() /* add this to call function buildMenu() */
      }

      Im not sure why this is not included in the tutorial

      Delete
    2. Anonymous22.4.14

      Edit: The above is all in Main.as

      Delete