TunnelTanks 0.4

Well, I've been pretty consistently working on TunnelTanks over the last two weeks, and I've actually gotten quite a bit done. Here's a summary of the changes, adapted from the CHANGELOG file:

  • Switched from a custom python script for configuration to a CMake/CPack-based build/packaging system, which can build binary tar.bz2's, deb's, and rpm's.
  • Improved Twitch's self-preservation instincts. He is now smart enough to try to return home when he is low on energy/health. ('Try' being the operative word. He can get stuck on level details...)
  • All code is now managed by the Bazaar VCS.
  • Made it possible for there to be multiple level generators, and added 2 new ones: 'simple' and 'maze'. The original one is named 'toast'. Better name coming soon. :)
  • Added fullscreen capabilities. Just run tunneltanks with "--fullscreen" to start that way, or hit 'F10' at any point to toggle into fullscreen.
  • Added a means for an AI to query the surrounding level. Twitch makes very basic use of this functionality, but it'll be far more useful for more complex AIs.
  • With the '--large' CLA, you can have the level generators make even BIGGER maps. (1500x750 vs 1000x500, which is 2.25x more pixels!)
  • Improved command-line parsing. Just run with "--help" to see all the new options.
  • Many other bugfixes.

There's a lot of new stuff there, but my favorite is the ability to add new level generators. Here are some sample levels from the current set of built-in level generators:

This is a sample 'simple' maze:

Sample simple maze

This maze started out as a way to accurately mimic the level style of the original game, since the original game will generate a rock wall sort-of like this, and then sprinkle the outer border with random rock outcroppings. Sadly, all attempts to reasonably capture this style fell short, so I instead worked mostly on generating the rock border, so that the original game's "fight in the middle" mechanic could at least be preserved. Sadly, it'd be difficult to perfectly replicate the original game's level generator without the original game's source code, since it has lots of quirks that are unique to it...

The second maze generator is the 'maze' generator. This was probably the most fun level generator to work on. :)

Sample maze map

The maze generator uses a Randomized DFS maze-generator, or as it's called on http://www.astrolog.org/labyrnth/algrithm.htm, the "Recursive Backtracker." It's fast, it's effective, and I really like the mazes that it generates. And also, check that page out. That guy is a huge maze fan, and he really knows his maze generating techniques. Interesting read... :)

And of course, we still have the 'toast' generator.

Sample toast map

I only called that generator 'toast' since I couldn't think of a better name. If you have a better name, do feel free to tell me. :)

That's about it. I am now keeping info on the project, including instructions on how to clone the Bazaar repository at: http://www.poweredbytoast.com/tunneltanks

Since I changed to CMake, the build instructions have also changed to:

cmake . && make && ./tunneltanks

If you wish to install this project, you COULD do a 'make install', but it'd be far cooler to use the new packaging targets. For example, do a 'make package-deb' to make a deb file, that can be installed through your package manager. (You can also do 'make package-tbz2' for binary tarballs, or 'make package-rpm' for an rpm file.)

Source: http://www.poweredbytoast.com/files/tunneltanks-0.4.tar.bz2 (GPLv3)

Enjoy! ;)

TunnelTanks 0.3

Hey there, everyone! Happy Thanksgiving! I'm just about to nod off to sleep, but before I do, I just want to get one little blog post in...

Well, after a very, VERY long delay, I'm releasing TunnelTanks 0.3. You know TunnelTanks? That game I was working on several months ago? Before I got the Nexus One, which got me all interested in the Android Framework and the JNI? Ring any bells? No? Oh, well.

So, for the uninitiated, way back when, I was being all nostalgic about an old DOS game called Tunneler that my sister and I used to play on my mom's old IBM laptop. Well, after tinkering around with cave-generating functions, I decided to start writing this game for myself using SDL, and name the subsequent project TunnelTanks. So, after a long hiatus, I've rediscovered the source code directory, and got back to work. Here are some of the new features:

Screenshot of TunnelTanks 0.3

  • Bases have been added, so every tank has a place that they can call home.
  • Energy and health is now tracked by the game engine, allowing things to die. (Finally!) Also, both energy and health can be replenished at your base, but only energy can be replenished at enemy bases. (And slowly at that.)
  • Fixed a noticeable bug, where small bits of the level could be erased by getting really close to something, and then turning towards dirt.
  • Lots of code improvements, such as changing the level generator from the DDA algorithm to Bresenham's algorithm, which makes the program almost completely float-free! (The only thing left that uses a floating point variable is the single line of code that prints how long it took to render the level.)
  • When a tank is low on power, its display will start glitching out.
  • All tanks now have their own, unique color schemes. So, be sure to tell me if some of the colors are awkward... I am slightly colorblind, and my definition of a decent color match is often wildly different from everyone else's. :)
  • Other code cleanups, and bug fixes...

Sadly, our beloved Twitch AI isn't faring well in the new version... you see, poor Twitch has no concept of direction, no ability to see enemies, no means of checking its energy levels, and no self-preservation instincts whatsoever. So, most Twitches will wander off and die within the first few minutes. The Twitches who live are just lucky/stupid enough to not even be able to escape their bases. (Which is both sad and funny at the same time.) I plan on extending the controller API, so that a controller can get some more info on a tank, allowing for smarter AIs in the future. However, that's an adventure for another day.

And there we go! I hope to keep on this project more diligently in the coming weeks. The game controls are now:

BLUE Tank: ASDW (movement) + LEFT CTRL (firing)
GREEN Tank: Arrow Keys (movement) + '/' Key (firing)

The controls were changed to dodge hardware limits on my laptop keyboard. (Using Enter to fire with the green tank meant that you couldn't move up and left at the same time...)

To build, just do a simple:

./configure && make && ./tunneltanks

You will need a Python interpreter to configure, GNU Make to compile, and the SDL libraries to run, so be sure you have all of those.

Source is here: http://www.poweredbytoast.com/files/tunneltanks3.tar.bz2 (GPLv3)

Have fun! :)

Gish Source

Gish Screenshot

Now that the Gish source code has been posted, it's time to actually look at this thing. Here are some of my notes while chugging through this thing quickly:

  1. Firstly, this project is in C, and not C++. C tends to be faster and more portable, but large game project tend to go with C++, so this surprised me, but in a good way. :)
  2. Secondly, in order to compile for Apple computers, you need to compile with "THINKSTUPID" defined. I lol'ed heartily about that.
  3. Thus far, I have not found any useful comments. They're all either licencing info or commented-out code. Too bad. :/
  4. This code uses OpenGL all over the place, not just in one neat file. This sucks, because in order for someone to port this to the Pandora, they'll have to go file-by-file through out this project, converting the glBegin()/glEnd() code to OpenGL ES's vertex buffers.
  5. Lots of the code REEKS of copy-and-paste, and magic numbers are littered throughout the code. This results in functions that are hundreds or thousands of lines long, with HUGE if-blocks that span nearly the entire function. Plus, many of the other functions could have been written WAY nicer. Consider loadstorylevel() from setup.c:
    void loadstorylevel(int levelnum)
    {
    #ifndef DEMO
    if (levelnum==1)
    loadlevel("sewer1.lvl");
    if (levelnum==2)
    loadlevel("sewer2.lvl");
    if (levelnum==3)
    loadlevel("sewer3.lvl");
    if (levelnum==4)
    loadlevel("sewer4.lvl");
    if (levelnum==5)
    loadlevel("sewer5.lvl");
    if (levelnum==6)
    loadlevel("sewer6.lvl");
    if (levelnum==7)
    loadlevel("sewer7.lvl");

    if (levelnum==8)
    loadlevel("cave1.lvl");
    if (levelnum==9)
    loadlevel("cave2.lvl");
    if (levelnum==10)
    loadlevel("cave3.lvl");
    if (levelnum==11)
    loadlevel("cave4.lvl");
    if (levelnum==12)
    loadlevel("cave5.lvl");
    if (levelnum==13)
    loadlevel("cave6.lvl");
    if (levelnum==14)
    loadlevel("cave7.lvl");

    if (levelnum==15)
    loadlevel("hell1.lvl");
    if (levelnum==16)
    loadlevel("hell2.lvl");
    if (levelnum==17)
    loadlevel("hell3.lvl");
    if (levelnum==18)
    loadlevel("hell4.lvl");
    if (levelnum==19)
    loadlevel("hell5.lvl");
    if (levelnum==20)
    loadlevel("hell6.lvl");
    if (levelnum==21)
    loadlevel("hell7.lvl");

    if (levelnum==22)
    loadlevel("egypt1.lvl");
    if (levelnum==23)
    loadlevel("egypt2.lvl");
    if (levelnum==24)
    loadlevel("egypt3.lvl");
    if (levelnum==25)
    loadlevel("egypt4.lvl");
    if (levelnum==26)
    loadlevel("egypt5.lvl");
    if (levelnum==27)
    loadlevel("egypt6.lvl");
    if (levelnum==28)
    loadlevel("egypt7.lvl");

    if (levelnum==29)
    loadlevel("church1.lvl");
    if (levelnum==30)
    loadlevel("church2.lvl");
    if (levelnum==31)
    loadlevel("church3.lvl");
    if (levelnum==32)
    loadlevel("church4.lvl");
    if (levelnum==33)
    loadlevel("church5.lvl");
    if (levelnum==34)
    loadlevel("church6.lvl");
    if (levelnum==64)
    loadlevel("sewer8.lvl");
    if (levelnum==65)
    loadlevel("cave8.lvl");
    if (levelnum==66)
    loadlevel("egypt8.lvl");
    if (levelnum==67)
    loadlevel("death.lvl");
    if (levelnum==68)
    loadlevel("death2.lvl");
    #else
    if (levelnum==1)
    loadlevel("demo1.lvl");
    if (levelnum==2)
    loadlevel("demo2.lvl");
    if (levelnum==3)
    loadlevel("demo3.lvl");
    if (levelnum==4)
    loadlevel("demo4.lvl");
    if (levelnum==5)
    loadlevel("demo5.lvl");
    #endif
    }
    That very well could have been just:
    void loadstorylevel(int levelnum) {
    char buf[16];

    assert(levelnum>0);

    #ifndef DEMO
    if (levelnum<=7 ) sprintf(buf, "sewer%d.lvl", levelnum);
    else if(levelnum<=14) sprintf(buf, "cave%d.lvl", levelnum-7);
    else if(levelnum<=21) sprintf(buf, "hell%d.lvl", levelnum-14);
    else if(levelnum<=28) sprintf(buf, "egypt%d.lvl", levelnum-21);
    else if(levelnum<=34) sprintf(buf, "church%d.lvl",levelnum-28);

    // All of the super-secret bonus levels are special cases:
    else if(levelnum==64) sprintf(buf, "sewer8.lvl");
    else if(levelnum==65) sprintf(buf, "cave8.lvl");
    else if(levelnum==66) sprintf(buf, "egypt8.lvl");
    else if(levelnum==67) sprintf(buf, "death.lvl");
    else if(levelnum==68) sprintf(buf, "death2.lvl");

    else assert(0);

    #else
    if (levelnum<=5) sprintf(buf, "demo%d.lvl", levelnum);
    else assert(0);

    #endif

    loadlevel(buf);
    }
    I know it really doesn't matter; the code does the same thing, but the code on the bottom is shorter and easier to read.
  6. I'm seeing all kinds of code that relies on chdir()'s, and other functions that force the program to be run out of the same directory as the executable. Plus, the high-scores, and game saves go into that same directory as well, and replays get saved into a replay directory. That type of code may be cool on Windows, but it's going to need some rewriting if we ever want to see a Gish *.deb, or *.rpm.
  7. Check out animation.c if you dare. 1 function. 1337 lines of code. That's a cool number for it to be, but O.O at the actual function...
  8. Finally, the reason I dove into this code in the first place... the physics code. The physics code is surprisingly simple. Gish himself is merely a bundle of unbreakable bonds and springs. (See: createtarboy() in object.c), as are all of the dynamic blocks and such. From what I can tell, the internals of their physics engine is actually incredibly simple. It will simply go through all dynamic objects subject for collision, and will then compare that block to every other dynamic block for collision handling. Each block will compare the level separately from the blocks, separately from the particles, and so on. With this engine, each physics update has a O(n2) running time, where n is the number of dynamic objects in the level. This simple approach seems to have worked well for Gish, but probably won't work for other projects, so don't try to use the Gish physics engine in other projects... It would probably be best to just stick with more advanced 2d physics libraries, like Box2d for C++, and Chipmunk for C. (I'm personally a huge fan of Chipmunk, and I used it in that Tetris Hell game I posted a short while back.)

One final note: this list may seem a little harsh at points, but I don't want it to be. I know that I have coded some pretty horrible things when I've been under pressure, and I don't want to act like I'm above committing my own personal coding sins. I am completely thrilled that the Gish code is now free under the GPL, and I hope more projects do the same!

That said, rewriting a few bits couldn't hurt... ;)

Tetris Hell

The other day, XKCD came out with a nifty little comic about Hell. I thought that this was so funny, that I whipped up a game that acted like that over the next day or so. And so here it is.

Hell screenshot

The game has no real purpose: there's no way to win it and there's no score. However, the game ends when the pieces pile up high enough, and it uses chipmunk physics to give a realistic Tetris experience. ;)

The game requires SDL and OpenGL. It comes with the latest version of Chipmunk, so you don't need to go get that. Just cd into the directory you unzipped the source to, and run 'make'. (Use 'make -j3' to make it build quicker on a dual-core machine...)

Controls are:
Left / Right - Move tetris piece left/right.
Up / Down - Rotate the piece clockwise / counter-clockwise.
Space - Slam the piece down.

License: GPLv3
Source: http://poweredbytoast.com/files/hell.tar.bz2

TunnelTanks 0.2 alpha

Loads of changes in this release. There are now 8 tanks in the world, 2 of which are within your control. The rest of the tanks are controlled by the game's first AI. (Although the 'I' in 'AI' is being used VERY loosely...) The AI's name is Twitch, and its movement is entirely random. Not a serious AI, but it's fun to watch. :)

TunnelTanks 0.2 screenshot

Other things done:

  • Shooting while digging will now let you travel through dirt at the same speed as if you were driving in an open area, like in the original game.
  • Controller code has been abstracted into a newer API, which will make developing AIs and alternative control systems (like joystick or network controllers) easier.
  • Fixed a bug where it's possible for a tank to be spawned in an area that can't be reached by tanks in other areas.
  • Collision detection works so you can run into and shoot other tanks. Nobody dies yet, but that's coming soon. :)
  • The program now prints out the random seed that was chosen by the 'rand_seed' function. (Which uses both time() and /dev/urandom as sources.) You can then manually hand that seed back in via the command line, so that the level generator will generate the exact same level. This will be removed for the later versions, but it's currently useful for debugging...
  • The 'Random Growth' phase of the level generator has been rewritten to use a more sophisticated algorithm, so that only cells that are up for consideration get processed. This has decreased the total time needed to make a level to .14 seconds. According to gprof, the next function that should be optimized is the smoothing operation, which currently takes ~50% of the total processing time. Here's to hope that we can get this thing to under 0.1 seconds!

Next up: Bases and health/energy. Then, maybe I'll take a legitimate swing at the AI, and not just a 'Twitch'... ;)

Controls:

Blue Tank: ASDW + LCtrl
Green Tank: Arrow Keys + Enter
Red Tanks: AI controlled. Just watch. :)

Source Code: tunneltanks2.tar.bz2

 1 2 Next →

About

User