(Backported from Steam Greenlight.)
We came up with this wily fellow. I spent way too much time fighting with this guy’s animations, but I’m quite happy as to how he came out.
We’ve still got a few more enemies planned for in the next release; more enemies for the swamps are definitely going to be a prime focus. This fellow gets us that much closer. 🙂
The biggest thing I’ve been working on recently has been a system to do compound sprites. Compound sprites are when you use multiple sprites to create the appearance of a single, larger object – in the old days of sega/nintendo, these were just used for size, since even seemingly ‘small’ characters like mario or sonic were actually a multiple of the native square-sprite size of the machine they ran on. These days, we don’t have any meaningful size limitations on sprites, so instead we use compound sprites for positioning or animation. For example, a clock face would easily be done with one sprite for the dial, and separate sprite for the hands – the purpose being that if the clock-hand sprites are separate, you can freely rotate them, programmatically, meaning you can have a full, 360° of rotation without any new drawings being necessary on the artist’s part.
We’ve wanted to have these in the game for quite a while; technically, we’ve been able to do them since the early alpha versions of our engine (Anura), but in practice they’ve been a buggy mess. The system I’ve come up with is no holy grail (there are a number of fancy animation-scripting things that still have to be done by hand), but it’s very, very clean, and should stay fairly bug-free. One of our main goals was to have these sprites, to the player’s eye, seem like a seamless extension of the core object they were a part of. Any damage-contact applied to any part of these should act like they’re one, indivisible object; any visual effects like color-flashing should affect the entire thing, without any inter-frame delays. Chiefly, we’re also worried about orphaning these child objects – if the core enemy they’re attached to, dies, we need to have a reliable system for getting rid of them.
Bug-free designs are really our top priority at this point; we had some compound enemies in the past and I’ve actually ended up mothballing a few of them because it was too much work to keep them running as they broke over and over due to other changes in the game. A tough lesson I’ve learned in game-design has been to resist the siren song of new features that I can’t quite come up with a rigorous way to handle; they might not take any longer to prototype than anything else, but I can’t treat a working prototype as a “bird in the hand”. I have to treat the real cost of features as the full, lifecycle cost of all the future bugs its going to incur as it interfaces with other elements in the game. Thus, I need to focus on things that have a way lower lifecycle cost – which is usually down to ‘devil in the details’ planning of how they work. It’s less about what I do, so much as it is about holding off unless I’ve got a really clean way to do something. There are plenty of “cool 2d platformer tropes” we’re leaving on the shelf unless they can meet this requirement, but the result is we’ll actually ship the damn game, so it’s a small price to pay.
As for the new compound-object system; there are several specific things that finally made it viable. Most of it had to do with some major, structural improvements to our “hittable” class (a long, ongoing project over the last year or so). Most of these have been down to a transition away from a type/recipient-blind “event-driven” system, and instead moving to a pure-functional, strict typed means of triggering behavior. A lot of this has been enabled by some huge upgrades to Anura’s scripting language, making it strictly typed – most especially with regards to non-null containers, and object access – we now can have hard, static-analysis guarantees that if we’re operating on some set of links to other objects (like, say, compound object parts) that we’re guaranteed to be pointing at a real, object, and not something that had been deallocated earlier. A big benefit here is that our hittable code has a very limited set of entry points; we’ve got one place where hit-triggering behavior gets captured, and this can safely redirect calls for being hit to a parent object (and thus make sure the full suite of damage-receiving code gets triggered correctly).
Hopefully this will bulletproof the few enemies we have based on compound objects, and let us gradually ease into doing more in the future.