Page 1 of 1

Data Modeling for Games in C Part I Part I: Enumerations Rate Topic: -----

#1 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2247
  • View blog
  • Posts: 9,237
  • Joined: 18-February 07

Post icon  Posted 15 April 2007 - 08:23 AM

Data Modeling for Games in C
by NickDMax
April 15, 2007

Foreword

This tutorial evolved out of a discussion in the Game Programming Forum. That is why it is a tutorial for C and not a discussion of data modeling in C++ using classes. That is not to say that those budding young C++ game developers can't learn a thing or two here. Rather, the ideas presented here can be powerfully extended from structures to classes in the C++ language. To quote my earlier post:

"Normally I would point you in the direction of classes and C++, but since you are using C, lets keep it complicated and talk about data modeling [in C]."

Introduction

When we first begin programming most of us write linear programs. The program starts at the top and works its way down to the bottom. As times goes by we learn to use functions to make our programs modular. This is a huge step that allows us to reuse code over and over. We can now take these functions and use them as building blocks for larger and more complex programs. This tutorial is about another tool that will help simplify your logic as well as give you better building blocks for creating programs (specifically in this tutorial: games).

The main goal of this tutorial is to mask powerful programming techniques in the language and examples of game development. I do not want the reader to think that these techniques apply only to games, nor are they unique to C/C++. These techniques can be extended to most languages and most if not all applications. However, it is important to note that the purpose of data modeling is to make you program and logic simpler, and thus by extension allowing you to create more complex programs with less effort. It is possible to get carried away by data modeling (as it is with modular programming and object oriented design) so that your program development environment is FAR more complicated than it needs to be. So remember this:

Begin design on PAPER (figuratively if need be). Before you begin to code, model your data using diagrams, and other visualizations (such as storyboards). Get a firm feel for the direction you want to go. Keep development concerns in mind; think about what you will have to do to code these models. Once all of your models are coded, it can be difficult to make drastic changes.

Data Modeling

One of the big problems with beginner game programming is the need for TONS of variables to keep track of all the data. There are variables that makeup the player's stats, there are variables that make up the players location in a map, there are variables that determine attacks, and defenses, and missions, and items, and inventories, and maps, and this, and that. The more involved a game is, the more data it needs to keep track of.

Data Modeling is about grouping this data into structures in memory so that all of the data used to describe any single entity in the program can grouped into a single block of memory that we can pass around to our modular functions, and therefore greatly reduce the complexity of our programs. These structures also give us new building blocks on which to build bigger and better, more complex games.

Enumerations

One kind of data that is ubiquitous in game programming is "type" or "kind". For example what kind of weapon is that, a sword? What kind of sword: short sword, long sword. My, what a big gun you have there, is that an ion gun or a lazer gun? WOW! What an ugly looking character you have made, is it an ogre, or woodland elf, or troll; is it male for female? This sort of data is simplified by creating an enumeration of all of the various "types" of an object.

Enumerations have the syntax:

enum [tag-name] {
ITEM [= seed value],
ITEM1,
ITEM2,

ITEMn [= new seed value],

ITEMm
};


They began my making the identifier ITEM equal to either zero, or a seed value. Then they count (enumerating the identifiers) upwards assigning ITEM1=ITEM+1, ITEM2=ITEM1+1, etc. You can at any time assign a new seed value, and it will continue to count up from that point.

Enumerations can be a powerful programming tool:

---------- Enum1.C: Incomplete code stub ----------

enum CharacterRace
{
HUMAN=0, //You can specify a starting number (default is zero
ELF, // but I like to make it explicit).
OGRE,
TROLL,
HALFLING,
GIANT
};

enum SwordType
{
SWORD=0, //General use hunting sword.
LONGSWORD, //35 inches or more, large handle, cruciform hilt. Hacking weapon.
SHORTSWORD, //Small, but larger than a dagger.
BROADSWORD, //Shorter than a longsword, thick blade for extra momentum. Hacking weapon.
RAPIER, //Light, fast, long, slashing/stabbing weapon.
FOIL //Very light, very fast, whipping/stabbing weapon.
};

//By using the typedef keyword we can make our enum into a datatype
// (although it is really an int, which we should always keep in mind).

typedef enum {MALE=0, FEMALE} CharacterSex;

//We can now use CharacterSex as a datatype
CharacterSex Player1Sex;

//If we had not used the "typedef" method we would have to
// use the the enum like this

enum CharacterRace Player1Race;

//Now we can use this information in our code with lines like:
Player1Sex = MALE; //Player1 is now a Male, Congats its a boy!

if (Player1Race == TROLL || Player1Race == GIANT)
{
printf("The small group of bandits flees at the sight of you.");
}

if (Player1Race==OGRE && Player1Sex==FEMALE)
{
printf("The baby ogre thinks that you are its mother!!");
} else
{
printf("The creature attacks you.");
}
---------- END Enum1.C ----------


Another form of data, though less popular than enumerated types, are attribute fields (a.k.a. enum flags, or bit fields). Lets us suppose that our game allows for mixed races. Although we could declare two or more variables to hold Player1’s race there is a way that we can combine it all into one variable. To do this we will make use of binary and declare the enumeration as:

enum CharacterRace
{
HUMAN=1, //Each item will be alligned to a bit value in binary.
ELF=2, //this way they do not overlap in memory.
OGRE=4,
TROLL=8,
HALFLING=16,
GIANT=32
};


Because each attribute lies upon its own bit we can create composite races by adding two or more races. For example a half human, half giant would be, "HUMAN + GIANT". Since we are dealing directly with binary data most programmers prefer to use the “or” operator rather than addition so they would write, "HUMAN | GIANT". Due to their binary nature attribute fields lend themselves nicely to the binary operators (AND &, OR |, XOR ^, and NOT ~). Although they can be a bit tricky to master, attribute fields can come in very handy.

//See if Player1 is part OGRE
if ((Player1Race & OGRE) && Player1Sex==FEMALE)
{
printf("The baby ogre thinks that you are its mother!!");
} else if (Player1 & HUMAN)
{
printf("But as you approch the baby ogre smells your human blood an bites you.");
} else if (Player1 & GIANT)
{
printf("As you approch the baby ogre is scared by your size.");
} else if (Player1 & TROLL)
{
printf("As you approch the baby ogre is scared by your hidious features.");
}
} else
{
printf("The creature attacks you.");
}


Now let us look at using enumerations to begin building models in our game:

---------- Enum2.C: Incomplete but compilable ----------
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

typedef enum {BLOND=0, BROWN, RED} HairColor;

//An array of strings can be used to convert type enums to strings...
const char HairColors[][10] = {"Blond", "Brown", "Red" };

typedef enum {
ROOM_EMPTY=1,
ROOM_HAS_NORTH_ENTERANCE=2,
ROOM_HAS_SOUTH_ENTERANCE=4,
ROOM_HAS_EAST_ENTERANCE=8,
ROOM_HAS_WEST_ENTERANCE=16,
ROOM_HAS_TRAPDOOR=32,
ROOM_HAS_DRAGON=64,
} RoomProperties;

//BUT arrays of strings are less than helpful in converting attribute fields.
//const char RoomProps[][100]=??? would need 128 values to hold all combinations.

//Often there is no reason to make the enum
// a datatype since we will never use it as one
// rather we will just use the enum as named constants.
enum Room_Names {
FOREST=0,
ENTRANCE,
PARLOR,
KITCHEN,
MAINHALL,
HIDDENPASSAGE1,
HIDDENPASSAGE2,
BEDROOM1,
BEDROOM2,
GRANDCLOSET,
ALCHEMYLAB,
NUMBER_OF_ROOMS, //This now tells us how many rooms have been defined.
NOT_ROOM //Might be used in a map to indicate a spacer.
};

const char RoomNames[][32] = { "Forest", "Enterance", "Parlor" }; //etc. etc.

//Note that room discriptions must be overly dramatic as there are no graphics
// to catch and keep the player's attention. (That and its tradition).
const char RoomDiscriptions[][1024] = {
//FOREST==0
"The Forest is damp and dark. Wild sounds fill the night \n"
"air with a scattered cacophony or howls, cracks, moans, and growls. \n"
"These wild sounds fill your heart with fear. In the distance you spot \n"
"a light, to the north a large manner emerges from behind curtains of fog. \n"
"This will be your shelter for the night.\n",

//ENTERANCE==1 -- Note that the discription must be in order!!!
"The entrance is a small anteroom that is filled with portraits \n"
"and luxuries beyond anything you have ever seen. There are two doors \n"
"The southerly one leads back out to the terrible fright of the forest \n"
"The northerly archway enters into a large hall with soft chairs and \n"
"a lit fire.\n",

//PARLOR==2
"The parlor is a small room lined with suits of armor. On the wall hang \n"
"maps of strange lands, tapestries depicting histories and battles of ages \n"
"long past. There is an air of power about the room. Everything in the room \n"
"seems overly sized which makes you feel small, and insignificant. The parlor \n"
"has three passages: To the west is the Main Hall filled with its comforts and \n"
"riches. From the south delicious aromas emanate from a kitchen. To the north \n"
"there is a well lit staircase.\n"
}; //etc. etc.

int main()
{
HairColor Player1Hair;
RoomProperties Rooms[NUMBER_OF_ROOMS]; //You can create arrays of enumerated types.
char cIn;
int CurrentRoom;

//Enums are really just integers and so, if you keep this in mind,
// you can manipulate them just like integers

srand((long)time);
Player1Hair = BLOND;
Player1Hair += (rand() % 3); //generate a random hair color.

//Since HairColor is an integer type, C has no trouble converting it to
// an integer. Although sometimes a typecast is needed.
printf("Player1's hair is %s\n", HairColors[Player1Hair]);

Rooms[FOREST] = ROOM_HAS_NORTH_ENTERANCE;
//Forest North -> ENTERANCE.
Rooms[ENTERANCE] = ROOM_EMPTY | ROOM_HAS_SOUTH_ENTERANCE | ROOM_HAS_NORTH_ENTERANCE;
//ENTERANCE South -> FOREST, North -> MAINHALL
Rooms[MAINHALL] = ROOM_HAS_SOUTH_ENTERANCE | ROOM_HAS_EAST_ENTERANCE | ROOM_HAS_TRAPDOOR;
//MAINHALL South -> ENTERANCE, East -> PARLOR, Trapdoor -> HIDDENPASSAGE1
//etc. etc.

CurrentRoom=FOREST;
printf("Player1 is in the %s:\n%s", RoomNames[CurrentRoom], RoomDiscriptions[CurrentRoom]);
printf("In which direction would you like to move [N, S, E, W]: ");
cIn = getchar(); //There are all kinds of problems with using getchar()
if (cIn=='n'||cIn=='N')
{
if (Rooms[CurrentRoom] & ~ROOM_HAS_NORTH_ENTERANCE)
{
printf("\nYou cannot go that way.\n");
} else
{
//CurrentRoom =
//Here you would change rooms.
}
} else if (cIn=='s'||cIn=='S')
{
if (Rooms[CurrentRoom] & ~ROOM_HAS_SOUTH_ENTERANCE)
{
printf("\nYou cannot go that way.\n");
} else
{
//CurrentRoom =
//Here you would change rooms.
}
}
return 0;
}


The above program goes a long away along the path of creating a text based RPG while demonstrating the usefulness of enumeration. These enumerations are nothing more than named constants, and yet they are very powerful. They allow us to represent all kinds of data and meta-data that will help simplify logic, and build more powerful tools for constructing games.

There are a few more tricks using the seed values that I would like to point out. There is no rule saying that two items in an enumeration cannot have the same value nor do my seed values have to be in any particular order. This is a very useful point. Let us say that I wanted to break my map up into a first floor and a second floor. I might use the following:

enum Room_Names {
FOREST=0, //=0
ENTRANCE, //=1
PARLOR, //=2
KITCHEN, //=3
MAINHALL, //=4
HIDDENPASSAGE1, //=5
HIDDENPASSAGE2, //=6
NUM_OF_1ST_FLOOR_ROOMS, //=7
BEDROOM1 = NUM_OF_1ST_FLOOR_ROOMS, //=7
BEDROOM2, //=8
GRANDCLOSET, //=9
ALCHEMYLAB, //=10
NUMBER_OF_ROOMS, //=11
NUM_OF_2ND_FLOOR_ROOMS = NUMBER_OF_ROOMS - NUM_OF_1ST_FLOOR_ROOMS //= 11- 7 = 4
};


One of the biggest problems of high level language programmers is that they tend to forget about the hardware and memory aspect of programming. Enumerators are much more powerful if you remember that they are integers, and that you remember that the computer stores integers in a binary format. When using most modern compilers an integer is 32 bits (on some older computers and compilers integers are only 16 bits). This means that an attribute field can only hold 32 attributes. This means that when you store them in a binary file they will occupy 4 bytes. Knowing these kinds of things will come in handy.

Next Section: Pointers.

References and Additional Information:

Enumerations
[1] Roddey, Dean. Stupid Enumeration Tricks
[2] Toccct. The Beginner's Guide to Using enum Flags
[3] Unknown, Wikipedia: Enumerated type
[*] I have to say that Dan Saks must love enumerations. I learned a great deal from these articles.
[4] Saks, Dan. Enumerations as Counters
[5] Saks, Dan. Well-Behaved Enumerations
[6] Saks, Dan. Enumerations Q & A
[7] Saks, Dan. More On Enumerations.
[8] Saks, Dan. Enumerations with noncontiguous values
[9] Wilson, Matthew. Flexible C++ #11: Imperfect enums, part 1: Declarations, Definitions, and Namespace Leakage, Dr. Dobb's, 2005

Is This A Good Question/Topic? 0
  • +

Replies To: Data Modeling for Games in C Part I

#2 sacwchiri  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 29
  • Joined: 27-March 07

Posted 16 April 2007 - 11:32 AM

yo... in this line here:
typedef enum {MALE=0, FEMALE} CharacterSex;

is it any different than saying

typedef enum charactersex
{
MALE = 0
FEMALE= 1
};
Was This Post Helpful? 0
  • +
  • -

#3 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2247
  • View blog
  • Posts: 9,237
  • Joined: 18-February 07

Posted 16 April 2007 - 02:02 PM

Yes there is a difference. The second should give you a compiler error, though not all compilers are the same so yours may not. The reason has nothing to do with using the enum incorrectly. The two enumerations are in fact the same (well the second one needs a comma). The problem is that you are misusing the typedef operator.

The syntax for the typedef operator would be:

typedef DataType Alias;

For example:
typedef char * MYSTRING;

So the FULL syntax for using typedef with an enumeration would be:

typedef enum [TagName] {
____ITEM1 [= seed value 1],
____ITEM2 [= seed value 2],
____...
____ITEMn [= seed value n]
} TypeAlias;


Where TagName is optional (I don't see off the top of my head how this would ever come up in enum's but the tag name would be required if you wanted to refer to the enum from within itself. This happens with struct's but I can't think of any reason it would occur inside an enum). The TypeAlias is not optional.

My compiler will compile the following,
typedef enum charactersex
{
MALE = 0,
FEMALE= 1
};
but it will not let me use "charactersex" as a datatype.

so this should look like:
typedef enum {
MALE = 0,
FEMALE= 1
} charactersex;

Was This Post Helpful? 1
  • +
  • -

#4 sacwchiri  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 29
  • Joined: 27-March 07

Posted 11 May 2007 - 12:51 PM

hey one more question on this in the weapons list define like

enum weapons{
sword,
longsword
};
how could i give em values then???

cos im trying to give them an upper and lower atack value and durability but i dont know how to do that...
Was This Post Helpful? 0
  • +
  • -

#5 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2247
  • View blog
  • Posts: 9,237
  • Joined: 18-February 07

Posted 11 May 2007 - 01:25 PM

The answer is going to actually be in structures (the part of the tutorial I have not gotten to yet).

An enumeration can only enumerate "Types", or enumerate "properties" etc. It can not really hold complex amounts of data (though you would be amazed at what people do with enumerations). Basically Enumerations let you NAME values. So they are like numeric constants.

So you could use an enumeration such as:
typedef enum {
	sword_MIN_DAMAGE = 0,
	sword_MAX_DAMAGE = 22,
	longsword_MIN_DAMAGE = 4,
	longsword_MAX_DAMAGE = 34
} weapon_damage;


This does not seem like all that much help, but in the tutorial I used arrays to translate enumeration values into more complex values such as strings. Suppose I use an array to hold the default property values for weapons:
int WeaponProps[] = {0, 22, 3, 34};

typefec enum {
Sword = 0,
LongSword= 2
} WeaponType;

typedef struct {
	WeaponType type;
	int MinDamage;
	int MaxDamage;
} Weapon;

...

Weapon Player1RightArm;
Player1RightArm.type = LongSword;
Player1RightArm.MinDamage = WeaponProps[LongSword];
Player1RightArm.MaxDamage = WeaponProps[LongSword+1];



There are many many ways to use enumerations in combinations with other datatypes such as Arrays and Structures that can build complex models. Remember that enumerations are just a way to name numbers, this allows for more clarity in your program.

BTW, you are still using the typedef statment incorrectly
Was This Post Helpful? 1
  • +
  • -

#6 sacwchiri  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 29
  • Joined: 27-March 07

Posted 13 May 2007 - 11:43 AM

thanx alot that might have just solved the issues i was having... :D


and about the typedef... oops :P
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1