Monochrome Programming: Finite State Machines, Repeated Code, and Version Control

PROGRAMMING PART 3

The first time anyone played Monochrome, then called Squares Game, the game would simply start up as soon as it was launched, and the user had better be prepared to instantly start dodging things. That’s ridiculous of course, and so I was going to have to add some menus. What’s more, back then I had some vague ideas about multiple game modes and I knew that I would need some way to create this in code, without a bunch of massive if statements.

ENUMERATED VALUES

If it seems like I’ve been hard of C++, that’s only because I believe that object orientated programming is massively oversold, but C++ isn’t just C with classes. One of the most useful C++ features is called enumerated values. Basically an enumerated type, or enum, is a set of integer values that are defined in english, or basically a set of named integer variables. That might not be clear so imagine instead:

enum GameState{
INTRO=0, MENU, CTF, BATTLE, CUTSCENE, DEBUG, QUIT
};

These are all integer values, and all one greater than the previous value. The use of enums is that instead of writing code like this:

int state;  //initialized somewhere

if(state==0) PlayIntro();
if(state==1)Menu();
if(state==2) PlayCTF();
if(state==3)PlayBATTLE();
if(state==4)PlayCutscenes();
…and so on

Instead we can write code like this:

GameState st;  //initialized somewhere

if(st==INTRO)PlayIntro();
if(st==MENU)Menu();
if(st==CTF)PlayCTF();
if(st==BATTLE)PlayBattle();
if(st==CUTSCENE)PlayCutscenes();
…and so on.

This might not sound so great, but we also get to change the gamestate to obvious things, and won’t accidentally change the state to the incorrect state. Imagine trying to hook up the buttons in a menu to the correct change of the menu or gamestate. We’d have to look up what each value does before changing our state variable. With a MenuState enum, if they press the CREDITS button, then we just make our enum CREDITS, such as menSt=CREDITS.

Someone might point out that in C, nothing is stopping us from just manually creating a bunch of values like this:

const int INTRO=0;
const int MENU=1;
const int CTF=2;
…and so on.

I would agree then, that the benefits of enumerated types for finite state machines is fairly limited, but I still found it to be clearly better, albeit slightly, than what can be written in pure C.

FINITE STATE MACHINES

Back when I was adding the first menu to the game, which was just white with a big PLAY button right in the middle, I could have gone about it in at least two different ways. The first way would be to use if statements such as:

int state=0;

if(state==0){
//do menu things, which include changing the state
//load in menu textures
//place buttons in the right area
//play background music
}else if (state==1){   //do gameplay things, which includes changing the state.
//load gameplay textures
//initialize all the characters
//handle movement
//…
}

There is one absolutely huge problem with this: it doesn’t let you put any of the code in different files. Since this is all part of the same if-else statement, we can’t seperate it out. What we can do is use finite state machines, which are especially useful when combined with an enumerated type that stores the state of the game, or menu, or what have you.

Basically, how it works is that instead of a gigantic wall of code dominated by simple state checks, we just do the same state checks, but change the code inside to a function that takes parameters, one of which is the state.

GameState st=MENU;

//I could use if-else statements, but switch statements seemed better in the actual code
while(!quitGame){
switch(st){
case MENU: Menu(&st); break;
case GAME: PlayGame(&st); break;
}
}

Of course, in the above code this will run forever because we have no way of changing quitGame, but I hope this gets the idea across. We run our program and we get to the switch statement and from there we either go into the Menu() function, or the PlayGame() function. These two functions are totally seperate from each other, and are only linked because each one can change the state from itself to the other.

The Menu() function will probably have a switch statement all of it’s own, which controls which screen we’re on. It might look like this:

MenuState menSt=TITLE;  //possibly passed into the Menu() function

while(*pSt==MENU){
switch(menSt){
case TITLE: TitleScreen(&menSt, pSt); break;
case CREDITS: CreditsScreen(&menSt); break;
case OUTRO: OutroScreen(&menSt); break;
}
}

The actual TitleScreen function will look something like this:

TitleScreen(MenuState* pMnSt, GameState* pSt)
{
//Load in textures, sound effects, music etcetera
while(*pMnSt==TITLE && *pSt==MENU){
//run logic, checking for user pressing the buttons
//change states accordingly, example:
if(UserPressesCreditsButton) *pMnSt=CREDITS;
if(UserPressesPlayButton) *pSt=PLAY;
//render everything
}
//Free up all allocated resources
return;
}

And that’s how we can use states to cleanly wall off our program, having only the linking that is necessary for functionality. It does force the programmer to be smart about how they design the states, because scope can be a problem, and passing in the same variables from three levels up can be tedious.

One of the major mistakes I made with this project was not having the MENU state be a superior state to all other states. It needn’t be called MENU even, just having one major controller state would do the trick. The problem was that I couldn’t change things in the MENU that affected the game, such as difficulty or starting level, without first creating them in Main(). This all lead to a much more cluttered and ugly Main() function than I wanted, as well as passing in too many variable pointers. In the future, I’ll just make a function called MainMenu() that will be called from the Main() function. It will control all gamestates beneath it.

REPEATED CODE

One of the major problems with using finite state machines occured mainly because of multiple game modes. Menus were mostly fine, although setting up an event loop is kind of tedious after a while, but the game modes had quite a bit of duplicate code that could have been reduced.

To start with, since the different gameplay modes all use the same images and sound effects, I should have just loaded them all into a struct* and passed a pointer to that struct, or constant copy, into the right function, such as PlayCTF(&media). However, after that there isn’t a lot that can be reasonably done with finite state machines. Since a lot of the duplicate code is in rendering things, or doing mouse-related functions, or doing collision detection, these would probably all be better served by being made into their own functions, than to build states that specifically handled the different code in between all of this. This is a hard problem to solve optimally.

*I detailed creating a Media struct in a previous post.

VERSION CONTROL

Far and away the biggest change that will be made to my next project is version control, also known as source control, or revision control. Version control is sort of like going from a text editor that never lets the user undo anything, to a text editor that does. Actually, that’s a bad example. Version control is more like going from a text editor that lets the user undo and redo quite smoothly, to a text editor that stores multiple copies of the document the user is working on, from a variety of different dates.

That doesn’t sound all that useful to someone who’s writing the typical school essay, or letter to someone. It sounds immediately useful to someone writing a novel, or a 30 page essay, or using legal documents, or really anything that is both large and has been worked on over time.

Code is definitely something that benefits from version control, and it is something that I started wanting more and more the longer I worked on Monochrome. One of the largest saps of my productivity was when I had to weigh the pros and cons of making a change to the code, for refactoring purposes, or design purposes, or whatever. If it’s very hard to just flip back to the previous version, then it introduces more fear when coding, and provides more justification for not changing something that really ought to be changed. This was a boat anchor on my productivity, both for re-writing code that worked, and for tweaking or outright changing the design of the game to something better.

Unfortunately Code::Blocks, the IDE that I’m using, does not automatically support version control. I need to download some plugins, which is sort of dicey. Regardless, this is just something I have to do, not valid arguments against it.

This entry was posted in Monochrome, Programming and tagged , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s