Hello! Time to dissect the application I’ve mentioned several times already: Cruz.
Long before starting Cruz, I had built many, many software applications. Some used sophisticated requirements, design, and configuration management techniques. Others, not so much. This one falls into the “not so much” camp.
But Cruz, like many other applications I have built, has very sophisticated programming. With me, that’s just the way I do things. I take pride in my programming skills, and I will go the extra mile to develop to a high level the engine of any game I create.
Cruz is a Flash game. Many Flash games are rich in graphical and sound content, but they lack programming sophistication. Inability to program forces many designers to act at the mercy of whatever tools or engines others have built, which further limits the types of games they can create. Could a graphical designer who knows little about programming create something like Cruz? Well, read on, and see for yourself.
System Components
There are six main components of the Flash application:
- Flash file (Cruz.swf)
- Graphical content (embedded in Flash file in this case)
- Audio content (MP3s loaded at run time)
- Level design data (XML files in “Cruz” format)
- Server-side scripts and storage (PHP and text files)
- Client-side savegame, progress, and config info (Shared objects)
The Flash file is loaded first, usually as an embedded object in a web browser, with graphical content loaded automatically. The audio content is loaded in two contexts: the sound effects at the beginning (a preloader precedes the title screen), and the musical tracks on an as-needed basis.
The level design data is loaded on an as-needed basis per episode, which is only when the user starts a game for a particular episode. The server-side scripts are used when handling high score update and retrieval operations. The savegame, progress, and config info are periodically updated and saved by the Flash environment on the client side.
If you’re building a Flash application, there’s a good chance you won’t need to touch the XML API, or write server-side scripts, or use shared objects. You might not even need a preloader, choosing to embed everything in the SWF. This keeps everything simple, but it limits what you can do, programming-wise.
If you’re building a game designed to give the user a full experience of rich content, savegames, and high scores with competition from everyone in the world, the “web application” way to do it is substantially different from the “home PC” way to do it or the “game system” way to do it. Different APIs, different components, different implementations of components.
But the underlying programming effectively does the same stuff, no matter what the platform is. You have load operations, save operations, ins, outs, etc. Business logic is business logic in any language.
Classes and Programming
What about the programming for Cruz? Well, as managed applications go, there is little to the imagination. Let’s look at what Cruz implements for unique classes:
- utils: Low-level utility functions, such as sign, absolute value, random chance, etc.
- IPoint: Coordinate pair of integers (x and y).
- Dir8: Eight-directional calculation functions and lookups; useful for 2-D gridded games.
- FxCont: Field effect container class (a rectangular region with specific game properties).
- objid: Enumerated constant and name lookup container for game object types.
- fxid: Field effect and level feature lookups.
- SF: Special flags-per-grid-cell storage and lookup.
- SaveGame: Represents savegame data.
- CruzOptions: Represents configurable options for the game.
- Game_Characters: Storage and functionality for ASCII-noir characters used in background text.
- RandInfoArray: Grid randomization functions.
- CruzMedal: Storage and functionality for medals (unlockable achievements found in various parts of the game).
- Sounds: Storage and functionality for sound effects and music.
- PathCalc: Mouse click-to-move path calculation functions.
- MsgParser: Special object message parsing class; used with tablets and special items.
- GPlayer: The main character’s object (the player).
- iobj: Individual game objects, capable of interaction. A single GPlayer individual is always present as the first individual.
- rtobj: Real-time object, representing a short-lived, animated object in the game that does not interact with anything.
- cruzparse: XML parsing functions for translating level design content into game data.
- game: The main gameplay class. This covers most general gameplay business logic and functions.
- cruz: The main interfacial class, controlling Flash-specific API operations and API callbacks, such as user input, timer, load complete, etc.
If I were writing in C# for a PC game, the class layout would look the same. If I were writing in C++ for a game console, the class layout would look the same.
With three exceptions, that is: enumerated constants, polymorphism, and server-side handling.
Enumerated constant definitions will look a bit different across languages. The differences across C++ and Java alone are pretty substantial. Enumerated constants are not directly supported in ActionScript, not even in AS3. If you want to make a constant, you must declare it as a named static constant in a class. I ended up doing this for fxid, objid, and SF classes, to name a few.
ActionScript does not support virtual functions. Even if you derive from a base class, you must cascade functions manually at every level of function implementation. This is annoying. For this reason, some objects, like iobj, were not broken into individual subclasses.
Server-side scripts are necessary for Flash applications that want to maintain a central data repository; there is no dedicated client-side API for handling server-side data. The PHP scripts are very simple. All that the scripts do is take HTTP requests from the Flash application and fire back resources at the application. Just a little text file cooking, that’s it.
Special considerations
As far as the class implementation is concerned…well, it’s here where software engineers earn their pay. I have been programming for well over a decade. It takes skill to know how to properly use arrays, vectors, integers, floating point numbers, and strings. A surgeon can show off the anatomy of a rat, but he can’t tell the lay observer how to sew the rat up so that he instantly knows how to make it live and breathe again.
So I won’t go into the detailed design and cover every function in Cruz. Not today, at least. But I will discuss a very important aspect of programming for web applications: respecting the fact that what you are creating is a web application.
Decades ago, we never would have dreamed that so many of these web applications would conduct advanced calculations in the background. That’s what a client-side web application usually is: a background application. It can be an advertisement, or a game, or a slideshow, or a fundamental user interface needed to enter data into a form. Regardless of what the application is used for, it should be expected that the user’s computer will be doing other things than paying every bit of attention to running your web application.
This means that slack needs to be built into timing routines that might have demanded precision otherwise. Synchronization of animation frames and timer ticks is not guaranteed. CPU performance could be drained by a computer function not even remotely related to the web browser or its applications. The user might destroy your application’s operating environment at any time by closing the window, forcing you to update shared objects or call server-side scripts at key moments to avoid data loss.
Oh, and one final hiccup: the gargantuan task of making Cruz, a cutting-edge netted AS3 application, resemble the gameplay and timing mechanisms of Kroz, a non-netted Pascal application, written over 20 years earlier without any event-driven programming.
Now you know what went into Cruz. Web applications aren’t necessarily easier or harder to program than general-purpose applications–they’re just different.
But in some ways, they’re the same as ever.
