We recently had a one word review of our debut game Drop Dead Z by creator of FlashDevelop, Philippe Elsass. That one word was “smooth!”
That got me thinking about the steps I took to make sure the game runs as smooth and as optimised as possible. On reflection, I think these are some really helpful tips that may be able to help others. Most of this advice has already been given elsewhere and some of it is just good practice but I think all of it is worth mentioning.
This is a very Haxe/OpenFL specific method but it works; the benchmarks prove it. If drawTiles intimidates you then try other third party libraries that bridge the learning gap but still deliver on performance. I read somewhere that each call to drawTiles is the equivalent of drawing one bitmap to the screen. This brings me onto the next tip…
Sprite Sheets and Batch Rendering
As much as you can cram into one draw call, do it. Your graphics card will love you for it! As a result this means that you want to avoid swapping between source images as much as possible so try to pack everything into sprite sheets. Think about the order everything is drawn to the screen and arrange your sprite sheets accordingly. This Starling article (read from Painters Algorithm) sums up nicely how you should be arranging your sprite sheets. Once you have your sprites organised, render them in as few drawTiles calls as possible. Drop Dead Z probably has around 5-10 calls per frame to draw everything on the game screen. Rendering the whole game takes approximately the same time as rending 5-10 bitmaps. Smooth!
Creating and destroying objects takes time. When pressing play in Drop Dead Z for the first time in a session, you may notice that it pauses for a small moment while it builds the game in memory. That is fine. But if game objects are being created and destroyed throughout play, the game will slow down and sometimes become a little jerky while it thinks between frames. To get a full, stable 60fps you need to get all of your game logic done and your game rendered in less than 16ms. That’s about 25 times faster than it takes the average human to blink. If you can minimise object creation and garbage collection, you’re onto a winning formula. Drop Dead Z uses object pools so once a zombie is removed from play, it’s put to the back of the queue to be placed on the next building. No new zombies are created unless one is needed and the queue is empty. Even then, after they are created, they just join the cycle. Everything in the game works on this principle to keep object creation and garbage collection to a more manageable level.
Time based updates
If your game is running on less than ideal hardware, you can expect the frame rate to drop below your intended target. If your animations are done by moving an object a set amount each frame and the frame rate drops from 60fps down to 30fps, the object will move at half the speed. If the frame rate is unstable the game will look jerky and progress at varying speeds, making it frustrating and awkward to play. Basing your animations on time reduces this effect and can make games aimed at 60fps playable on less capable hardware. (Playable that is, not perfect.) Beyond doubt, even on high end devices, there will be things that make your frame rate unstable. Your phone my get a notification in the background, you may connect to a wireless network; it’s not all necessarily the fault of your code or even avoidable. What you can do is try to mitigate the effect on your game. By using time based animations, the amount an object moves is dependant on the time that’s passed between the current frame and the last. If processing a frame takes twice as long as it should then the game will skip forward keeping the flow of play consistent. Drop Dead Z uses time based animation for everything.
Separating game logic and rendering
If using the display list, this is usually done by the onEnterFrame loop; your code updates everything and then OpenFL / Flash renders everything. This is a useful tip to anyone using custom renderers – if your game uses some physics or collision detection that needs to be stepped multiple times per frame to prevent tunnelling, make sure you do all of this before rendering and don’t render anything until everything is in its final place. Only update the sections you need to update – there’s no point updating the animation code 5 times per frame if this doesn’t affect the physics. Drop Dead Z updates it’s physics in 16ms increments based on the amount of time that has passed. If rendering at 30fps the logic is still running at 60fps to keep everything working properly.
Tweened as opposed to frame-by-frame animations
I know this is not always possible but tweens can look miles better than frame-by-frame animations as the movement can be interpolated. The character from Drop Dead Z is built up of smaller static parts and these are tweened in a keyframe based animation. His animations will still look amazingly smooth in super slow motion because his movement can be split infinitely over time. This makes varying his running speed so much easier without eating up memory. Imagine having enough bitmap frames to make the player run smoothly in slow motion – he would need multiple sprite sheets of his own!
Avoid software rendering
In OpenFL, there are some things you can do that force the CPU to get involved with rendering your scene instead of leaving it to the GPU. If this happens then expect your game to crawl at about 5fps. Masks and filters are the usual culprits and there’s no easy way around this. If your filters can be applied once and cached then you should be OK but dynamic drop shadows are a big no. Rectangular masks can be simulated using scrollRect and this is enough for most uses but the easiest bet is just to design around the issue. Try and bake as many of your effects into your images as possible so your game has less to process at run time.
In summary, use drawTiles or the most optimised rendering method for your target platform. Try and reduce the amount of draw calls by batching as much as you can into each call. To do this use sprite sheets and be sure to optimise your asset organisation so images drawn consecutively come from the same sprite sheet. Use object pooling to reduce the time spent creating and destroying objects in memory. Always use time based animations as these are the most flexible and still work well even when not hitting your target frame rate. Finally avoid using masks and filters. Bake effects into your images and use scrollRect for rectangular masks.
I hope I have provided enough information here to be useful to others. There is probably plenty I’ve missed but the above worked well for Drop Dead Z and got us the “Smooth!” rating from Mr Elsass. If you haven’t played Drop Dead Z yet, you can get it on Android and Amazon. If you play it and like it please give us a good review.
Until next time,