
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:
- 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. :)
- Secondly, in order to compile for Apple computers, you need to compile with "THINKSTUPID" defined. I lol'ed heartily about that.
- Thus far, I have not found any useful comments. They're all either licencing info or commented-out code. Too bad. :/
- 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.
- 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)
That very well could have been just:
{
#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
}void loadstorylevel(int levelnum) {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.
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'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.
- 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...
- 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... ;)