goombas.org
menu
main
links
all items
rss feed

Topics:
coding
games
goombas.org
horror
judgement
life
minutiae
music
rants
software
fool.
 _-
 oO
 |/
/|
/ \
Ugh, stop twitching
swarm redux, engine only
coding Posted 2003-12-15 12:37:34 by Jim Crawford
I'm still working on the game that goes with this engine, but it's occurred to me that some people might be interested in just the engine portion of it, which is ready to be looked at and learned from, at least.

I've used test data converted from the previous iteration of Swarm, but it totally does not do the new engine justice. Details are available here, but in short, it's the most sophisticated 2D platformer engine I've ever seen ... mostly because the rest of the world moved on to 3D before consumer hardware was ready to support all the great features. But never mind that.

Here is a source download. Here is a Win32 binary and test data download. Here's a collection of DLLs that you probably don't have but will need to run the executable. Here are the details:

First, the tech stuff. It's about 2900 lines of C++. That probably doesn't sound like much, and actually it isn't, but bear in mind that I have an almost pathologically compact coding style (more code per screen means more I can see at once and less I have to keep in my head). I developed it in Win2K, using Visual Studio “.NET,” but it should theoretically be fairly portable, as it uses OpenGL and SDL for IO. It does use the Windows API, but only minimally.

Now, onto the fun stuff. The big advances on the graphical front, neither of which required much work from me, (thanks, OpenGL!) are using arbitrarily-sized stamps rather than tiles for graphics, and having full alpha channel support for all images. This allows for a very organic feel, with smooth-looking fake anti-aliasing.

My current plans are to develop the levels as enormous images, broken into, say, 256 x 256 pixel stamps. The engine isn't up to that at this point; for decent-sized worlds, it would have to be modified to support loading stamps on demand rather than loading them all at once. Not a big deal, I just haven't done it yet. I also haven't yet implemented the tool that would allow reasonable editing of images on the order of 16k x 16k pixels.

On the gameplay side of things, the physics is on par with any 2D engine I know of, though it falls far short of the rigid body dynamics and ragdoll physics of recent 3D games. The physicality of the world is defined as convex polygons, so arbitrary sloping floors work. Objects can push each other and carry along with them things that are resting on top of them. The pushing code is recursive, so objects can push rows of other objects (the carrying code doesn't need to be recursive to support stacks). Objects are limited in shape to axis-aligned rectangles, so infinite recursion should be impossible. Also, mass isn't taken into consideration.

On the code architecture side, every aspect of the code is subservient to the need to make adding and editing game object types as easy as possible. This means that I pushed all possible complexity out of the object definition code into the supporting libraries, even if that meant increasing code complexity overall.

Every physical object in the game is part of the physical object hierarchy, which has the physics code described above built-in, along with hooks for object/object and object/world interaction. A preprocessor builds the load and save methods for each object, so I don't have to waste my time writing a bunch of fwrite and fread calls for each data member, and also keep it synchronized when I add or remove data types. All that's necessary is adding tags in the comments to specify the default value for each data member, and a macro call to add the class to a registry, so the main loading function knows what load method to call when it sees a particular object name.

More interesting stuff:

  • Reference counting pointers. These are nothing new, but they're substantially easier than manual memory management and they're more efficient than garbage collection. Sadly, they don't work for data structures with circular references, but my code doesn't have any of those anyways. I think.
  • All the file formats that aren't already standards (.png files, e.g.) are stored as LISP-style s-expressions -- that is, parenthesized, nested lists. These are functionally equivalent to XML, but easier to implement and less verbose. The format is not, unfortunately, as compatible as XML, even though it's been around for longer.
  • The engine is not tick-based. The physics code handles an arbitrary amount of time between frames, passed as a float. This was a big pain in the ass to implement, but I feel that it's necessary to provide the appearance of smooth motion under different screen refresh rates.
  • Objects and polygons are placed into spatial partitions for scalability. Objects can only interact with other objects or polygons sharing the same partition or partitions.
  • Input logging makes bug reproduction a cinch. If I can reproduce a bug, I'm 90% of the way to fixing it.

Damn. I realize this is starting to read like ad copy, but I swear, I didn't realize just how great this code is until I started writing this document :)

[link to this] [See more on “coding”]

comments
swarm engine
Posted by Anonymous (Craig Timpany) on 2003-12-16 16:40:54
Sounds fantastic. I can't wait to read that code.

At what stage is the game you're writing for this engine?
re: swarm engine
Posted by Jim Crawford on 2003-12-16 21:56:53
 Sounds fantastic. I can't wait to read that code.


Let me know what you think of it.

 At what stage is the game you're writing for this engine?


Mostly high-level design. I've got an art style in mind: basically Seiklus, but zoomed in to the scale of Super Mario bros, using thicker, anti-aliased lines. The low-level gameplay won't change much from the previous versions of Swarm (or Super Mario Bros 3, if you prefer to think of it that way), and the high-level gameplay will probably be more inspired by Super Mario 64.

I know, I know, another collect-a-thon. But if I ever get this thing done, I'm going to have spent a ridiculous amount of time building the worlds, and they won't really amount to that much in terms of surface area. I won't want people to finish the game in half an hour.

There's a story, too. I don't want to spoil it, but in short, Swarmie is the head chef of a magical palace. He's disgraced by Smarmie, his arch enemy, and is thrown in the dungons. He must use all his chefly platforming skills to chef his way back out, through some of video gaming's most popular climates, and reveal the truth to the populace.

Shit, I think I just about spoiled everything there.

Yes, I intend to work actual cooking-related gameplay into there somehow.
cooking gameplay
Posted by Anonymous (Craig Timpany) on 2003-12-21 18:39:33
I'm still reading the code, there's quite a bit packed in there.


 Yes, I intend to work actual cooking-related gameplay into there somehow.


Every game I've seen where the designer has tried to do that, the cooking gameplay has been confined to a mini-game. It doesn't have to be this way!

There are lots of ways you can put cooking-themed play into the main game:

Dice up food-themed monsters, collect their pieces and get bonuses for palatable combinations.

Or, the game allows you to pick up objects and dead monsters like in SMB2. It's difficult to defeat monsters in direct confrontation, but they can be distracted by feeding them food or having them fed to something else. The game has many puzzles built around this food chain.

Or, have edible monsters/items/terrain and a fearsomely hungry main character. The game is a race against the character's dwindling food meter. There's a trade-off where player control becomes slightly less responsive and more inertial as more food is taken on board. Players must check the food map at the beginning of the level to plan a route that will avoid starvation.

And so on.
plumbers, chefs, bi-plane pilots
Posted by Anonymous (Craig Timpany) on 2003-12-23 16:24:34
Seems you're not the only one who sees chefs as the logical progression from plumbers:

Superstar Chefs
http://www.arcadelab.com/chefs.html
cooking plumbers
Posted by Jim Crawford on 2003-12-23 20:14:39
 There are lots of ways you can put cooking-themed play into the main game

[good stuff]


Good stuff. I'll probably incorporate some of those suggestions into it. I have some ideas for the feeding enemies, but I don't know if I'm up for designing something as intricate as a food chain.

 Superstar Chefs


I knew I should've gone with my sister's suggestion of electricians. They could travel through wires just like Mario and Luigi travel through pipes!

Well, I tried Superstar Chefs. I wanted to keep playing when the 30 minute trial period ran out, but it ain't worth $20.

Mini-review, which will probably help noone: the gameplay is competent, but ill-tuned. The graphics are nice, but the art design is uninspired. The animation is extremely smooth, but could really use antialiasing. There are a good number of different enemies, but they pretty much all behave identically. The music, uh, blows.

I bet the 2-player mode would be a blast. Still not worth $20, unless that includes a friend with whom to play it.

And the story really brings back the good old days of Engrish in video games. "OH MY DEAR! SOMEONE HAS STOLEN ALL OUR RECIPES WE JUST GOT TO GET THEM BACK!"

That kind of stuff really has a lot of personality, which is something I can't say for most current video game writing, however impeccable the spelling and grammar may be.
re: cooking plumbers
Posted by Anonymous (Craig Timpany) on 2003-12-25 13:41:13
 I don't know if I'm up for designing something as intricate as a food chain


That's the beauty of it, you don't do much design at all. You just make a big mess and call it 'emergent gameplay'. :)

 I knew I should've gone with my sister's suggestion of electricians. They could travel through wires just like Mario and Luigi travel through pipes!


Or vets, so they can travel through animals!

Wait, that's been done.

 Superstar Chefs


I was just pointing out their choice of player-characters. I admit I didn't actually download it myself, I just looked at the screenshots and concluded "oh, it's one of those".

Look on the bright side, all you need to do to be widely seen as original is to be the first person to carry out an idea successfully.
e.g. Xerox Star vs Apple Macintosh.
the code
Posted by Anonymous (Craig Timpany) on 2003-12-25 14:51:59
It's very clever stuff and quite an advertisement for people to go out and rewrite their code twice. The input logging, smart pointers, and automatically generated load/save code are particularly futuristic parts.

Your collision partition implementation is the cleanest I've ever seen. When I tried to code one, I wanted to avoid duplicating an object pointer across multiple partitions to avoid problems when the object gets destroyed. What you seem to be doing is putting pointers in all the partitions the object's in, and then using another data structure to look up where an object has all its pointers.

It's a much much better way to do it. I should've realised I would need to search the partitions both spatially and by a particular object.

I really should use also vectors for positions in mojotron 2. I dithered about it but then decided it wouldn't help much, and now I've changed my mind again. You're getting a lot of use out of them, and if I have a proper data type, I can encapsulate my high resolution position (in 1/256 pixel units) and give it an interface that deals in pixels or pixels per second, and avoid having all those conversions through my code.

Interesting choice of things to template. 2D vectors get templated, collision partitions get templated, object factory methods get a homemade #define. It seems to work just as well, but I don't see what lead to your choice.

You've managed to do serialisation with very little code. It's quite spooky. What kind of restrictions need to be followed in order for BLS to read your headers?

Your timekeeping code still mystifies me. Why floating point time deltas? Why are they running averages?

And why are collisions kept in a data structure instead of being processed as they're found? Is this to avoid possible duplicate collision events?

(These questions shouldn't be read as constructive criticism or suggestions, they're just bits I didn't quite get.)
re: the code
Posted by Jim Crawford on 2003-12-25 22:40:14
Thanks. I'm so happy with this code, it helps to be sure that I'm not just fooling myself :)

 Your collision partition implementation is the cleanest I've ever seen.


The interface is clean, but I am worried about the efficiency of the internal implementation. Of course, it runs fine on my system, and I can't actually check it easily, because Visual Studio .NET doesn't come with a goddamn profiler.

 Interesting choice of things to template. 2D vectors get templated, collision partitions get templated, object factory methods get a homemade #define. It seems to work just as well, but I don't see what lead to your choice.


I learned how to do templates pretty late in the game, probably about halfway into this project, so I don't claim to have that good a sense of what to template and what not to. I don't think that the object registration macro is something that could be done with a template, though.

 You've managed to do serialisation with very little code.


Yeah, almost as little as if the language were introspective :)

 What kind of restrictions need to be followed in order for BLS to read your headers?


It counts class [classname]:public [parentclass] as the start of a class and }; as the end. Either of these delimiters must be the first non-whitespace data on its line in order to register.

Between class delimiters, it looks for lines that contain the string set:. On lines do, it interprets the first two tokens on the line as the data type and the identifier. It'll check if it's a single-dimensional array, but won't bother looking for any additional dimensions.

The whitespace handling inside each line is fairly flexible, so aside from the array limitation, it's not that restrictive. If you share my particular coding style, at least :)

 Your timekeeping code still mystifies me. Why floating point time deltas? Why are they running averages?


The averaging is specifically to keep it from running too jerkily, but the timekeeping code is mostly placeholder code right now. I'm waiting on the real thing until the knowledge of how to query for the screen refresh rate falls into my lap. (I'm not actively looking for that information mostly because it doesn't sound like fun.)

 And why are collisions kept in a data structure instead of being processed as they're found? Is this to avoid possible duplicate collision events?


That, and also because I wanted to allow an object's reaction to touching or being touched to modify its velocity without screwing up the collision detection loop.

Speaking of Mojotron 2, how's that coming along?
mojotron 2
Posted by Anonymous (Craig Timpany) on 2004-01-02 02:22:29
 That, and also because I wanted to allow an object's reaction to touching or being touched to modify its velocity without screwing up the collision detection loop.


Uh, how would this screw up the collision detection loop? I don't think I'm familiar with that problem.

 Speaking of Mojotron 2, how's that coming along?


I haven't done much work on it in the last few months.

Lately I've been haunted by game concepts akin to Deus Ex and System Shock. I disagree with the some of game mechanics that've been cut and extended in the progression from System Shock 1 to System Shock 2 to Deus Ex to Deus Ex 2. I've been spending time coding a little test bed (just turn based and text only) for my gameplay ideas.

As for Mojotron 2, I went and overextended myself. This always seems to happen with hobby projects shortly after I get them to basic playability. I immediately start chucking in features without stopping to fix the bugs.

The shit really hits the fan when I code something that screws up the heap. I really am wondering if I've missed out on some crucial insights into memory management because substantial bits of my coding experience have been in Java.

Sooner or later I'll tidy it up.
re: mojotron 2
Posted by Jim Crawford on 2004-01-02 10:14:24
 
 That, and also because I wanted to allow an object's reaction to touching or being touched to modify its velocity without screwing up the collision detection loop.


Uh, how would this screw up the collision detection loop? I don't think I'm familiar with that problem.


I was vague because I couldn't think of any examples, actually :) Now that you ask, and I've tried a bit harder, I can't think of anything that would break for certain, but, of course, I don't have my mind as wrapped around the design of the physics engine as I did when I was developing it.

The engine did go through several significant revamps to fix little niggly issues, it's very possible that one of them made the defer unnecessary. But I'm still reluctant to undo it, because the code was always very tense. I just have vague misgivings about springboard-like objects pushing against each other, and bad stuff happening.

Now I'm getting disillusioned about my code again :)

 Lately I've been haunted by game concepts akin to Deus Ex and System Shock. I disagree with the some of game mechanics that've been cut and extended in the progression from System Shock 1 to System Shock 2 to Deus Ex to Deus Ex 2. I've been spending time coding a little test bed (just turn based and text only) for my gameplay ideas.


Interesting... want to talk about it, or wait until you have something to show? Or maybe we should take this exchange to email.

 The shit really hits the fan when I code something that screws up the heap. I really am wondering if I've missed out on some crucial insights into memory management because substantial bits of my coding experience have been in Java.


Yeah. No matter how carefully you encapsulate your modules to avoid exponential interaction possibilities, it all goes out the window when you get a buffer overrun. (I'm assuming you're not talking about a bad or missing call to free() or something else that might screw up the heap :)

My debugging process hasn't changed in years: it's a loose binary search for the point in execution time where the data being manipulated is different from what I expect. Working with a debugger doesn't change this, it just favors a slightly different tuning. The only two problems with the process are working with data for which it's not easy to visually determine whether or not it's right, and buffer overruns. I have no good process for debugging buffer overruns.

I haven't found any in the current Swarm engine, however, and it's the biggest single C or C++ project I've worked on. I believe that the reason for this is that I simply didn't have to deal with pointer arithmetic. Much.

I did use it for the list parsing code, because by god, that code was cleaner than the equivalent high-level C++ would've been. But other than that, there isn't much in the way of bare pointers in Swarm at all. I use reference counted pointers, and I dip into the STL for various list structures. Except where actually necessary, I don't quite go so far as to use iterators, because the syntax makes my eyes bleed, but the iteration loops are generally very simple.

I don't know how much of this was dumb luck that I didn't need to deal with data structures that don't hold up well under this style of implementation, but you can bet I noticed.

Moral of the story: if you use pointers or arrays, don't do that! As much as I adore C and think it's the best language evar, I wouldn't want to actually work with it in a project of any size.

Hm. It would be cool if a high-level language such as Python supported inline C the way I used to drop inline assembler into my Turbo C code... but I think that really only worked because assembler and C have the same data model, whereas I'm sure Python "int"s have all sorts of metadata associated with them.

Oh yeah, and the compiled vs. interpreted thing, that too.
re: mojotron 2
Posted by Anonymous (Craig Timpany) on 2004-01-14 23:35:42
 Want to talk about it, or wait until you have something to show?


If I make something fun, I'll let you guys know. /me gestures to the studio audience.

 (I'm assuming you're not talking about a bad or missing call to free() or something else that might screw up the heap :)


For me it's not so much buffer overruns, it's double deleting objects.

My new year's resolution is to use auto_ptr and references where appropriate instead of making everything a pointer. The more things that are cleaned up when they go out of scope, the less I hurt myself trying to do it manually.
re: mojotron 2
Posted by Jim Crawford on 2004-01-21 15:20:12
 For me it's not so much buffer overruns, it's double deleting objects.

My new year's resolution is to use auto_ptr and references where appropriate instead of making everything a pointer.


auto_ptr isn't as useful as it seems at first. No reference counting means you can't pass it around between functions.

Reference counted pointers are the way to go. Or garbage collection, I guess.
add a comment
Only anonymous comments are available for now until I get the user system up and running again. Not many people were logging in anyway, so enh.
Permitted HTML tags: <b>, <i>, <u>, <tt>. Also permitted is the <q> pseudo-tag which is meant to delimit quotes from other messages.
name:
email:
subject:
body:
Preview
To prove you are sentient, please type "sentient" into this box

what's this?
This is Jim Crawford's blog. Details and contact information.

On Twitter: @mogwai_poet

recent comments
Packers and movers in Gurgaon (Anonymous on may 2014 microblog digest)
QuickBooks 24/7 Support Phone Number (Anonymous on may 2013 microblog digest)
no subject (Anonymous on may 2013 microblog digest)
no subject (Anonymous on boxing: a history)
QuickBooks Technical Support Phone Number @1844-640-1482
(Anonymous on may 2013 microblog digest)
phone number for quickbooks support (Anonymous on may 2013 microblog digest)
no subject (Anonymous on boxing: a history)
Accountancy (Anonymous on may 2013 microblog digest)
QuickBooks Technician (Anonymous on may 2013 microblog digest)
no subject (Anonymous on boxing: a history)
quickbooks support (Anonymous on may 2013 microblog digest)
Loved the game (Anonymous on boxing: a history)
no subject (Anonymous on may 2013 microblog digest)
no subject (Anonymous on may 2013 microblog digest)
Aol Tech Support Number (Anonymous on may 2013 microblog digest)
Comments RSS