As Krystal Bytes team reached the development phase of the Citadel Arcanes game, I decided to bring in my experience in the project by building its framework. At start I was somewhat worried I might have gone rusty and all, but it all started flowing naturally once I got my hands dirty. I guess when you enjoy something so much, it just doesn't leave you over time. I must say AS3 is quite a dream language for every multimedia coder: runs almost everywhere, lots of great animation functions, garbage collector, OOP, etc... Although having managed a few AS3 projects in the past, I knew it has quite a few weaknesses and so I tried to risk-manage these early on. Here is how I tackled a few of these problems.
1. Game flow: Scenario Manager, Game screens and Overlays
In any software developpement project, it is crucial to find a good architecture that helps keep things clean while not getting in the way. Of course OOP introduced with AS3 goes a long way regarding that matter, but that is still not enough, especially for something as complicated as a game.
Experienced developpers know it is a good practice to slice the game into independant "screens" or "phases", so this is what I wanted to have at the core of the framework. This kind of decoupling is essential to insure you won't have too many impacts and regressions when you are fixing bugs. Another important aspect is how the game flows betweens those screens, and in a game it is also quite usual to reuse screen, unlock access, go in a special mode, etc. depending on your progress. I therefore imagined a structure where a sequence of screens could be scripted, and parameters could be passed on to each screen. A single XML File - the "Scenario" - is read early on by the game and used to know when and how to launch each of the screens.
Building on this, I ended up having 4 important elements :
Screen: a single main gameplay phase, a "view" in the game, launching a new screen closes the previous one
Overlay: a view that is displayed over the screen but that doesn't have a strong coupling to it. ie Dialogs, popups etc.
Trigger: an event that can be passed between the scenario, the screen and its overlays
Sequence: a sequential series of screens and overlays, also used as a container to limit the range of a trigger
This structure proved quite effective as all these were compiled in a ScenarioManager that became the maestro of the game, with the following benefits
- Screens and Overlays are highly uncoupled, and can therefore be reused easily
- Because the current screen is closed with all its overlays when you are opening a new one, it makes it easy to identify and fix memory leaks (yes, even with a Garbage collector you can have memory leaks!)
- the game flow can be easily scripted in the Scenario.xml file, making it extremely agile to modify the game behaviour depending on the player progress in the game, and reuse screens with various parameters
In the end this approach even if it not as rigorous as a true MVC, revealed quite practical. In fact having the hands so deep in the engine made it possible to tackle structural problems easily, while keeping top performance.
2. Asset loader
Another difficulty was to handle asset loading in the game. Clearly AS3 is an online-oriented language and so is its loading system. On the good side, its dynamic loading capabilities are excellent, but on the bad side, you have to take care of a lot yourself with a bunch of different classes depending on what you are loading. One of the common pitfalls is that loading is asynchronous, which brings in more complexity when you are trying to connect it with a central maestro system as described above.
So the idea behind the asset loader was to surrender control to a mass-loading system that would ensure everything necessary is there when a screen starts.
Eventually, as we decided to launch the game offline (PC/MAC casual download version), we ended up loading everything upfront according to a mass-loading script built with an Air tool. This tool is also calculating MD5 and file size to display correct loading progress and ensure we get the correct version of the file.
The system proved invaluable as it could also load classes in external SWF, which was great to speed up integration of interfaces and animated clips (therefore decoupling graphic work from code).
In the end we also had problems with simultaneous file loads (we had a static parameters to handle how many files could be loaded simultaneously), but it seemed the flash player just couldn't handle so many (1200) file loads. We had mysterious crashes and "white screens" that Adobe is not intending to fix it seems. To work around this problem I made a script to package all images into a single swf, reducing the filecount to 300. This also proved to speed up loading times considerably !
So this works all good now, but there are still a few features that I'd like to implement when I get a chance
- requirement based loading: the scren declares upfront what it needs to run and a small loading happens before the screen is launched where only the necessary assets are loaded
- dynamical unloading: When the screen is closed, each asset is decreased making it "unnecessary" when it reaches 0. The system can then dispose of these assets if it needs RAM
- review the class loading system to be iOS and stage3D compatible: unfortunately currently it seems like it is not possible to bind anything with a linkage name in the iOS Air Packager (you get an error message when the app runs on iPhone/iPad). This is most certainly due to Apple security restrictions as it would then be easy to inject malicious code in the App. There are known workarounds for all these, but it really feels like this should be included in any good AS3 gaming framework & toolset. As for stage3D, well everything nedds to be texture and therefore bitmap image on this layer.
These optimizations would be quite essential to port the game on mobile devices.
3. Performance manager
Probably the most well known weakness of flash is that it is slow compared to other high level languages (in the pre-stage3D era at least). Even with its hardware acceleration and Just-In-Time compiler, it remains far from reaching the speed of C++/OpenGL for example. This is probably why most of the "real" (as they like to name themselves ^^) programers don't want to learn or use AS3.
Starting with this in mind, I refused to admit that we could not do something about it, and woud be forced early on to aim at mediocre performances whatever happens. This idea found its echo in the performance manager. This system monitors framerate on the fly and switches between 4 performance levels (LOW, MEDIUM, HIGH, VERY_HIGH) to adapt frame rate and game effects dynamically. Every Screen is just probing this global class to know if it is adequate to run a special effect and if not. In other words, the system will simply drop anything unnecessary on the fly, with various degrees of priority.
We were quite surprised with the results of this system, as this really made it possible to keep decent performances on old machines.
Of course we also spent a lot of time deploying good old game development tricks like caching images as bitmap (automatically or by hand), creating bitmap animations for particle systems, etc.