Making a Space Invaders clone with PushButton – The player

Google+ Pinterest LinkedIn Tumblr +

PLAY THE DEMO

DOWNLOAD THE SOURCE CODE

RETURN TO THE TUTORIAL INDEX

The next step in creating the space invaders clone is to add the player. Because the player is not animated it is actually easier to define than the enemy due to the fact that we don’t need to deal with the AnimationController. The player will be defined as a template, not because more than one will be added at any one time, but because it makes it easier to create a new player entity from multiple levels if needed. Without a common template each separate level would have to define the player entity individually, which is an unnecessary inconvenience. In fact for that reason pretty much every entity in a multilevel game would be defined in a template, and referenced from a level definition.

Code

While PushButton includes components do deal with a lot of the basics, like positioning, moving and rendering an entity, one thing it does not do is provide any way to control an entity. This can either be control via keyboard or mouse input, or an AI controlling an enemy. The player in the space invader game can only perform a few simple actions, namely move left and right, and fire its weapon. This logic will be housed in a component called PlayerControllerComponent.

Code

PlayerControllerComponent.as

The players movement (and eventually the firing of the weapon) will be updated once per frame. The easiest way to perform actions within the PushButton render loop is to create a component that extends the TickedComponent class. This gives you access to the onTick function, which is called once per frame.

public class PlayerControllerComponent extends TickedComponent
{

The PlayerControllerComponent needs two references to variables held by its parent entity: the velocity and position. Moving the player left and right will be achieved by modifying the x component of the velocity, while the position will be modified to prevent the player from moving off the screen.

 [TypeHint(type="flash.geom.Point")]
 public var velocityReference:PropertyReference;
 [TypeHint(type="flash.geom.Point")]
 public var positionReference:PropertyReference;

The next three variables define the width of the display, the speed that the players ship will move, and how far away from the edge of the screen to stop the player as it moves horizontally.

 public var screenWidth:int = 480;       
 public var speed:Number = 200;
 public var sideBuffer:int = 32;

As a general rule the constructor of a component should not include any code. When a new component is created, and the constructor is called, it is not attached to an entity. Although they are not used here, the onAdd and onRemove functions are essentially the equivalent of the constructor and destructor (for those C++ programmers out there).

 public function PlayerControllerComponent()
 {
  super();
 }

The onTick function is where we update the player. First we get a reference to the entities velocity and position. These values are held by the spatial component and linked in the XML template definition, but it makes no difference to the PlayerControllerComponent where these values come from. This kind of loose coupling is one of the aspects of the PushButton component design system. The PlayerControllerComponent knows how to modify the position and velocity of an entity, but it does not care how the position and velocity are used.

 public override function onTick(tickRate:Number):void
 {
  super.onTick(tickRate);
  
  var velocity:Point = owner.getProperty(velocityReference);
  var position:Point = owner.getProperty(positionReference);

There is always the possibility that the property references were not set, in which case the position and velocity variables will be null. Even though the release flash player fails silently with these kinds of errors, we might as well deal with this possibility gracefully here by not proceeding any further.

  if (velocity == null || 
   position == null)
   return;

First we want to bound the position of the player so it will not move off the edge of the screen.

  if (position.x > screenWidth - sideBuffer)
   position.x = screenWidth - sideBuffer;
  else if (position.x < sideBuffer)
   position.x = sideBuffer;

Then we need to update the velocity depending on which arrow key is pressed, if any.

  if (InputManager.isKeyDown(InputKey.LEFT))
  {
   velocity.x = -speed;  
  }
  else if (InputManager.isKeyDown(InputKey.RIGHT))
  {
   velocity.x = speed;
  }
  else
  {
   velocity.x = 0;    
  }

We then sync these changes back to the parent entity.

  owner.setProperty(positionReference, position);
  owner.setProperty(velocityReference, velocity);
     
 }
 
}

References.as

While PushButton will handle the creation of components from the class name strings in the XML file, the ActionScript compiler itself has no idea what components have been referenced in XML. This can be a problem, because classes not referenced by ActionScript are not included in the final SWF. Flex developers may have noticed that compiler errors are not raised for classes with syntax errors unless those classes are referenced from the main application. In order to force the compiler to include classes not other referenced in code, a class, called References in this case, is created. The only purpose of this class is to include variables whose types match those that are being referenced by the XML. Note that no objects have to be created, but just mentioning the components types is enough for the compiler to include those classes in the final SWF.

The References class below was taken from a demo supplied with the PushButton SDK. We just need to add a reference to our PlayerControllerComponent to ensure it is included.

Additional components will need to be referenced in the same way, but the process is exactly the same, so in future tutorials when you see a new component being created it is assumed that a reference has been added to the References class.

public class References
{
 private var _scene2DComponent:com.pblabs.rendering2D.Scene2DComponent;
 private var _spriteRenderComponent:com.pblabs.rendering2D.SpriteRenderComponent;
 private var _spriteSheetComponent:com.pblabs.rendering2D.SpriteSheetComponent;
 private var _simpleSpatialComponent:com.pblabs.rendering2D.SimpleSpatialComponent;
 private var _basicSpatialManager2D:com.pblabs.rendering2D.BasicSpatialManager2D;
 private var _cellCountDivider:com.pblabs.rendering2D.CellCountDivider;
 private var _animationController:com.pblabs.rendering2D.AnimationController;
 private var _box2DDebugComponent:com.pblabs.box2D.Box2DDebugComponent;
 private var _box2DManagerComponent:com.pblabs.box2D.Box2DManagerComponent;
 private var _box2DSpatialComponent:com.pblabs.box2D.Box2DSpatialComponent;
 private var _polygonCollisionShape:com.pblabs.box2D.PolygonCollisionShape;
 private var _circleCollisionShape:com.pblabs.box2D.CircleCollisionShape;
 private var _animatorComponent:com.pblabs.animation.AnimatorComponent;
 private var _sceneView:com.pblabs.rendering2D.ui.SceneView;      
 private var _playerController:PlayerControllerComponent; 
}

Resources.as

Resource management was covered in this tutorial here. For this demo we have added the players ship image as a resource. Again, whenever a new resource is added to the demo from this point it is assumed that it has been embedded by the Resources class.

public class Resources extends ResourceBundle
{
 [Embed(source="../media/player.png", mimeType="application/octet-stream")]
 public var ImgPlayer:Class;

 [Embed(source="../media/enemy1.png", mimeType="application/octet-stream")]
 public var ImgEnemy1:Class;   
 
 [Embed(source = "../media/templates.xml", mimeType = 'application/octet-stream')]
 public var XmlTemplates:Class;
 
 [Embed(source = "../media/managers.xml", mimeType = 'application/octet-stream')]
 public var XmlManagers:Class;
 
 [Embed(source = "../media/spritesheets.xml", mimeType = 'application/octet-stream')]
 public var XmlSpriteSheets:Class;
 
 [Embed(source = "../media/levels.xml", mimeType = 'application/octet-stream')]
 public var XmlLevels:Class;
 
 [Embed(source = "../media/level1.xml", mimeType = 'application/octet-stream')]
 public var XmlLevel1:Class;
 
}

Share.

About Author

Leave A Reply