gpu

Day 6 – Mobile Game Progress

Its been a few more days now and quite a bit of progress has been made so I thought I would update. First, have a go at the game in its current form above (use the keyboard to control).

Since last time I have concentrated on the parts I was less technically confident about such as the correct rendering method of animated sprites and how I was going to handle the rotation of the world.

First I tackled rendering. In my previous update the tiles in the world rendered simply by drawing vector squares to Sprite.graphics, then shifting their container to move them. When I tried to run this on an actual mobile device however the performance was extremely poor, less than 1FPS. The reason for this was because drawing vectors on mobile devices doesn’t really fit well into their GPU-orientated graphics pipeline. To get decent frame rates on mobile I needed to find a way to take advantage of the mobile GPU.

Fortunately for me those clever guys who wrote NME were way ahead of me and developed the “Tilesheet.drawTiles()” API:

  1. extern class Tilesheet
  2. {
  3. static var TILE_SCALE:Int;
  4. static var TILE_ROTATION:Int;
  5. static var TILE_RGB:Int;
  6. static var TILE_ALPHA:Int;
  7. static var TILE_TRANS_2x2:Int;
  8.  
  9. static var TILE_BLEND_NORMAL:Int;
  10. static var TILE_BLEND_ADD:Int;
  11.  
  12. function new(inImage:BitmapData):Void;
  13. function addTileRect(rectangle:Rectangle, centerPoint:Point = null):Void;
  14.  
  15. /**
  16. * Fast method to draw a batch of tiles using a Tilesheet
  17. *
  18. * The input array accepts the x, y and tile ID for each tile you wish to draw.
  19. * For example, an array of [ 0, 0, 0, 10, 10, 1 ] would draw tile 0 to (0, 0) and
  20. * tile 1 to (10, 10)
  21. *
  22. * You can also set flags for TILE_SCALE, TILE_ROTATION, TILE_RGB and
  23. * TILE_ALPHA.
  24. *
  25. * Depending on which flags are active, this is the full order of the array:
  26. *
  27. * [ x, y, tile ID, scale, rotation, red, green, blue, alpha, x, y ... ]
  28. *
  29. * @param graphics The nme.display.Graphics object to use for drawing
  30. * @param tileData An array of all position, ID and optional values for use in drawing
  31. * @param smooth (Optional) Whether drawn tiles should be smoothed (Default: false)
  32. * @param flags (Optional) Flags to enable scale, rotation, RGB and/or alpha when drawing (Default: 0)
  33. */
  34. function drawTiles (graphics:Graphics, tileData:Array<Float>, smooth:Bool = false, flags:Int = 0):Void;
  35. }

With it you pass an array of tile data with a number of optional properties such as Rotation, Alpha and Scale. It then does the heavy lifting behind the scenes of building a vertex buffer and sending it to the GPU for rendering with your Tilesheet texture. This results in two big wins for performance; 1) you can render a great many sprites on the GPU in the same render, 2) you dont perform any expensive texture switches if all your sprites are on the same texture.

As for the tilesheet itself. At first I was planning on using a manual method of putting the sprites onto the tilesheet such that each sprite was the same size and we arranged in a very simplistic “one row per animation” fashion:

The problem with this however was that it was going to be a fairly inefficient way of organising the assets and it was going to be tricky for Moh (the artist) to build these animations frame by frame and put them on there while keeping them looking good.

Fortunately for me this problem has already been encountered by those before me. Philippe Elsass (of Flash Develop fame) has written a library called TileLayer that solves two problems. Firstly it abstracts out the some of the hard work of using the low-level drawTiles() API into a more friendly parent-child-like syntax which should be very familiar to any flash developer. Secondly it provides a parser for “Sparrow” spritesheets.

Sparrow is a library for game development in pure Objective-C. Part of that library is a method for loading tightly packed, animated spritesheets. These spritesheets can be generated by a number of tools but the best one I found was Texture Packer (by Lee Brimelow Andreas Löw):

Using the tool you can take a number of input’s such as SWFs or other images and output a compact spritesheet and an XML document containing the data NME will use to render the sprites on the screen.

One problem I encountered with using Texture Packer with SWFs however was that for it to work you must do all your animating on the timeline, it wasn’t smart enough to populate named sprites from the library. Another thing to remember is that you must arrange your animations from the top left of the stage. If you put the animation in the middle of the stage it will be offset when it is rendered in the game. Not big problems but it did cause some head-scratching for a while before I realised what was going on.

With my sprites now loading and animating correctly in flash I decided to have a go at getting the game to run on mobile devices. Thanks to the way NME works it was a pretty simple process to generate a template project “nme update application.nmml ios” then run the generated xcode project file. And it runs! The only problems was that the frame rate was around 15FPS on my iPhone 4, which was far lower than I was hoping for.

So I started thinking about what could be causing a slow down. In flash when you try to render objects that are off-screen, flash automatically works out if it needs to be rendered and if not then it skips the render and hence improves the performance. I had a suspicion that Tilelayer / NME couldnt perform this operation so all the tiles that weren’t on the screen were being unnecessarily drawn to an off-screen buffer and hence lowering the performance.

Because in this game the player is always in the centre of the screen I knew that a crude way to calculate whether a tile was on screen was to simply check the distance of the tile from the player, if it was greater than the width + height of the screen (because the world rotates) then I knew it would be off-screen and thus didn’t need to be rendered:

As suspected this had a marked improvement in FPS, the game was now running on my iPhone at about 30FPS. An improvement but still short of what I was hoping for.

After some more experimentation I discovered another problem. In the code for checking whether to render a tile or not I was doing the following:

  1. public function update(delta:Int) : Void
  2. {
  3. var dx = x - game.player.x;
  4. var dy = y - game.player.y;
  5. visible = animated = (dx * dx) + (dy * dy) < game.root.stage.stageWidth * game.root.stage.stageHeight;
  6. }

The problem is that apparently the call to “stage.stageWidth” and “stage.stageHeight” is very expensive. I presume behind the scenes NME is making an expensive call to the device for width and height information.

Once I took out those calls (cacheing them in the game object instead) and tested it again on the device the FPS was now up to more what I was hoping for at 60FPS :)

The final technical hurdle was to rotate the world. I was worried about this one as I was contemplating all the mathematical calculations that would be needed to work out the rotation for each tile in the game. Fortunately however there was a better solution. Because NME is based on the flash API I was able to put the world within a container Sprite, then offset the container by the player’s position – screenH/2 and screenW/2 then rotate the container thus giving the illusion of the world rotating.

To my relief this idea actually worked and even more incredibly it didn’t seem to affect the frame rate! See the video below of it running one my iPhone 4, iPad 3 and my old iPhone 3G.

The 3G will need some more work to make it run at acceptable framerates, but im impressed it even runs on that thing, you cant do this with Adobe’s Stage3D!

Well that enough of me talking about this for now, I need to get on with making the damn thing! Next up is some more game-play elements and perhaps some menu structure.

GPU State Preserving Particle Systems with WebGL & HaXe

Well this is the post I didnt think was going to happen. I have been struggling for weeks with this little bit of tech, ill explain more about why it has been so difficult in another post. For now however, ill just talk about this sample.

So the idea was to build upon what I had been working with previously with my stateless particles systems with WebGL and HaXe. The intention from the start was to replicate some of my very early work (from 2007) on state preserving particle systems in WebGL.

Before I go any further, you can check it out in action here:
http://mikecann.co.uk/projects/HaxeWebGLParticles/ 

First a quick reminder. The difference between a stateless and state-preserving particle simulation is that in the latter we store and update the positions, velocities and other properties of each particle per frame, allowing us to interact and control the simulation. This differs from the stateless particle simulation (detailed in my previous post), where the position for each particle is calculated each frame based on a fixed algorithm.

A fairly reccent addition to WebGL made this possible, namely texture lookups in the vertex shader (aka Vertex Texture Fetch). I wont go over the exact details how this makes state preserving particle systems possible as I have already documented it in my earlier work. A brief explanation is that it allows you to use the fragment shader to perform the updates on particle state stored in textures then use the vertex shader to map those states to a point-sprite vertex buffer.

Basically what this means is that the entire particle simulation can be contained and updated on the GPU, which means no read-back. This allows us to achieve simulations of millions of particles without too much difficulty (depending on GPU ofcourse).

I have uploaded the source for people to perouse at their leisure:
https://github.com/mikecann/HaxeWebGLParticles

As usual it was written using the JS target of HaXe so it should be fairly easy to understand whats going on if you have written any Ecma-script-like-language. Im going to detail this in my next post, but the code isnt the best I have ever written as its a result of a mish-mash of various samples and examples I have found on the web. If anyone has any comments on things that are wrong or could be done better I would be very happy to hear about them.

Assembly 2010 – CNCD & Fairlight Demo

It was the massive annual Assembly gathering last week. Traditionally its a place for the demo scene to gather and present their demos and to hack out new ones.

For those not in the know the demo scene or “scene” is where a team of people come together and compete to make a music video. Except that they arent videos in the traditional sense as its all generated using code and runs in realtime. This means that you have to be super efficient with your code. Some of the competitions are “64k” or even “4k” competitions which means that the whole demo must fit in 64kb or 4kb! The music is also produced by the groups and often excellent.

I have been checking out some of the demos produced by the teams this year and they are simply stunning. Every year they get better and better.

This one from CNCD and Fairlight particularly caught my eye as it has massive numbers of particles and a stunning soundtrack kinda reminiscent of glitchy Radiohead. Therefore I thought it essential I share it here.

Enjoy:

Oh while im at it check out this one from last year:

If you are interested in more demos from assembly you can get them all here: http://scene.org/newfiles.php?dayint=7&dir=/parties/2010/assembly10/

Oh I have also posted about the demoscene before here: http://mikecann.co.uk/art/andromeda-software-development-lifeforce-2007/

 Scroll to top