Apple Seizure

...with apologies to Ferry Halim.

I. The project

The brilliant casual game designer, Ferry Halim, includes on his Web site, orisinal.com, a number of excellent Flash games. Perhaps the simplest of the lot is a lovely little game called Apple Season, in which the player maneuvers a basket back and forth across the bottom of the screen in order to intercept apples that fall randomly from branches above.

Above is an imitation (visually crude, but operationally accurate) of Halim's game. Because of the way the Flash Player interacts with Web browsers, you must click your mouse within the window above before you can control the bucket. Use the left and right arrows after that initial click.

As this game neatly illustrates a number of useful scripting techniques, we'll dissect it here. A few finer details, such as the scoring system and the internal animation on the bucket, will be left out for the sake of simplicity. (See if you can figure those out on your own.)

II. Architecture

The main movie (.fla) file for this project contains a total of 9 layers. The lowermost of these is devoted to the tree graphics, which are simply drawn onto the layer. Next is a layer containing an instance of bucket_mc, our moveable apple-catcher, given the instance name theBucket. Above these two layers are seven more layers, each containing an instance of apple_mc. The way our code is written, these instances do not need names.

Each movie clip is linked to an ActionScript class: bucketClass and appleClass, respectively. There is no document class in this version of the project.

III. The Bucket class

Here's the entirety of the file bucketClass.as:

package
{
  import flash.display.MovieClip;
  import flash.events.*;
	
  public class bucketClass extends MovieClip
  {
		
    public function bucketClass():void  //CONSTRUCTOR
    {
      y = 400-height/2;
      x = width/2;
	  stage.addEventListener(KeyboardEvent.KEY_DOWN, moveBucket);
    }
		
    public function moveBucket(event:KeyboardEvent):void
    {
      if(event.keyCode == 37 && x > width/2) x-=8;
      if(event.keyCode == 39 && x < 650 - width/2) x+=8;
    }
  }
}

By now, classes of this type should start to look fairly familiar. There's a minimal Constructor function that sets the initial placement of the object, flush with the left margin (i.e., offset from X=0 by half the Movie Clip's width), and flush with the bottom of the window (maximum Y value, or 650, minus half the object's height). Next we have an Event Listener that tunes into the KEY_DOWN event, which is generated whenever anything on the keyboard is pressed.

The main action takes place in the callback method, moveBucket(). Every keypress generates a code, and here we listen for two in particular, the ones belonging to the left arrow key (37) and the right arrow key (39). Depending which key is pressed, we increment or decrement the value of x, moving the bucket right or left.

Note we don't just listen for the key values, though: we also test to make sure the x value is not out of range -- that's what those further clauses on the right of each double ampersand (&&) signify. Without them, the player could move the bucket offscreen.

IV. The Apple class

The class attached to all seven apple objects is a bit more extensive than the bucket's relatively simple animation, so we'll handle it in chunks, rather than a single block. Remember that all the sections given below go into a single package{} container, as in any ActionScript 3 class.

Here are the importations:

  import flash.display.MovieClip;
  import flash.events.Event;

These are fairly straightforward. We import the MovieClip package because we're scripting a Movie Clip (which could be a Sprite; though if you want to add that bobbing effect with internal animation, you'll need to stick with Movie Clip, in order to work with an internal timeline). We import flash.events.Event because we'll be using an ENTER_FRAME listener/callback.

Now here's the start of the class body, and the variable declaration section:

  public class appleClass extends MovieClip
  {
    public var downAmt:Number;
    public var action:Boolean = false;

We declare two variables, downAmt, a Number, and action, which is of the type Boolean, meaning it will either be true or false. We initialize it to false. You'll see what both these variables do in just a bit.

Next comes the Constructor for appleClass, which contains only two statements:

  function appleClass():void // CONSTRUCTOR
  {
    this.addEventListener(Event.ENTER_FRAME, animate);
    reSet();
  }

The first statement is a familiar Event Listener, handing off to a custom method called animate(), as in our very first example of the bouncing ball. The second is a call to a custom method named reSet(), which sets the start position and intial state of every apple still hanging from the tree.

Since it's the less interesting of the two custom methods, let's start with reSet():

  function reSet():void
  {
    x = width/2 + Math.random()*(650-width);
    y = height/2+Math.random()*10;
    action = false;
    downAmt = 1;
    rotation = Math.random()*15;
  }

As you can see, this method sets the horizontal and vertical positions of the Movie Clip (one of those seven apples) to randomized values. The horizontal range is adjusted so that the object cannot be placed partly or entirely offscreen. The vertical range offsets the apple from the top of the screen, with a possible 10 pixel variation, to make the distribution less uniform in appearance.

We set action to false here, which may look redundant, since action is initialized to that value; but remember, reSet() is invoked whenever the apple falls below the bottom of the screen, so if action changes to true at some point (and we're coming to that), it needs to be changed back every time the apple recycles.

We also set the variable downAmt to 1, and the current rotation of the ojbect to a random value between 0 and 15. Again, this lends visual diversity, tilting each apple some random amount.

Finally we arrive at the all-important animate() method, where all the good stuff happens:

  function animate(event:Event):void
  {
    if(action)
    {
      y += downAmt;
      downAmt += downAmt*0.2
      rotation +=downAmt;
      if(y > 400) reSet();
    }
    else
    {
      if(Math.round(Math.random()*75) == 1) action = true;
    }
			
    //hit test
    if(hitTestObject(MovieClip(root).theBucket)) reSet();
  }

Note first that the animation statements -- the instructions that increment the y property of the object, causing it to fall down the screen -- are contained within an if/else structure. We do this because we don't want every apple simply to tumble down after it appears. Instead, the apples need to hang in place for some tantalizingly unpredictable interval until they fall off the tree.

The difference between a hanging and a falling apple is the value of the Boolean variable action. When action is false, there is no action, and the else branch of the if/else condition is taken.

On that else branch, we generate a random value between 0 and 74 -- in effect, we roll a 75-sided die. If and only if the value that comes back is 1, then we change the value of action to true, meaning that on the next pass through the if/else condition, we'll be taking the first branch, where the action is.

If this business of the 75-sided die seems counter-intuitive, remember that the Flash Player is executing these instructions several times every second. (We're using a frame rate of 24 fps in our example.) So it actually takes only a few seconds, most of the time, for the random number generator to throw a 1. Note that we round the random value to the nearest integer; without this step, the value of 1 would be much less likely, though not mathematically impossible.

So now we have action set to true, and on the next frame entry, we pass down the other side of the if/else circuit. We increment the y property of the current Movie Clip by downAmt, which is at least 1, then add 20% of downAmt back to that variable. This line gives us that progressive acceleration observable in Ferry Halim's game. We plug the same accelerating value into rotation, so that our apple not only tumbles every more rapidly down the window, but also spins progressively faster.

Once the apple's y value exceeds the window height (400), we call reSet() and hang the apple somewhere back in the trees.

There's only one more detail to consider: the hit test. If you haven't already read our discussion of inter-object communication, this would be a good time. It will explain what that mysterious prefix MovieClip(root) means, attached to theBucket. Basically, as you probably already know, this line tests for intersection between the geometry of our apple and the bucket. In the complete version of the game, intersection has scoring consequences. In our simplified version here, we do an early reset.

V. Source files

There are three source files for this project:

You may download these files from the directory appleSeizure_022508, within MULTIMEDIA in the shared account on student-iat.



University of Baltimore Logo

Last updated: 02/23/08 17:16:56
Copyright © 2008 School of Information Arts and Technologies