by Samuel Asher Rivello
Articles this issue
● Developing Flash Platform games with PushButton Engine
With the recent release of the PushButton Engine (PBE) from PushButton Labs, game developers finally have a community-created framework to help organize code and set industry standards. PBE is an open-source Flash game engine and framework that makes it easy to bring together great existing libraries and components for building Flash games. The idea is to spend less time on code, and more time on building fun games. In this article, we'll explore PBE technology, and then use it to create a simple game.
As of Q2, 2010 over 60 million player experiences have been served up on PBE technology. It is already in use inside many major game development companies such as Zinga, Playdom, RockYou Games, and Game House as well as many indie-developers and freelance developers. The PBE website's game showcase has several games proving the technology too including the recently released SocialCity Facebook simulation game and the highly-anticipated Grunts: Skirmish RTS game.
The creators of PBE, PushButton Labs, have vast expertise in the console and PC game industry and decided to target the Flash Platform for the PBE project. They want to enable game developers to more rapidly create game projects of any genre. Fifteen developers have contributed to the code-base and the group of PBE developer community who is actively using the technology, writing on the forums, and creating tutorials is already 1500 strong.
Benefits of PBE
PBE and the Flash Platform are a perfect combination. Professional game development teams can rapidly create rich, robust game experiences. Benefits include:
● The flexibility and power of PBE's component-based development paradigm
● The standardization and interoperability of third-party game kits and add-ons; coupled with the vibrant, helpful community, game developers now have a way to contribute and utilize truly plug and play libraries
● Proven game-specific development tools, including a Console, Logger, Profiler, ResourceManager, and more
● The ubiquity of the Flash Platform (it is everywhere!) and its tight integration with the Adobe Creative Suite (limitless asset creation!)
Even a non-PBE project can be quickly modified to take advantage of these tools. See the following code for a simple "PBHelloWorld." A few of these lines added to the beginning of your existing project can empower you with some of PBE's benefits:
/***************************************************************************************************
* Copyright (C) 2007 - 2010 : Rivello Multimedia Consulting
* For more information email : presentations2010@RivelloMultimediaConsulting.com
* For more information see : http://www.RivelloMultimediaConsulting.com
*
* This file and/or its constituents are licensed under the terms of the MIT license,
* which is included in the License.html file at the root directory of this SDK.
***************************************************************************************************/
//Marks the right margin of code *******************************************************************
package
{
//--------------------------------------
// Imports
//--------------------------------------
import com.pblabs.engine.PBE;
import com.pblabs.engine.debug.Logger;
import flash.display.Sprite;
//--------------------------------------
// Class
//--------------------------------------
/**
* <p>CLASS : PBHelloWorld: Main Class</p>
*
* <p>DATE : May 02, 2010</p>
* <p>AUTHOR : Samuel Asher Rivello</p>
* <p>CONTACT : presentations2010@RivelloMultimediaConsulting.com</p>
*
*/
public class PBHelloWorld extends Sprite
{
//--------------------------------------
// Properties
//--------------------------------------
//PUBLIC GETTER/SETTERS
//PUBLIC CONST
public static var WIDTH : Number = 800;
public static var HEIGHT : Number = 600;
//PRIVATE
//--------------------------------------
// Constructor
//--------------------------------------
/**
* FLASH: Setup the Project!
*
*/
[SWF(width="800", height="600", frameRate="60")]
public function PBHelloWorld ()
{
//SUPER
super ();
// STARTUP PBE: CONSOLE, LOGGER, & RESOURCE MANAGER
PBE.startup(this);
// TEST THE LOGGER
Logger.print(this, "PB Hello World!");
}
}
}
However these tools are just the start. One must develop a game from inception with PBE in mind to truly exploit its power. At the core of the PBE engine is a 'component-based' development paradigm and property referencing system that showcase the engines stated goal of providing a 'spectrum of rigidity'. Basically this means PBE gives you scalable tools to start your game prototypes fast and get a playable game ready quickly, yet continue to offer a clear migration path from a light-and-easy prototype to a robust, standards-compliant finished game project.
Under the hood of PBE
The PushButton engine is an ActionScript 3 library that can be added to any Flash or Flex Based project. The two projects included in this article for download feature the full editable source to facilitate learning, but PBE comes as a precompiled SWC file too for more rapid project compilation. The library is fit for deployment on the desktop, the browser, or on mobile devices. Anywhere Flash can play, PBE can play!
PBE is made of three general types of classes. There may be many of each type in each game project:
● Manager — Managers are the brains of the operation — each one has a unique task; for instance one focuses on the spatial area of the screen and the objects within it and one on graphics rendering of visible objects. Depending on its role in the game, a manager may or may not relate to entities and components directly.
● Entity — Each visual element in your game is represented by an entity. This could be anything; your player, the enemies, collectible items, etc....
● Component — Each entity contains components. This is where the bulk of your game code lies. Each components handle a specific responsibility. For instance perhaps one component handles rendering the entity in which it resides.
Managers can be created by developers, but PBE ships with managers for our most common needs. Here is a partial list of the managers packaged with the core PBE library:
● ProcessManager — Controls game logic and rendering logic and makes sure the right stuff is called at the right time. The API supports decelerating, accelerating, pausing, and stopping 'time' too.
● SpatialManager — Keeps track of simple objects in 2d space (width and height). The developer may choose a basic implementation or one with built-in Box2D physics. Plug-n-play game-physics? Yes! Developers can customize this manager to work in 3D too.
● ScreenManager — This is a simple way to swap game UI in and out as you move through your game. e.g. front menu screen, game screen with HUD, game-over screen, etc.
● SoundManager — Control all your game audio from a centralized location.
Entities are essentially empty shells. As a developer you fill them with the components needed. Each component has access to any Flash API desired, custom PBE events, as well as the following built-in PBE events:
● onFrame — Best used just for graphics rendering-routines. Each PBE 'frame' occurs in sync with a Flash 'frame' (e.g. 60 frames per second)
● onTick — Ticks are guaranteed to happen at a fixed interval. If the Flash Player slows down due to some number-crunching or heavy rendering, PBE will attempt to 'catch up' by calling more ticks. It balances the run-time experience well. As Lavon Woods of HybridMindset.com points out "Finally, PBE has gotten around Flayer Player's well known elastic racetrack issue".
● onThink — Based on an arbitrary, per-use timer. This is ideal for AI code which can be processed slowly (e.g. every 1–5 seconds)
● onCollision — Based on built-in collision checking routines. Highly customizable and very useful!
PBE theory
The core, unique difference with PBE game development is its 'component-based' code organization. Components here are not user interface components such as a Button or DropDownList, here components mean a reusable, modular class used for gaming. Typically flash game developers begin their career with inheritance-based game development and may evolve to creating a custom framework for their games. These Object-oriented styles are are different from the system in PBE. In the following sections we see 3 approaches to game development from least evolved to most.
"Quick and dirty" game development
I created the Flyer game in a previous Edge article, An introduction to developing games on the Adobe Flash Platform. The game-play style is similar to the 1980's Frogger game for Atari. In it all the code for the game is placed on Frame 1 of the ActionScript timeline. This approach is easy to create and easy to understand, making it ideal for an intro-level article. However more complex games beg for better code organization — breaking up the code into separate classes.
OOP game development
The Flash Platform's ActionScript 3 (AS3) programming language supports object-oriented programming (OOP). A principal advantage with OOP compared to the quick-and-dirty approach is that code is organized into 'inheritance-chains' where if a 'child-class' is to act similar to its 'parent-class'. The 'extends' keyword is used to establish this parent-child relationship. Once 'extended' the child behaves exactly like the parent. Then more customized abilities can be given to the new child. OOP is how the majority of Flash Platform development works. For RIAs this is a well suited technique for code organization. However due to the unique needs of game development OOP can present challenges.
Figure 1. Bloated classes in a game's class inheritance chain will become overburdened and difficult to maintain.
OOP has drawbacks too. Using OOP for the Flyer game we'd likely start with two classes representing the onscreen objects; FlyerMC and BiPlaneMC. Both would take advantage of MovieClip, extending that built-in class to handle rendering and other tasks. Then game-specific, common code would necessitate the creation of a custom MoverItem class. What often happens is that so much of the game code is shared by classes that the 'MoverItem' gets very bulky. The bulky classes in the inheritance chain will become difficult to maintain and will become a challenge to debug. Adding new features is slow because new code must be added carefully. This is a simple example, but illustrates a problem that rings true in so many game projects; bloated overburdened parent-classes. A better solution is using component-based development for games.
Component-based game development: The Flyer Game
Ben Garney, Lead Developer of PBE knows from experience, "The biggest shift is thinking in terms of components, not monolithic classes. The rest is straightforward." To understand more about how the component-based game development works, let's continue with the examples above. I created a PBE version of the Flyer game. Here the game logic is spread between entities for Flyer, BiPlane, Blimp. The first two entities are shown in Figure 2. BlimpEntity works just like BiPlaneEntity so it is not shown. The two entities shown share a render component, but the flexibility of PBE allows them to vary greatly in the other components used. FlyerEntity has 4 components of its own. BiplaneEntity as two of its own (the same two used by BlimpEntity). What is evident from this simple example is by entities composed of reusable components we have great flexibility without creating any overly-bulky classes like in the OOP example above. Very nice!
Development
This article shows the playable Flyer game below and also shows much of the code below too. To see even more you may download the source code too.
- Download and unzip the source file.
- Open Adobe Flash Professional CS5.
- Open PBFlyerGame/src/PBFlyerGame_v1.fla using File > Open.
- Publish the project using File > Publish Preview > Default.
The resulting Flash application is shown in Figure 3. Use the arrow keys to play. Simple and fun!
Figure 3. The PBE Flyer Game. Click anywhere to play. Use the arrow keys to move to the top of the screen. Avoid hitting obstacles. Refresh the page to restart.
The core game code can be seen below. This is the main PBFlyerGame.as class containing the entry point into the coding. Reviewing the code and code-comments is a great introduction to PBE. More explanation follows in the article below the code:
/***************************************************************************************************
* Copyright (C) 2007 - 2010 : Rivello Multimedia Consulting
* For more information email : presentations2010@RivelloMultimediaConsulting.com
* For more information see : http://www.RivelloMultimediaConsulting.com
*
* This file and/or its constituents are licensed under the terms of the MIT license,
* which is included in the License.html file at the root directory of this SDK.
***************************************************************************************************/
//Marks the right margin of code *******************************************************************
package com.rmc.projects.pbflyergame
{
//--------------------------------------
// Imports
//--------------------------------------
import com.pblabs.box2D.Box2DManagerComponent;
import com.pblabs.engine.PBE;
import com.pblabs.engine.debug.Logger;
import com.pblabs.engine.entity.IEntity;
import com.pblabs.engine.entity.PropertyReference;
import com.pblabs.rendering2D.AnimationController;
import com.pblabs.rendering2D.AnimationControllerInfo;
import com.pblabs.rendering2D.SpriteSheetRenderer;
import com.pblabs.rendering2D.spritesheet.SWFSpriteSheetComponent;
import com.pblabs.rendering2D.ui.SceneView;
import com.rmc.projects.pbflyergame.components.CollisionDetectComponent;
import com.rmc.projects.pbflyergame.components.FaceForwardComponent;
import com.rmc.projects.pbflyergame.components.GameOverComponent;
import com.rmc.projects.pbflyergame.components.MoveByKeyboardInputComponent;
import com.rmc.projects.pbflyergame.components.MoveHorizontallyComponent;
import com.rmc.projects.pbflyergame.components.ScreenTrapComponent;
import com.rmc.projects.pbflyergame.components.ScreenWrapComponent;
import com.rmc.projects.pbflyergame.screens.GameScreen;
import com.rmc.projects.pbflyergame.screens.IntroScreen;
import com.rmc.utils.pbe.FlyerGameHelper;
import flash.display.Sprite;
import flash.geom.Point;
//--------------------------------------
// Class
//--------------------------------------
/**
* CLASS : PBFlyerGame: Main Game Class
*
* DATE : May 02, 2010
* AUTHOR : Samuel Asher Rivello
* CONTACT : presentations2010@RivelloMultimediaConsulting.com
*
*/
[SWF(width="800", height="600", frameRate="60")]
public class PBFlyerGame extends Sprite
{
//--------------------------------------
// Properties
//--------------------------------------
//PUBLIC GETTER/SETTERS
//PUBLIC CONST
public static var WIDTH : Number = 800;
public static var HEIGHT : Number = 600;
//PRIVATE
//--------------------------------------
// Constructor
//--------------------------------------
/**
* FLASH: Setup the Game and Start Playing!
*
*/
public function PBFlyerGame ()
{
//SUPER
super ();
// START PBE
PBE.startup(this);
// 0.5 = HALF-SPEED,
// 2.0 = DOUBLE SPEED (SMOOTHER ANIMATION)
PBE.processManager.timeScale = 0.8;
// TEST TRACING OUTPUT MESSAGE
Logger.print(this, "PBFlyerGame - Press 'Alt/Option' + '~' to Open Debugger");
// LOAD EMBEDDED RESOURCS
PBE.addResources(new PBFlyerGameResources());
// Set up our screens.
PBE.screenManager.registerScreen ("intro_screen", new IntroScreen());
PBE.screenManager.registerScreen ("game_screen", new GameScreen());
// Show the intro_screen - Then wait for a user click and it calls 'restartGame()'
PBE.screenManager.goto ("intro_screen");
}
//--------------------------------------
// Methods
//--------------------------------------
//PRIVATE
/**
* PBE: Restart Game
*
* @return void
*/
public function restartGame ( ) : void
{
//PLAY SOUND
PBE.soundManager.play( PBFlyerGameResources.SOUNDTRACK_SOUND,"sfx",1,9999); //loop many times, 'forever'
// CLEAR SCREEN (IF 'RE'-STARTING). Not Fully Working So Restart is Disabled After Gameplay
_clearEverything ();
// CREATE SCENE ENTITY
_createScene();
// CREATE BACKGROUND ENTITY
_createBackgroundEntity();
// CREATE OBSTACLE ENTITIES
_createObstacleEntities();
// CREATE FLYER ENTITY
_createFlyerEntity();
}
/**
* PBE: CLEAR SCENE
*
* @return void
*/
private function _clearEverything ( ) : void
{
//clear any screen entities
PBE.rootGroup.destroy();
PBE.rootGroup.clear();
}
/**
* PBE: CREATE SCENE ENTITY
*
* @return void
*/
private function _createScene ( ) : void
{
//make the scene
var sceneView : SceneView = new SceneView();
sceneView.width = WIDTH;
sceneView.height = HEIGHT;
PBE.initializeScene (sceneView, FlyerGameHelper.SCENE, null, Box2DManagerComponent);
// Adjust graphics for convenience
PBE.scene.setWorldCenter ( new Point (-WIDTH, -HEIGHT));
//PHYSICS ARE NOT NEEDED SO WE DISABLE GRAVITY
//BUT 'BOX2D' IS USED FOR ITS COLLISION DETECTION - NICE!
(PBE.spatialManager as Box2DManagerComponent).gravity = new Point (0,0);
}
/**
* PBE: CREATE FLYER ENTITY
*
* @return void
*/
private function _createFlyerEntity ( ) : void
{
//
// PROPERTIES
//
var position_point : Point = new Point (WIDTH*.65,HEIGHT-50);
var size_point : Point = new Point (.1,.1);
var zIndex_uint : uint = 10;
//
// ENTITY
//
// Allocate an entity for our background sprite
var flyer_entity:IEntity = PBE.allocateEntity();
FlyerGameHelper.createSpatialEntity ( flyer_entity, position_point, size_point);
//
// COLLISION DETECTION
//
var collisionType_str : String = "Flyer";
var collidesWithCollisionTypes_array : Array = ["Obstacle"];
FlyerGameHelper.enableCollisionDetection ( flyer_entity, collisionType_str,
collidesWithCollisionTypes_array, true);
//
// RENDER
// COMPONENTS
//
// LOAD MC ASSET
var swfSpriteSheetComponent : SWFSpriteSheetComponent = new SWFSpriteSheetComponent();
FlyerGameHelper.loadMovieClipAsset (swfSpriteSheetComponent,
PBFlyerGameResources.ASSETS_SWF,
PBFlyerGameResources.MOVIE_CLIP_OBSTACLE_FLYER);
// USE ASSET TO RENDER
var spriteSheetRenderer:SpriteSheetRenderer = new SpriteSheetRenderer();
FlyerGameHelper.setupSpriteSheetRenderer ( spriteSheetRenderer,
swfSpriteSheetComponent,
0,
zIndex_uint);
// CREATE ANIMATION LOOP #1
var idle_animationControllerInfo:AnimationControllerInfo = new AnimationControllerInfo();
idle_animationControllerInfo.loop = false;
idle_animationControllerInfo.frameRate = 1;
idle_animationControllerInfo.spriteSheet = swfSpriteSheetComponent;
// CREATE ANIMATION LOOP #2
var move_animationControllerInfo:AnimationControllerInfo = new AnimationControllerInfo();
move_animationControllerInfo.loop = true;
move_animationControllerInfo.frameRate = 1;
move_animationControllerInfo.maxFrameDelay = 250;
move_animationControllerInfo.spriteSheet = swfSpriteSheetComponent;
// SAVE ALL ANIMATION LOOPS
var animationController : AnimationController = new AnimationController ();
animationController.spriteSheetReference = new PropertyReference (FlyerGameHelper.RENDER_SPRITE_SHEET);
animationController.currentFrameReference = new PropertyReference (FlyerGameHelper.RENDER_SPRITE_INDEX);
animationController.animations [FlyerGameHelper.ANIMATION_IDLE] = idle_animationControllerInfo;
animationController.animations [FlyerGameHelper.ANIMATION_MOVE] = move_animationControllerInfo;
animationController.defaultAnimation = FlyerGameHelper.ANIMATION_IDLE;
animationController.currentAnimationName = FlyerGameHelper.ANIMATION_IDLE
animationController.changeAnimationEvent = FlyerGameHelper.ANIMATION_CHANGE_EVENT;
animationController.currentAnimationReference = new PropertyReference (FlyerGameHelper.CURRENT_ANIMATION_REFERENCE);
// ADD COMPONENTS
flyer_entity.addComponent(animationController, FlyerGameHelper.ANIMATION_CONTROLLER);
flyer_entity.addComponent(spriteSheetRenderer, FlyerGameHelper.RENDER);
//
// MOVE-BY-KEYBOARD
// COMPONENT
//
// Create an instance of our hero controller component
var moveByKeyboardInputComponent:MoveByKeyboardInputComponent = new MoveByKeyboardInputComponent();
flyer_entity.addComponent( moveByKeyboardInputComponent, MoveByKeyboardInputComponent.NAME);
//
// SCREEN-TRAP/WRAP
// COMPONENT
//
var isScreenWrapping_boolean : Boolean = false; //Try (true) and see the flexibility of PBE
if (isScreenWrapping_boolean) {
//EXPERIMENT: WRAP AROUND SCREEN (From Left Edge to Right Edge, Etc...)
var screenWrapComponent : ScreenWrapComponent = new ScreenWrapComponent();
flyer_entity.addComponent ( screenWrapComponent, ScreenWrapComponent.NAME );
} else {
//EXPERIMENT: BE 'TRAPPED' AND STAY IN SCREEN'S BOUNDS
var screenTrapComponent : ScreenTrapComponent = new ScreenTrapComponent();
flyer_entity.addComponent ( screenTrapComponent, ScreenTrapComponent.NAME );
}
//
// FACE-FORWARD
// COMPONENT
//
var faceForwardComponent : FaceForwardComponent = new FaceForwardComponent();
flyer_entity.addComponent ( faceForwardComponent, FaceForwardComponent.NAME );
//
// COLLISION-DETECTION
// COMPONENT
//
var collisionDetectComponent : CollisionDetectComponent = new CollisionDetectComponent();
flyer_entity.addComponent ( collisionDetectComponent, CollisionDetectComponent.NAME );
//
// GAME-OVER
// COMPONENT
//
var gameOverComponent : GameOverComponent = new GameOverComponent ();
flyer_entity.addComponent ( gameOverComponent, GameOverComponent.NAME );
//
// INITIALIZE
//
flyer_entity.initialize ("flyer_entity");
}
/**
* PBE: CREATE OBSTACLE ENTITIES
*
* @return void
*/
private function _createObstacleEntities ( ) : void
{
// CREATE OBSTACLE ENTITY (MOVIE_CLIP_APPEARANCE, POSITION, AND DRAWING DEPTH)
_createObstacleEntity ( PBFlyerGameResources.MOVIE_CLIP_OBSTACLE_BIPLANE, new Point (WIDTH*.0, HEIGHT*0.20), 1, 1, 30);
_createObstacleEntity ( PBFlyerGameResources.MOVIE_CLIP_OBSTACLE_BLIMP, new Point (WIDTH*.20, HEIGHT*0.40), 2, -1, 25);
_createObstacleEntity ( PBFlyerGameResources.MOVIE_CLIP_OBSTACLE_BIPLANE, new Point (WIDTH*.35, HEIGHT*0.55), 3, 1, 15);
_createObstacleEntity ( PBFlyerGameResources.MOVIE_CLIP_OBSTACLE_BLIMP, new Point (WIDTH*.50, HEIGHT*0.70), 4, -1, 35);
}
/**
* PBE: CREATE OBSTACLE ENTITIES
*
* @return void
*/
private function _createObstacleEntity (aMovieClipName_str : String,
aPosition_point : Point,
aZIndex_uint : uint,
aHorizontalDirection_int : int,
aHorizontalSpeed_num : Number) : void
{
//
// ENTITY
//
// Allocate an entity
var obstacle_entity:IEntity = PBE.allocateEntity();
FlyerGameHelper.createSpatialEntity ( obstacle_entity, aPosition_point);
//
// COLLISION DETECTION
//
var collisionType_str : String = "Obstacle";
var collidesWithCollisionTypes_array : Array = ["Flyer"];
FlyerGameHelper.enableCollisionDetection (obstacle_entity, collisionType_str, collidesWithCollisionTypes_array, false);
//
// RENDER
// COMPONENTS
//
// LOAD MC ASSET
var swfSpriteSheetComponent : SWFSpriteSheetComponent = new SWFSpriteSheetComponent();
FlyerGameHelper.loadMovieClipAsset (swfSpriteSheetComponent, PBFlyerGameResources.ASSETS_SWF, aMovieClipName_str);
// USE ASSET TO RENDER
var spriteSheetRenderer:SpriteSheetRenderer = new SpriteSheetRenderer();
FlyerGameHelper.setupSpriteSheetRenderer (spriteSheetRenderer, swfSpriteSheetComponent, 0, aZIndex_uint);
// ADD AS RENDERER
obstacle_entity.addComponent( spriteSheetRenderer, FlyerGameHelper.RENDER );
//
// MOVE-HORIZONTALLY
// COMPONENT
//
var moveHorizontallyComponent:MoveHorizontallyComponent = new MoveHorizontallyComponent();
moveHorizontallyComponent.horizontalDirection_int = aHorizontalDirection_int;
moveHorizontallyComponent.horizontalSpeed_num = aHorizontalSpeed_num;
obstacle_entity.addComponent ( moveHorizontallyComponent, MoveHorizontallyComponent.NAME );
//
// SCREEN-WRAP
// COMPONENT
//
var screenWrapComponent : ScreenWrapComponent = new ScreenWrapComponent();
obstacle_entity.addComponent ( screenWrapComponent, ScreenWrapComponent.NAME );
//
// FACE-FORWARD
// COMPONENT
//
var faceForwardComponent : FaceForwardComponent = new FaceForwardComponent();
obstacle_entity.addComponent ( faceForwardComponent, FaceForwardComponent.NAME );
//
// INITIALIZE
//
obstacle_entity.initialize("obstacle_entity" + aZIndex_uint);
}
/**
* PBE: CREATE BACKGROUND ENTITY
*
* @return void
*/
private function _createBackgroundEntity ( ) : void
{
//
// PROPERTIES
//
var position_point : Point = new Point (0,0);
var zIndex_uint : uint = 1;
//
// ENTITY
//
// Allocate an entity for our background sprite
var background_entity:IEntity = PBE.allocateEntity();
background_entity.initialize ("background_entity");
FlyerGameHelper.createSpatialEntity ( background_entity, position_point);
//
// RENDER
// COMPONENTS
//
// LOAD MC ASSET
var swfSpriteSheetComponent : SWFSpriteSheetComponent = new SWFSpriteSheetComponent();
FlyerGameHelper.loadMovieClipAsset (swfSpriteSheetComponent, PBFlyerGameResources.ASSETS_SWF, PBFlyerGameResources.MOVIE_CLIP_BACKGROUND_GAME_SCREEN);
// USE ASSET TO RENDER
var spriteSheetRenderer:SpriteSheetRenderer = new SpriteSheetRenderer();
FlyerGameHelper.setupSpriteSheetRenderer (spriteSheetRenderer, swfSpriteSheetComponent, 0, zIndex_uint);
// ADD AS RENDERER
background_entity.addComponent( spriteSheetRenderer, FlyerGameHelper.RENDER );
}
}
}
Component communication
In Figure 2 we see the four components used by FlyerEntity. Required of every PBE game, components must communicate. One component responsible for the rendering of the entity, must check the position and rotation from another component before drawing itself onscreen. PBE is flexible in this communication.
Three types of component-to-component communication exist in PBE:
● Direct-Access — Here one component holds a reference to another component hard-coded and calls methods as needed. This is simple to use but it introduces dependencies and makes code less portable. Generally it is to be avoided.
● Events — Useful for broadcasting info, just like in ActionScript and Flex. Typically events are broadcast from a component via the Entity. Other components 'listen' to the Entity for those events. This is more favorable than direct-access because it requires less 'knowledge' between components; thus more independence.
● Property References — This is the best way to communicate. There is a simple syntax and there are no hard-coded dependencies. Code is more flexible and reusable. Property references are heavily used in the Flyer game. Available in the download to this article is the 'ScreenWrapComponent.as' class. Reviewing this class, it will be evident how a property reference to position and a property reference to size are used to keep the Flyer within the screen's bounds at all times.
To recap, the PBE world is full of entities. Each entity has components. To communicate between any two components in the world the preferred manner is to use property references.
Where to next?
With deeper exploration into PBE its XML Level loading system and its ResourceManager the power of PBE become more evident. There is certainly much to learn but the rewards are many. To learn more, download and investigate the Flyer game from this article, checkout the PBE website, and then get started by developing a very simple game or two.
There is a wealth of information available to PBE developers:
● Games Showcase — See great games created by the community to get inspired
● Community Forum — Post questions and learn from others; project leaders regularly offer advice and code samples, too
● Component Store — Download and upload free and premium components
● Documentation — Learn basic and advanced info with tutorials, AsDocs, and more
Conclusion
I wish PBE existed in every [programming] language.- Phil Peron, Professional Game Developer
PBE comes packed with great game-specific tools that are ready right out-of-the-box. We've seen the promise of power and flexibility in PBE's component-based development methodology. The scalability and speed-of-use of the property referencing systems is tricky at first will be well worth the effort. Get your feet wet with a simple game or two and then set out to create your next Flash Platform game masterpiece with the PushButton Engine.
Visit the Adobe Flash Platform Gaming Technology Center
Acknowledgements: I want to offer a special thanks to the contributors of this article, to Autumn Rain Turkel ofTangledDreams who created all art for the Flyer game, and to all of the developers and community leaders who provided valuable insight and expertise for this article. I'd also like to thank Minimal Noise for providing the sound track used in the Flyer game.