back

Developing physics-based games with Adobe Flash Professional

by Samuel Asher Rivello

ELSE ELSE ELSE

The entertainment industry continues to shift toward interactive gaming content, and the competition to captivate users has never been fiercer. In 2009, revenue generated from the gaming industry beat out the film industry for the first time. What's more, game revenue is expected to grow 12% globally this year to reach $36 billion. Online gaming is a significant part of the industry, and you can be sure that entertainment bigwigs as well as indie developers are clamoring to create and license winning games on the web.

While there is no single solution for creating a great online gaming experience, you can greatly improve the enjoyment-to-production-budget ratio by considering a few things.

First, adding social aspects to games will build community. Many gameplay mechanics for a single-player experience will benefit from multiplayer functionality. If you spend development resources to create an environment and tools where social interaction can take place, then much of the fun comes organically from social interaction.

Second, as a game designer, you can greatly engage users with a departure from hackneyed gameplay mechanics. Users are attracted to unique experience they can't find elsewhere. Physics-based gaming can give your game development repertoire a huge boost, enabling sandbox-style game mechanics and emergent gameplay.

We live in a real world ruled by real physics. So we expect everything to act as if it was in the world we are used to living in. Consider the PC game Max Payne with its rag doll character physics. The game would not be nearly as much fun without such realism.

— Emanuele Feronato, Box2DFlash game developer, EmanueleFeronato.com

Physics games created with Flash software have been popular for a decade. Some of the biggest blockbuster games are physics-based such as Line Rider. But recently, there has been an explosion of great physics games for Flash, including the addictive, innovative gameplay of Wake the Royalty.

In this article, I discuss the types of physics games you can create with Adobe Flash technology and examine the ROI of adding physics to your existing games as well as creating all new physics-based game experiences. I review the basics and go in depth with two examples. Readers may experiment and republish the projects via Adobe Flash CS4 Professional or simply read along. All the source code is shown in this article and is available for download.

Why physics?

Practically any project that involves some sort of interaction between physical objects can benefit from physics. Creating your physical world inside the physics world allows you to model movements and interactions more uniformly and realistically and to intuitively add additional effects at any time like object destruction and explosions.

— Matthew Bush, creator, Box2DFlash

Flash is a number-crunching powerhouse and can support the rapid mathematics needed for physics calculations. While physics is not native to the Flash Player API, third-party libraries provide the needed algorithms. The integration of physics can improve your existing non-physics-based games with new enhancements. Imagine innovating on a classic game like Asteroid to enable the asteroids to bounce convincingly off of each other instead of passing through each other like in the original game. Physics can do just that.

Beyond improving existing games, game physics unlocks all-new forms of gameplay that are impossible without rigid body dynamics.

fantastic_contraption_screenshot_v1.jpg

Figure 1. Fantastic Contraption, a Flash puzzle physics game.

An exemplary Flash puzzle physics game is Fantastic Contraption. This game would not even be possible without a strong physics engine. The objective in each level is to move all red objects from a blue starting area, past obstacles, and into a red goal area (see Figure 1). The user adds components (such as gears, pulleys, and so forth) to the environment, creating a simple machine to solve the level. You may think that with just five components, the gameplay would be limited. However, the complexity of the object interactions in the world allows for sandbox-style gameplay where you feel the freedom of a child with a pile of toys. This freedom fuels and rewards the player's creativity and makes for a very engaging game experience.

Choosing a physics engine

The programming required to perform accurate, efficient physics simulations is daunting. For most intermediate and even advanced Flash developers, the mathematics required is out of reach. Thankfully, the community has responded with various physics libraries (also referred to as physics engines) that do the heavy lifting for you. Not all of these libraries are created equal, however.

Over the past ten years, Flash developers have created engines to solve physics interactions. Many of those projects have lost their support and died off, but a few are powerful and still active. Here are some of the more recent offerings that are free to use and compatible with ActionScript 3:

Some libraries, such as APE, Fisix, FOAM, Wow-Engine(3D), and Motor Physics 2, showcase impressive features but are not ideal for production use because they are either in early alpha stage or they are outdated.

A few of these options are acceptable for production use. In this article, I use Box2DFlash because it incorporates all the benefits of its powerful predecessor (Box2D), it runs smoothly and dependably, it has a robust community, and it is regularly updated and improved. Box2DFlash even powers the Fantastic Contraption game, shown in Figure 1.

The original Box2D project is a fantastic software-based physics simulation library created by Erin Catto. Debuted at the Game Development Conference in 2006, Box2D is written in platform-independent C++. It has been ported to other languages such as C#, Java, JavaScript, Python, and ActionScript.

The Box2DFlash library is the port to ActionScript 3 by Flash developer Matthew Bush. It works great and apparently is updated whenever the original Box2D library is updated. With a strong community and ongoing support, Box2DFlash is a big win for Flash Platform game developers. It has been used to power hundreds of online games you can play today.

Box2DFlash pros and cons

Box2DFlash does many things well. Using Box2DFlash, you can:

Using Box2DFlash is not without its drawbacks though. Todd Kerpelman, creative director at NeoEdge, authors Kerp.net — a fantastic Box2DFlash tutorial site. He loves the library but points out some of its weaknesses. According to Kerpelman, “Box2DFlash is not particularly ActionScript centric. For instance, rather than using the built-in Flash flash.geom.Point class, it has its own class that works similarly to Point, but just different enough that it'll probably mess you up the first few times you try using it. Also it's slower than some other engines when dealing with certain [calculations].”

Box2DFlash syntax

Box2DFlash shares the same C++ style API as the original Box2D (for example, b2Body.GetMass(); and b2Body.m_invI;). This is a benefit because developers can find code snippets to common algorithms from any Box2D port, and it's likely that the snippets will work in the ActionScript 3 version. However this API style looks a little foreign to the average ActionScript 3 developer, so it can be difficult to learn, use, and debug. Also, setting up a new project takes some time and considerable boilerplate code. See the Box2DFlash project page for examples.

Luckily, developer Zevan Rosser has wrapped the functionality of Box2DFlash into his QuickBox2D project. This takes care of that boilerplate code and eases the burden of some of the most common physics tasks. So we have QuickBox2D wrapping Box2DFlash, which in turn is a port of Box2D.

Getting started with QuickBox2D

QuickBox2D is a mini library I created to work with Box2DFlash. The main purpose of this library is to significantly simplify instantiation of rigid bodies and provide a simple way to skin rigid bodies with custom graphics.

Zevan Rosser, creator, QuickBox2D ActionScript 3 library

To understand the basics of this library, let's set up a simple demo project.

To get started:

  1. Download and unzip the source files.
  2. Open Adobe Flash CS4 Professional.
  3. Open /src/QuickBox2DDemo_v1.FLA by choosing File > Open and navigating to the file.
  4. Publish the project by choosing File > Publish Preview > Default.

The resulting Flash application is shown in Figure 2.

AlertThis content requires Flash

Download the free Flash Player now!

Get Adobe Flash Player

Figure 2. The Complete Flash CS4 Demo 'QuickBox2Ddemo_v1.swf'. Drag the three blocks with your mouse to play.

While incredibly simplistic, this little demo shows the power of physics for Flash. The blocks fly, fall, and bounce off each other in realistic ways. Even without any rules this demo is fun and thought-provoking for new game ideas.

In Figure 3, you can see how to set up a simple QuickBox2D project:

//Marks the right margin of code *******************************************************************
package
{
   
   //--------------------------------------
   //  Imports
   //--------------------------------------
   import com.actionsnippet.qbox.QuickBox2D;
   import com.actionsnippet.qbox.QuickObject;
   
   import flash.display.MovieClip;
   import flash.display.Sprite;

   /**
    * This metadata sets the size and framerate for the project.
    * 
    **/ 
    [SWF(width="600", height="300", backgroundColor="#cbcbcb", frameRate="30")]
   //--------------------------------------
   //  Class
   //--------------------------------------
   public class QuickBox2DDemo extends Sprite
   {      
      
      //--------------------------------------
      //  Properties
      //--------------------------------------
      //PUBLIC GETTER/SETTERS
      /**
       * Master Container for holding the physics bodies' visual parts
       * 
       **/ 
      private var _sim_movieclip : MovieClip;
      
      /**
       * Master Container for holding the physics bodies' data objects
       * 
       **/ 
      private var _sim_quickbox2D : QuickBox2D;
      
      /**
       * MovieClipSkins. A or B
       * 
       * A) Published From Flash Builder 4: This project utilizes the './libs/StackZGame_assets_v1.swc' for graphics skinning
       * B) Published From Flash CS4: This project utilizes library assets for graphics skinning
       * 
       * Below when BlockSkin class is used, know that it is from that swc.
       * 
       */
      
      /**
       * Constants
       * 
       **/
      public static const PHYSICS_SCALE       : Number = 1/30;
      public static const PHYSICS_TIME_STEP   : Number = 1/30;
      
      
      //--------------------------------------
      //  Constructor
      //--------------------------------------
      /**
       * Initialization of;
       * The display list for visual elements
       * The Physical world with properties to define how the world will behave
       * The Blocks are added to the world with their own properties
       * The world is 'started' so the interaction and 'fun' begins.
       * 
       * <span class="hide">Any hidden comments go here.</span>
       *
       */
      public function QuickBox2DDemo ()
      {
         //   SUPER
         super (); 
         
         //   CREATE A MOVIECLIP FOR THE PHYSICS WORLD
         _sim_movieclip  = new MovieClip ();
         addChild (_sim_movieclip);

         //   CREATE THE PHYSICS WORLD (Partial list of parameters (default + description) is shown
         _sim_quickbox2D = new QuickBox2D (_sim_movieclip, 
            {
               
               //   Rendering
               debug         :    false,              //false - Temporarily Render w/ Drawing API
               simpleRender  :    true,               //true - Toggles simple vs Custom Graphics
               renderJoints  :    true,               //true, - Toggles rendering of joints
               
               //   Physics
               gravityX      :    0,                  //0 - The x component of gravity
               gravityY      :    20,                 //20 - The y component of gravity
               timeStep      :    PHYSICS_TIME_STEP,  //1/60 - The Box2D time step.
               iterations    :    20,                 //20 - The Box2D iterations.
               frim          :    false,              //true, Toggles (Frame Rate 
                                                      //Independent Movement).
               
               //   Misc
               bounds      :    [-100, -100, 100, 100], //[-100, -100, 100, 100] - World Bounds
               customMouse :    false                   //false - For MouseEvents in 3D Worlds
               
            });
         
         //   CREATE RIGID WALLS AROUND STAGE (NOT NEEDED, BUT CAN BE USEFUL)
         _sim_quickbox2D.createStageWalls();
         
         //   SET DEFAULTS FOR MOVABLE OBJECTS   (density != 0)
         _sim_quickbox2D.setDefault   
            (   
               {
                  linearDamping   :    1,    //Friction applied from the 'air'
                  density         :    1,    //More Dense = Moves Slower
                  friction        :    1     //Friction applied from colliding with other rigid bodies
               }
            );
         
          //   START THE PHYSICS & ENABLE MOUSE
         _sim_quickbox2D.start();       //run an enterfame to power the engine
         _sim_quickbox2D.mouseDrag();   //allow mouse to move all objects (with density != 0)

         //   CREATE SOME BLOCKS TO PLAY WITH
             for (var blockIndex_uint : uint = 0; blockIndex_uint < 3; blockIndex_uint ++) {
            _doCreateNewBlock (blockIndex_uint);
         }
         
      }
      

      /**
       * Create a new physical block with visual skin
       * 
       * @param aBlockIndex_uint The index of the current block being created.
       * 
       * @return void
       */
      private function _doCreateNewBlock (aBlockIndex_uint : uint) : void
      {
         //CREATE MOVABLE OBJECT - A falling block at top, offscreen
         var lastBlock_quickobject : QuickObject = _sim_quickbox2D.addBox   
            (      
               {
                  x      : PHYSICS_SCALE     * (100 + Math.random()*400), //Remember to Convert Pixels to Meters
                  y      : PHYSICS_SCALE     * (100 + Math.random()*200), 
                  width   : PHYSICS_SCALE    * 100, 
                  height    : PHYSICS_SCALE  * 100, 
                  skin    : BlockSkin,        //Skin For Rigid Physics Body
                  
                  draggable: true             //User may manipulate the blocks with mouse
               }
            );
         
         //   THE SKIN IS INSTANTIATED, AND ACCESSIBLE AS 'userData'
         //   YOU CAN ALSO STORE ARBITRARY PROPERTIES ON 'userData' for whatever you need
         (lastBlock_quickobject.userData as BlockSkin).character_mc.head_mc.gotoAndStop (aBlockIndex_uint+1); //show 3 faces
      }
   
      
   }
}

Figure 3. The Complete Flash CS4 Document Class 'QuickBox2DDemo.as'

The QuickBox2D Constructor accepts two parameters. First, this is passed as a reference to the current object, the parent of the physics world. The physics object's children. The second parameter is the flexible and powerful parameter object. The parameter object contains high-level settings for the physics world. (See 'Partial list of parameters in Figure 3 for more information.)

Note that all Box2D distance units are in meters. To convert meters to pixels, use the following conversion:

var someDistanceInMeters_int  :  int = someDistanceInPixels_int  / 30;

In my projects, I store the 30 in a constant to prevent typos:

var someDistanceInMeters_int  :  int =  someDistanceInPixels_int  / PHYSICS_SCALE;

Creating the physics-based game StackZ

To see a more complex example of this library, let's set up a game project called StackZ. In this game, the user drags a seesaw platform with the mouse to collect and stack falling boxes. The platform and the boxes react with realistic physics, creating a simple but engaging experience.

Design

Not everything in a physics game is part of the physics simulation. Let's divide up the screen elements into two major groups:

The non-physics elements are: The physics elements are:
  • The background
  • The finish line
  • The foreground
  • The display text
  • The pedestal below the platform
  • The platform below the blocks
  • The blocks in the stack

The non-physics elements are laid out in the typical Flash fashion. They appear either behind the physics world (for example, the background) or in front of it (for example, the display text).

The physics elements will be created, skinned, and simulated using QuickBox2D. Most of the interactions will happen automatically as part of the simulation. That's the beauty of Box2DFlash. However some custom object interactions will be coded manually.

The following are Box2DFlash built-in interactions: The following are custom object interactions:
  • The pedestal is static.
  • The platform rotates on a pivot point.
  • Blocks fall, bounce, and rotate due to forces such as gravity and friction.
  • The user may drag the platform with the mouse to reposition it at any time.
  • A block in the stack positioned above the finish line will cause a victory.
  • A block falling off the screen's bottom will cause a failure.

Note: Much of using Box2DFlash is just creating objects and then letting the engine take care of the rest. It becomes quite empowering when you realize how easy it is to set up complex simulations. However some common functionality is trickier than you would think in physics-based games. Workarounds can be found on the Box2D Forum, on its Flash subsection, and by searching for solutions with Google.

The following tasks are possible but tricky in Box2DFlash:

  • Changing the size of a rigid body (you must destroy it and then re-create it)
  • Controlling a rigid body with keys (you must use forces)
  • Manually detecting collisions to play a collision sound (you must use a ContactListener object and navigate linkedLists)
  • Clearing and resetting the physics world after the game is over (see the _doRestGame() method in Figure 5 for a solution)
  • Detecting when the highest block is on top of a stack of contiguously touching blocks (see the _onEnterFrame method in Figure 5 for a simple, functional, yet imperfect solution)

Development

The project will use the Box2DFlash library for most of the heavy lifting. All custom code will be within the project's document class StackZGame.as.

To get started:

  1. Download and unzip the source files.
  2. Open Adobe Flash CS4 Professional.
  3. Open /src/StackZGame_v1.FLA by choosing File > Open and navigating to the file.
  4. Publish the project by choosing File > Publish Preview > Default.

The resulting Flash application is shown in Figure 4.

AlertThis content requires Flash

Download the free Flash Player now!

Get Adobe Flash Player

Figure 4. The Complete Flash CS4 Game 'StackZGame_v2.swf'. Drag the black platform with the mouse to play.

In Figure 5, we see how to set up all the major parts of a physics-based game project.

//Marks the right margin of code *******************************************************************
package
{
   //--------------------------------------
   //  Imports
   //--------------------------------------
   import Box2D.Dynamics.b2Body;
   
   import com.actionsnippet.qbox.QuickBox2D;
   import com.actionsnippet.qbox.QuickObject;
   
   import flash.display.MovieClip;
   import flash.display.Sprite;
   import flash.events.Event;
   import flash.events.MouseEvent;
   import flash.events.TimerEvent;
   import flash.utils.Timer;
   
   /**
    * This metadata sets the size and framerate for the project.
    * 
    **/ 
   [SWF(width="600", height="600", backgroundColor="#00ff00", frameRate="30")]
   
   //--------------------------------------
   //  Class
   //--------------------------------------
   public class StackZGame extends Sprite
   {      
      
      //--------------------------------------
      //  Properties
      //--------------------------------------
      //PUBLIC GETTER/SETTERS
      /**
       * Master container for holding the physics bodies visual parts
       * 
       **/ 
      private var _sim_movieclip : MovieClip;
      
      /**
       * Physics world and graphics.  Arranged by depth.
       * Tabbed-in here, means its a child of line above.
       * 
       **/ 
      private var _background                 : Background;
      private var _world_sprite               : Sprite;
      private var _platform_quickobject       : QuickObject;
      private var _pedestal_quickobject       : QuickObject;
      private var _lastBlock_quickobject      : QuickObject; 
      private var _platformArrows             : PlatformArrows;
      private var _finishLine                 : FinishLine;
      private var _foreground                 : Foreground;
      private var _scoreTextMC                : ScoreTextMC;
      private var _instructionsTextMC         : InstructionsTextMC;
      private var _border                     : Border;
      private var _sim_quickbox2D             : QuickBox2D;
      
      /**
       * Setup sound 
       * 
       **/
      private var _blockCreation_timer        : Timer;
      private var _blockCreationSound         : BlockCreationSound;
      private var _blockFallenSound           : BlockFallenSound;
         
      /**
       * Block-specific data
       * 
       */
      private var _blocks_array : Array;
      private var _blocksFallenTotal_uint     : uint;
            
      /**
       * MovieClipSkins. A or B
       * 
       * A) Published From Flash Builder 4: This project utilizes the './libs/StackZGame_assets_v1.swc' for graphics skinning
       * B) Published From Flash CS4: This project utilizes library assets for graphics skinning
       * 
       * Below when BlockSkin class is used, know that it is from that swc.
       * 
       */   
      
      /**
       * Constants
       * 
       **/
      public    static const PHYSICS_SCALE                : Number = 1/30;
      public    static const PHYSICS_TIME_STEP            : Number = 1/30;
      public    static const BLOCKS_FALLEN_TOTAL_ALLOWED  : Number = 5;
      private static const BLOCK_FALLEN_Y_LIMIT           : Number = 590;
      
        
      //--------------------------------------
      //  Constructor
      //--------------------------------------
      /**
       * Setup the Layout. This is mostly visual, not physics setup yet.
       * 
       * <span class="hide">Any hidden comments go here.</span>
       *
       */
      public function StackZGame ()
      {
         //   SUPER
         super (); 
         
         //   CREATE WORLD
         _world_sprite = new Sprite ();
         addChild(_world_sprite);
         
         //   ----------------------------------------------------------
         //   POPULATE WORLD 
         //   (from lowest visual depth to highest)
         _background = new Background();
         _world_sprite.addChild (_background);
         //
         _finishLine = new FinishLine ();
         _world_sprite.addChild (_finishLine);
         _finishLine.y = 300;
         //
         _platformArrows = new PlatformArrows ();
         _world_sprite.addChild (_platformArrows);
         //
         var _foreground : MovieClip = new Foreground ();
         _world_sprite.addChild (_foreground);
         //
         _instructionsTextMC = new InstructionsTextMC ();
         _world_sprite.addChild (_instructionsTextMC);
         _instructionsTextMC.y = 5;
         _instructionsTextMC.textField.htmlText =   "Mouse-drag the black pedestal to play! " +
                                                    "Create a tall pile before dropping " +
                                                    BLOCKS_FALLEN_TOTAL_ALLOWED + " blocks";
         
         //
         _scoreTextMC   =  new ScoreTextMC ();
         _world_sprite.addChild (_scoreTextMC);
         _scoreTextMC.y = _instructionsTextMC.y +  _instructionsTextMC.height;
         //
         _border = new Border ();
         addChild (_border);
         
         
         //   ----------------------------------------------------------
         //   CREATE SOUND
         _blockCreationSound = new BlockCreationSound ();
         _blockFallenSound    = new BlockFallenSound ();
         
         //   LISTEN FOR THE RESTARTGAME MOUSE CLICK
         addEventListener (MouseEvent.MOUSE_DOWN, _onMouseClickAnywhere);
         
         //   DO THE INITIAL RESTART - (AKA... the 'start')
         _doRestartGame();
         
      }
      
      /**
       * This is called initially, then upon each game restart
       * 
       * Most of the visual are setup already, so this is primarily the physics setup
       * 
       * @return void
       */
      private function _doRestartGame () : void
      {
         
         //   DELETE SIM MOVIECLIP IF EXISTING
         if (_sim_movieclip != null) {
            _sim_quickbox2D.destroy();
            _world_sprite.removeChild(_sim_movieclip);
         } 
         
         //   CREATE SIM MOVIECLIP
         _sim_movieclip  = new MovieClip ();
         _world_sprite.addChildAt (_sim_movieclip ,_world_sprite.getChildIndex(_finishLine));
         
         //   RESET FALLEN BLOCK COUNT
         _blocksFallenTotal_uint = 0;
         
         //   CREATE AN ARRAY TO HOLD ALL BLOCKS
         _blocks_array = new Array ();
         
         //   ----------------------------------------------------------
         //   CREATE THE PHYSICS WORLD (Partial list of parameters (default + description) is shown
         _sim_quickbox2D = new QuickBox2D (_sim_movieclip, 
            {
               
               //   Rendering
               debug          :    false,                 //false - Temporarily Render w/ Drawing API
               simpleRender   :    true,                  //true  - Toggles simple vs Custom Graphics
               renderJoints   :    true,                  //true, - Toggles rendering of joints
               
               //   Physics
               gravityX       :    0,                     //0    - The x component of gravity
               gravityY       :    20,                    //20   - The y component of gravity
               timeStep       :    PHYSICS_TIME_STEP,     //1/60 - The Box2D time step.
               iterations     :    40,                    //20   - Calculation cycles per TimeStep
               frim           :    false,                 //true - Toggles (Frame Rate 
                                                          //Independent Movement).
               
               //   Misc
               bounds         :    [-100, -100, 700, 700], //[-100, -100, 100, 100] - World Bounds
               customMouse    :    false                   //false - For MouseEvents in 3D Worlds
               
            });
         
         
         //   SET DEFAULTS FOR IMMOVABLE OBJECTS   (density = 0) 
         _sim_quickbox2D.setDefault   
            (
               {
                  linearDamping   :    1,    //Friction applied from the 'air'
                  density         :    0,    //More Dense = Moves Slower, 0 means immovable
                  friction        :    1     //Friction applied from colliding with other rigid bodies
               }
            );
         
         
         //   CREATE IMMOVABLE OBJECT - The pedestal at the bottom of the screen.
         _pedestal_quickobject = _sim_quickbox2D.addBox   
            (      
               {
                  x      : PHYSICS_SCALE    * (_border.width/2), //Remember to Convert Pixels to Meters
                  y      : PHYSICS_SCALE    * (525), 
                  width  : PHYSICS_SCALE    * 10, 
                  height : PHYSICS_SCALE    * 10, 
                  skin   : PedestalSkin            //Skin For Rigid Physics Body
               }
            );
         
         //   SET DEFAULTS FOR MOVABLE OBJECTS   (density != 0)
         _sim_quickbox2D.setDefault   
            (   
               {
                  linearDamping   :    1,  //Friction applied from the 'air'
                  density         :    1,  //More Dense = Moves Slower
                  friction        :    1   //Friction applied from colliding with other rigid bodies
               }
            );
         
         //   CREATE MOVABLE OBJECT - The platform at the bottom of the screen, above the pedestal
         _platform_quickobject = _sim_quickbox2D.addBox   
            (      
               {
                  x      : PHYSICS_SCALE    * (_border.width/2), //Remember to Convert Pixels to Meters
                  y      : PHYSICS_SCALE    * (520), 
                  width  : PHYSICS_SCALE    * 300, 
                  height : PHYSICS_SCALE    * 40, 
                  skin   : PlatformSkin                     //Skin For Rigid Physics Body
               }
            );
         
         //   MATCH THE VISUAL-ONLY (NON-PHYSICS) ARROW GRAPHICS TO THE PLATFORM
         //   I didn't want the block to bounce off the arrows
         _platformArrows.x = _platform_quickobject.userData.x;
         _platformArrows.y = _platform_quickobject.userData.y;
         
         
         //   CREATE A JOINT - The platform balances on top of the pedestal
         _sim_quickbox2D.addJoint
            (
               {
                  type: QuickBox2D.REVOLUTE, 
                  a:_platform_quickobject.body, 
                  b:_pedestal_quickobject.body, 
                  collideConnected:false,
                  lowerAngle:-Math.PI/6,
                  upperAngle:Math.PI/6,
                  enableLimit:true
               }
            );
         addEventListener(Event.ENTER_FRAME, _onEnterFrame);

         //   START THE PHYSICS
         _sim_quickbox2D.start();       //run an enterfame to power the engine
         _sim_quickbox2D.mouseDrag();   //allow mouse to move all objects (with density != 0)
         
         //   SHOW TEXT
         _doUpdateScoreText ();
         
         //   SETUP AUTO-BLOCK-CREATION
         if (!_blockCreation_timer) {
            _blockCreation_timer = new Timer (4000);
            _blockCreation_timer.addEventListener(TimerEvent.TIMER, _onBlockCreationTimer);
         }
            _blockCreation_timer.start();
         
         //   CREATE ONE BLOCK IMMEDIATLY
         _doCreateNewBlock();
         
      }
      
      /**
       * Create a new block off the top of the screen.  It will fall with gravity.
       * 
       * @return void
       */
      private function _doCreateNewBlock () : void
      {
         //   PLAY SOUND
         _blockCreationSound.play();
         
         //   CREATE MOVABLE OBJECT - A falling block at top, off screen
         _lastBlock_quickobject = _sim_quickbox2D.addBox   (      
            {
               
               x      : PHYSICS_SCALE    * (_border.width/2 - Math.random()*50 + Math.random()*50), 
               y      : PHYSICS_SCALE    * (0), 
               width  : PHYSICS_SCALE    * 50,   //Remember to convert pixels to meters with PHYSICS_SCALE
               height : PHYSICS_SCALE    * 50, 
               skin   : BlockSkin,               //Skin For rigid physics body
               
               draggable: false                  //User may only manipulate the platform, not the blocks
            }
         );
         
         //   KEEP A LIST OF BOXES
         _blocks_array.push (_lastBlock_quickobject);
         
         //   THE SKIN IS INSTANTIATED, AND ACCESSIBLE AS 'userData'
         //   YOU CAN ALSO STORE ARBITRARY PROPERTIES ON 'userData' for whatever you need
         (_lastBlock_quickobject.userData as BlockSkin).character_mc.head_mc.gotoAndStop (1); //show a 'happy' face
      }
      
      /**
       * Count each time a block falls offscreen.  This will cause a game loss eventually.
       * 
       * @return void
       */
      private function _doUpdateBlockFallenTotal () : void
      {
         //   COUNT UP
         _blocksFallenTotal_uint++;
         _doUpdateScoreText ();
         
         //   CHECK FOR GAME OVER
         if (_blocksFallenTotal_uint >= BLOCKS_FALLEN_TOTAL_ALLOWED) {
            _doEndGameAsLoser();
            
         }
      }
      
      /**
       * Remove blocks that fall offscreen and increment counter
       * 
       * @param aEvent QuickObject which has fallen offscreen
       * 
       * @return void
       */
      private function _doHandleBlockOffScreen (aQuickObject : QuickObject) : void
      {
         
         //   PLAY A SOUND
         _blockFallenSound.play();
         
         //   DESTROY OBJECT
         aQuickObject.destroy();
         
         //   REMOVE FROM CUSTOM ARRAY
         _blocks_array.splice (_blocks_array.indexOf(aQuickObject),1);
         
         //   CHANGE DISPLAY TEXT
         _doUpdateBlockFallenTotal ();
         
      }
      
      /**
       * Show the 'You Won!' text and stop the game physics from running.
       * 
       * @return void
       */
      private function _doEndGameAsWinner () : void
      {
         _scoreTextMC.textField.htmlText =    "Blocks Dropped Offscreen: " + 
            _blocksFallenTotal_uint + " of " + 
            BLOCKS_FALLEN_TOTAL_ALLOWED + 
            " Allow<BR><BR><BR><P ALIGN='CENTER'><FONT SIZE = '40'>YOU WON!</FONT></P><FONT SIZE = '10'><BR>Mouse-click anywhere above the " +
			"'Finish Line' " + "to restart the game</FONT>ed";
         
         _doStopGamePermanently ();
      }
      
      /**
       * Show the 'You Lost!' text and stop the game physics from running.
       * 
       * @return void
       */
      private function _doEndGameAsLoser () : void
      {
         _scoreTextMC.textField.htmlText =    "Blocks Dropped Offscreen: " + 
            _blocksFallenTotal_uint + " of " + 
            BLOCKS_FALLEN_TOTAL_ALLOWED + 
            " Allow<BR><BR><BR><P ALIGN='CENTER'><FONT SIZE = '40'>YOU LOST!</FONT></P><FONT SIZE = '10'><BR>Mouse-click anywhere above the " +
			"'Finish Line' " + "to restart the game</FONT>ed";
         
         _doStopGamePermanently();
      }

      /**
       * Show the # of blocks which have fallen.  Too many fallen means game over.
       * 
       * @return void
       */
      private function _doUpdateScoreText () : void
      {
         _scoreTextMC.textField.htmlText = "Blocks Dropped Offscreen: " + _blocksFallenTotal_uint + " of " + BLOCKS_FALLEN_TOTAL_ALLOWED + " Allowed";
      }
      
      /**
       * Stop the game physics from running and other repeated tasks
       * 
       * @return void
       */
      private function _doStopGamePermanently () : void
      {
         _sim_quickbox2D.destroy();
         removeEventListener (Event.ENTER_FRAME, _onEnterFrame);
         _sim_quickbox2D.stop();
            _blockCreation_timer.reset();
         _blockCreation_timer.stop();
      }
      
      
      //--------------------------------------
      //  Events
      //--------------------------------------   
      /**
       * Capture Event: Event.ENTER_FRAME, 30 times per second, this code executes my custom code
       * 
       * @param aEvent Event
       * 
       * @return void
       */
      private function _onEnterFrame (aEvent : Event) : void
      {
         
         //   MATCH THE VISUAL-ONLY (NON-PHYSICS) ARROWS TO THE PLATFORM
         _platformArrows.rotation = _platform_quickobject.userData.rotation;
         
         //   LOOP THROUGH BLOCKS FOR THREE UNIQUE CHECKS (A,B,C)
         for each (var quickObject : QuickObject in _blocks_array) {
            
            
            //   A) DID BOX FALL INTO PLAY - Necessary for 'victory' detection
            if (!quickObject.userData.isInPlay_boolean &&
               Math.abs(quickObject.body.m_linearVelocity.y) > .1 ) {
               quickObject.userData.isInPlay_boolean = true;
            }
            
            
            //   B) DID BOX FALL OFFSCREEN? - A bad thing for user
            if (quickObject.y / PHYSICS_SCALE > BLOCK_FALLEN_Y_LIMIT) {
               _doHandleBlockOffScreen(quickObject);
            }
            
            
            //   ----------------------------------------------------------
            //  CONDITIONS FOR VICTORY  - Is there a 'stack' of contiguously touching boxes 
            //                          - with at least one above the finish line?
            //                          - **The solution below is simple, but not perfect**
            //   ----------------------------------------------------------
            //   C) Is there at least one box that meets ALL of the following criterion
            //      1. Is above the Finish Line
            //      2. Is 'in play'
            //      3. Is moving very slowly (approximatly non-moving)
            //      4. Touching at least one other block (this doesn't prove its in a 'stack' but it helps)
            //
            //   ----------------------------------------------------------
            if (   (quickObject.y / PHYSICS_SCALE) < _finishLine.y       && 
                  quickObject.userData.isInPlay_boolean               &&
                  Math.abs(quickObject.body.m_linearVelocity.y) < 0.4         ) {
                  
               //IS IT COLLIDING WITH ANOTHER BLOCK (NOT ITSELF)
               for each (var quickObject2 : QuickObject in _blocks_array) {
                  if (quickObject != quickObject2) {
                     if ((quickObject.userData as MovieClip).hitTestObject (quickObject2.userData) ) {
                        _doEndGameAsWinner ();
                     }
                  }
                  
               }
            }
            
         }
         
         
      }
      
      /**
       * Capture Event: TimerEvent.TIMER, Every X seconds, create a new block
       * 
       * @param aEvent TimerEvent
       * 
       * @return void
       */
      private function _onBlockCreationTimer (aEvent : TimerEvent) : void
      {
         _doCreateNewBlock ();
      }
      
      
      /**
       * Capture Event: MouseEvent.CLICK, When user clicks on the screen, restart the game.
       * 
       * @param aEvent MouseEvent
       * 
       * @return void
       */
      private function _onMouseClickAnywhere (aEvent : MouseEvent) : void
      {
         if (aEvent.stageY < _finishLine.y) {
            _doRestartGame();
         }
         
      }
      
      
   }
}

Figure 5. Complete Flash CS4 document class, StackZGame.as

Fantastic! In just about 400 lines (500 with comments) of ActionScript 3 we have a fully functioning physics-based game. This would not be possible without using Box2D-based libraries to ease our development.

Conclusion

The quality of casual, browser-based games is now very high and the public is beginning to view these as professional products. Physics-based games allow players to be creative with open-ended game solutions and they key into the users' natural ability to predict the smooth motion of physical objects. This adds needed polish and fun to gaming.

— Erin Catto, creator, Box2D physics library

We now see the value of physics to create fun, new styles of gameplay. By taking advantage of mature, powerful physics libraries such as Box2DFlash and QuickBox2D, teams can rapidly create unique and engaging gameplay. While thankfully much of the physics interactions are "automatic," sometimes seemingly simple tasks can be tricky to complete. Together with the downloadable examples and the explanation in this article, the time is now to add physics-based gaming to your Flash Platform development repertoire.

Acknowledgements

I want to offer a special thanks to Autumn Rain Turkel of TangledDreams who created all the art for the StackZ game, and to all the developers and community leaders who provided valuable insight and expertise for this article.

‹ Back


Samuel Asher Rivello is the principal of Rivello Multimedia Consulting (RMC). RMC’s Flash and Flex services include software architecture, consulting, development, and training. Sam has a decade of experience creating games and applications, and is currently traveling the globe to collaborate with top companies.