22nd May 2010
Minimising subjective download time - part 1
One of the things I tend to obsess about is the size of my game file. This matters particularly for online games because the bigger it is, the longer it will take to download. However, if you need to minimise the size of your swf file you can easily find that information already, so this time I'm not going to write about that. Instead, I'm going to assume you've already made your game as small as possible, and it's still large enough for the time required to download it to be a concern. What can you do to improve the issue?
Before I get in to this I should say that the example provided is built using flash 8, so it's AS2. Some things may be implemented differently in AS3, but the basic concepts should be applicable.
The average download speed is currently of the order of 2 megabits per second. This means a file should download on average at 1 megabyte every 4 seconds. For much of the casual game market, eight seconds is too long to wait for a 2 Meg game before indicating that anything is happening. But that's not even the complete story. At least half the audience will have a lower maximum download rate than that. Furthermore, if the server supplying the file is slow or the internet is congested, the file could take much longer than that to completely load, even for the better connected.
To counter this issue, the swf format is intended to 'stream'. Part of the file can be played while the rest of it is downloading. Unfortunately, this can have unpredictable results when assets are required before they are loaded.
The basic solution
The slightly boring approach generally adopted is the preloader.
This is stored at the start of the file, and when this has loaded you can display
a meter of some description, showing how much of the file remains to load.
Which is fine, as far as it goes.
So you - or your sponsor - decorate the screen with an image or what-not. I mention the sponsor here to point out that the preloader you use may not be entirely under your control. Many sponsors provide their branding as a preloader which you're then obliged to use. And the protocol by which files are transferred across the internet means that fetching the initial part of a file will generally be the slowest. So now the preloader takes a little time to load - during which time the player sees a blank screen. "High-throughput" players regularly see broken games which never load, so they are finely tuned to this pause and are at their most impatient. So if there is a slow connection, players start rating it low and posting comments like "0/5 doesn't load". Sheep then copy those ratings and your game is toast.
This is particularly disastrous since these ratings and comments will come in first - people actually playing the game won't have commented yet, so your game's chances on a portal can be destroyed very quickly indeed.
Beyond the lowest common denominator
What you need is a pre-preloader. This is merely a minimal loader which can
put something onscreen in the shortest time possible. One of the smallest ways
of doing this is a text-box saying 'loading' in a device font,
and code in an onEnterFrame event (or similar) which simply waits for the
frame with the preloader-proper to load. Since you want to show that it hasn't
just crashed, also include some animation - for example the number of bytes
already loaded in a second text box.
Using a device font is important, since any embedded characters will increase the delay before anything is displayed. It doesn't have to look pretty, it just has to show some sign of life if the game is downloaded over a slow connection. Under normal circumstances, it won't be displayed for long.
Now, what you may find is that before the first frame has loaded, the second
and subsequent frames have already been played. This is odd, since in theory
the first frame isn't executed until it has loaded.
What I believe happens is that the first frame is displayed as soon as the stage has loaded, regardless of whether any code on the frame has also done so. This code still gets executed, but if the subsequent frame doesn't have to load assets then it will be entered in the next onEnterFrame event. The flash-player stacks up the code in each frame, then executes them all, one after the other. If you have "stop();" on frame 1, you may find that actually it stops on frame 2 or beyond. If code on frame 2 replaces an onEnterFrame event in frame 1, then the first function will be overwritten, which may in the worst case lead to the game apparently failing to load.
If you've got placeholder frames eg. for mochiads then this can cause some strange bugs - in particular, failing to display your prepreloader. The solution is simply to arrange for some assets to load in the second frame; don't leave it empty.
I've made a demo of this, which includes a couple of pessimised bitmap graphics to bulk up the filesize for demonstration purposes.
This is what the frames look like, size-wise:
Frame # Frame Bytes Total Bytes Scene ------- ----------- ----------- ---------------- 1 347 347 Scene 1 2 258216 258563 (AS 2.0 Classes Export Frame) 3 423 258986 4 1 258987 5 1851589 2110576 6 611 2111187
- frame 1 is the pre-preloader.
- frame 2 is an assets frame for the preloader, or where you'd load classes if you're eg. using mochiads, which use classes in the preloader.
- frame 3 is the graphically intensive preloader (this is exaggerated - you probably shouldn't have a 250 kb preloader!)
- frame 4 is an empty frame (see below).
- frame 5 is an assets frame for the main game.
- frame 6 is where the game would go.
Just in case you're wondering, I made a tweaked version which displayed a test mochiad. This didn't increase the size of the first frame.
While initially testing the demo online, I found that the main preloader wasn't
displaying at all. After some experimentation, I discovered that during loading in the browser,
_framesloaded had a value one lower than at an equivalent point when tested locally.
Since in this case I had the main assets frame immediately following the preloader, the preloader wouldn't start until that had loaded, thus negating its point.
Obviously this is an extreme case - if you have a series of asset frames the preloader will still display, just a bit slower than it ought to. In any case, a simple workaround is to include an empty frame after your main preloader.
I find it fascinating that the two preloaders need opposite fixes to ensure that they work as intended. This sort of issue is probably why the basic preloader is so prevalent. But now you know the traps, you should be able to avoid them.
So the upshot is that the prepreloader can take effect almost immediately. If you want to view the source, you can download the .fla file in a zip archive.
So this is all well and good. You've got something displayed ASAP, and interesting
graphics up when they becomes available. If you're showing adverts, then you're also
using up some of the time (potentially at the cost of extending that time).
But why stop there? Why not use the streaming nature of Flash to its potential?
This is the subject of a follow-up article, see you then.