In previous articles, the entities used for the player to control have been simple vector shapes. However, you have probably already noticed when playing web games online that they pretty much all use bitmaps instead of vectors to render the visual game assets.
There are a couple of reasons for this. Firstly, bitmap images provide a particular level of polish that, while achievable with vector shapes, doesn’t always render as desired (also almost everyone’s first experience of ‘drawing’ with a computer is with a raster based drawing package, not a vector package). Couple this with the fact that most of the time game assets don’t need to be scaled and the value of using vectors (with the ability to scale without degrading) doesn’t make much sense.
Blitting
The second reason is that most games ‘blit’ their assets. Now this may be the first time you’ve ever heard the term ‘blit’ or come across the process of ‘blitting’, so let me explain in a bit more detail. Blitting is the process by which you take two or more bitmap data objects (one being the destination object, the rest being the source objects) and you combine them in to a single bitmap data object. Think of this like merging layers in Photoshop. One advantage of this is that it is far more efficient to just have one item rendered to the display than lots of individual assets. As a result you can have more assets displayed than you would using normal display methods (using the DisplayList in Flash for example). Now this may sound odd. I know when I first had it explained to me many many years ago it sounded logical, but my brain just kept saying no…
For a more technical explanation of blitting, including its origins, check out its Wikipedia page: http://en.wikipedia.org/wiki/Bit_blit
When we, as Flash creative professionals, think of animation, we think of timelines and frames and multiple items on the display list at once. Blitting just converts that to a single image and each pass it updates the parts that have changed. In some respects it’s not too different than celluloid film used in the cinema in which you only have one image and each frame it updates.
Switching Sprites
Enough of the theory, let’s look at how to use a bitmap sprite instead of a vector sprite for our ‘hero’ first. Then we can look at adding a background image to complete the visual transformation from simple monochrome coloured vectors to richer raster based images (obviously the term “richer” is subjective so don’t shoot the messenger :P).
The first thing you’ll need is the source project from the previous article. You can grab that here. I’m using Flash Builder Burrito for these articles, mainly because I’ll be talking about devices and PBE in future articles. However, beyond the fact that the SDK and project settings files will be different you could easily create a new project in Flash Builder 4 and import the files if you prefer. I’m not using any Burrito specific code or anything specific to Flex SDK 4.5 at this point in time.
Currently the initializeHero() method looks like this:
private function initializeHero():void { var _hero :IEntity = allocateEntity(); var _spatial :SimpleSpatialComponent = new SimpleSpatialComponent(); _spatial.size = new Point(50, 50); _spatial.position = new Point(0, 0); _hero.addComponent(_spatial, "Spatial"); var _renderer :SimpleShapeRenderer = new SimpleShapeRenderer(); _renderer.fillColor = 0x000000; _renderer.fillAlpha = 1; _renderer.isSquare = true; _renderer.isCircle = false; _renderer.lineAlpha = 0; _renderer.scene = PBE.scene; _renderer.sizeProperty = new PropertyReference("@Spatial.size"); _renderer.positionProperty = new PropertyReference("@Spatial.position"); _hero.addComponent(_renderer, "Renderer"); var _input :KeyboardController = new KeyboardController(); _input.positionProperty = new PropertyReference("@Spatial.position"); _hero.addComponent(_input, "Controller"); _hero.initialize(); } |
First, remove the _renderer entry, since you no longer need the SimpleShapeRenderer. Instead you need to use a SpriteRenderer, which allows you to provide a reference to the image you want to load as the visual element of your entity. The ‘hero’ code now looks like this:
private function initializeHero():void { var _hero :IEntity = allocateEntity(); var _spatial :SimpleSpatialComponent = new SimpleSpatialComponent(); _spatial.size = new Point(50, 50); _spatial.position = new Point(0, 0); _hero.addComponent(_spatial, "Spatial"); var _renderer :SpriteRenderer = new SpriteRenderer(); _hero.addComponent(_renderer, "Renderer"); var _input :KeyboardController = new KeyboardController(); _input.positionProperty = new PropertyReference("@Spatial.position"); _hero.addComponent(_input, "Controller"); _hero.initialize(); } |
Now that you have the correct rendering component in place you need to wire it up with the spatial component and also tell it where to find the all important image file that will be displayed. I add the graphic first so that the renderer know what it is supposed to display. To do this you just need to set the fileName property on the SpriteRenderer instance. In this case it needs to point to the image file ufo.png located in the assets > sprites folder in the source zip that accompanies this article.
var _renderer :SpriteRenderer = new SpriteRenderer(); _renderer.fileName = "assets/sprites/ufo.png"; |
The last thing you need to do is link the renderer up to the spatial component like you did in the previous articles. The only property you need to link is the position property from the spatial component. You don’t need to worry about the size property at this point in time (I’ll discuss the boundaries of entities when I cover collision detection at a later date). Lastly you need to make sure it is added to the scene. Your completed code should now look like this:
private function initializeHero():void { var _hero :IEntity = allocateEntity(); var _spatial :SimpleSpatialComponent = new SimpleSpatialComponent(); _spatial.size = new Point(30, 30); _spatial.position = new Point(0, 0); _hero.addComponent(_spatial, "Spatial"); var _renderer :SpriteRenderer = new SpriteRenderer(); _renderer.fileName = "assets/sprites/ufo.png"; _renderer.layerIndex = 10; _renderer.scene = PBE.scene; _renderer.positionProperty = new PropertyReference("@Spatial.position"); _hero.addComponent(_renderer, "Renderer"); var _input :KeyboardController = new KeyboardController(); _input.positionProperty = new PropertyReference("@Spatial.position"); _hero.addComponent(_input, "Controller"); _hero.initialize(); } |
Run the game and you’ll see that the simple square hero from the previous examples is now a simple little UFO. However, because the background of the game is a mid grey the UFO unfortunately disappears into the scene. Don’t worry you’re going to address that next.
Adding A Background
Backgrounds can be added just like any other entity (the hero included), so the actual process of adding the background should be fairly obvious as it follows the same process you just employed to add the UFO graphic for your hero entity, with a couple of minor tweaks. First up, after the call to the initializeHero() method in the games constructor you need to add a call to a new method named initializeBackground(). Don’t worry about the fact it doesn’t exist yet. You’re going to create it next.
private function initializeBackground():void { var _bg :IEntity = allocateEntity(); var _spatial :SimpleSpatialComponent = new SimpleSpatialComponent(); _spatial.position = new Point(0, 0); _bg.addComponent(_spatial, "Spatial"); var _renderer :SpriteRenderer = new SpriteRenderer(); _renderer.fileName = "assets/sprites/starfield.png"; _renderer.layerIndex = 1; _renderer.scene = PBE.scene; _renderer.positionProperty = new PropertyReference("@Spatial.position"); _bg.addComponent(_renderer, "Renderer"); _bg.initialize(); } |
As you can see apart from the lack of a controller component it is structurally identical to the initializeHero() method. One thing I do want to draw your attention to is that it has a layerIndex value set on the renderer. This allows you to order how your entities are stacked visually; the lower the number the ‘further back’ they will be rendered. In the case of the background the layerIndex is set to 1 so it appears at the lowest order in the stack. However, this does mean that you need to update the initializeHero() method so that it too knows on what layer the renderer should be placed. To keep a fair amount of space between the background and the hero entity set its layerIndex to 10. That way if you decide to place items in a layer between the two, you can do so without having to update your hero entity.
Here is the completed code with the call to the initializeBackground() method and the enhancements to the hero renderer:
package { import com.flashgen.gaming.controllers.KeyboardController; import com.pblabs.engine.PBE; import com.pblabs.engine.entity.IEntity; import com.pblabs.engine.entity.PropertyReference; import com.pblabs.engine.entity.allocateEntity; import com.pblabs.rendering2D.SimpleSpatialComponent; import com.pblabs.rendering2D.SpriteRenderer; import com.pblabs.rendering2D.ui.SceneView; import flash.display.Sprite; import flash.geom.Point; [SWF(width="800", height="600", frameRate="30", backgroundColor="0xCCCCCC")] public class Main extends Sprite { public function Main() { PBE.startup(this); initializeScene(); initializeHero(); initializeBackground() } private function initializeBackground():void { var _bg :IEntity = allocateEntity(); var _spatial :SimpleSpatialComponent = new SimpleSpatialComponent(); _spatial.position = new Point(0, 0); _bg.addComponent(_spatial, "Spatial"); var _renderer :SpriteRenderer = new SpriteRenderer(); _renderer.fileName = "assets/sprites/starfield.png"; _renderer.layerIndex = 1; _renderer.scene = PBE.scene; _renderer.positionProperty = new PropertyReference("@Spatial.position"); _bg.addComponent(_renderer, "Renderer"); _bg.initialize(); } private function initializeHero():void { var _hero :IEntity = allocateEntity(); var _spatial :SimpleSpatialComponent = new SimpleSpatialComponent(); _spatial.size = new Point(50, 50); _spatial.position = new Point(0, 0); _hero.addComponent(_spatial, "Spatial"); var _renderer :SpriteRenderer = new SpriteRenderer(); _renderer.fileName = "assets/sprites/ufo.png"; _renderer.layerIndex = 10; _renderer.scene = PBE.scene; _renderer.positionProperty = new PropertyReference("@Spatial.position"); _hero.addComponent(_renderer, "Renderer"); var _input :KeyboardController = new KeyboardController(); _input.positionProperty = new PropertyReference("@Spatial.position"); _hero.addComponent(_input, "Controller"); _hero.initialize(); } private function initializeScene():void { var _sceneView :SceneView = new SceneView(); _sceneView.width = 800; _sceneView.height = 600; _sceneView.name = "MainView"; PBE.initializeScene(_sceneView); } } } |
That’s pretty much all you need to know about the basics of using raster based images as sprites within your Pushbutton Engine game. Next up I’ll be looking at how to work with sprite sheets and animating game entities.
Summary
In this article you saw how to remove the simple drawn vector shape for the hero entity and replace it with a bitmap graphic. You also saw how to add in a background image and how to adjust the stacking order (by using the layerIndex property on the renderer components for the hero and background entities) so that the hero sits above the background.
Related Files
PBE003_WorkingWithBitmapSprites.zip
One Comment