Game structure

Started by
7 comments, last by CrazyCdn 16 years, 1 month ago
Been struggling with organizing my basic game structure, I've been through the FAQ and looked at some game source code, and looked into several books but I'm still not getting the big picture... I'm in the process of setting up my main game structure, I'm working in VC2008, C++ and a I'm working on an RPG strategy game. So far my game structure is pretty simple and looks something like: Headers: Globals.h Main.h Menus.h Graphics.h ..etc Source Main.cpp Menus.cpp Graphics.cpp ...etc Where each of the above contains info such as:

------- Globals.h -----
class cGlobals
{
    public:
    int ScreenWidth, ScreenHeight, ScreenCX, ScreenCY, FullScreen, TotalMenus;
}; // that's it for globals



------- Main.cpp --------
Class cApp
{
  public:
cGlobals	  g;

  protected:
WNDCLASSEX    wc;
HWND          hWnd;
HINSTANCE     hInst;
SOCKET				ListenerSock, SockClient, fdmax, newfd;	// Socket handles
...etc
  private:
cGraphics			Graphics;
cInput			Input;
cMenuType			Menu[TotalMenus];
...etc

  public:
BOOL Init();
BOOL Shutdown();
BOOL Frame();

// Networking
BOOL InitServer();
BOOL DetectSocket();
..etc
};


So I'm new at creating a game structure in C++ and probably the first thing you're cringing at (among other) is that I have globals. The reason I set up a globals class is cause I was getting really tired of sending info like ScreenWidth and ScreenHeight into almost every function... advice? So now I'm running into a couple issues, one is setting up a menu state, I will have a fair amount of menus with buttons and sliders and am trying to create a fairly robust system, but am getting an error when initializing a pack of menus with: cMenuType Menu[30];

class cMenuType
{
  public:
int			menux, menuy, menuw, menuh, mx, my;
int			TotalButtons, ButtonState[7], ButtonWidth, ButtonHeight;
...etc
  protected:
cGlobals g;

  protected:
BOOL MenuCalcs();
BOOL DrawRect();
..etc

The error is as follows: Unhandled exception at 0x00415364 in game.exe: 0xC0000005: Access violation writing location 0x00130030. It stops at the area where I'm passing the globals into a menu initializing function, after about the 20th pass into the intialize function... I know I'm weak at this and have not had good success in finding info on writing structure for a large program or game, I've got several c++ books that go over ever command in detail but not in setting up a robust large "system" The other thing I'm uncertain about is how do I now add in stuff like: Player Info Monster Info Item Info ...etc Would you set these up with their own .cpp and .h files? When do you decide when to create a new .cpp file vs. adding to an existing .h file (my .h files contain mostly just classes)... Any info or recommendation you could offer would be much appreciated... any links, books or old posts? Thanks, Vanz
Advertisement
I'm not an expert on game design (still struggling with some concepts myself, as ASP.NET programming is somewhat different).

I currently am solving the globals / multiple parameters by grouping the most needed / used high level variables into their own class (I'm using C#, but it's the same principle in C++, though you'll probably use a struct). Then you simply pass a const pointer to any method requiring access to some or all of those variables.

I can't guarantee that is the best way, but it seems good to me.

As for your menu, I can't remember off the top of my head, but I *think* C++ uses very similar array allocation to C#,

MyMenu[] menu = new MyMenu[30]();

rather than the way you have it. You can try that or wait till someone more proficient at C++ comes out with the answer.

As for game types, yes I think you're heading in the right direction, the best way from here is to just do it, and learn from your mistakes. You might end up with a base class for a movable entity and inherit from that for your player and monster etc.

As for knowing when to split files, it comes down to good programming practice. You should group functions that work on the data in the same class. Other functions belong in classes of their own. This is just however a guideline, and I'm sure someone will slap me for saying that, but in the real world, sometimes you just need to cross the boundary to get work done vs hypothesising all day long over what is more "correct".

HTH.
When it comes to globals, yes, there are many who will poopoo that. But really, sometimes you just need to get real work done (not more theoretical bookworm philosiphatin'!), so i'm not against globals entirely. But i would make a few suggestions:

1) If you use them, put them in a namespace. Globals are not a "class".

2) if you have globals, you have to set up ALL their values BEFORE those values get used by any other part of the code. Because they are just dangling out there, they are free to get used by any code at any time. that's bad. If you accidentally use an initialized global, you get weirdness and/or crashing. You can avoid that issue by passing the values to those that need it. YES it is annoying, BUT you can guarantee the value is initialized before you give it to other parts of the code.

BadFunction() {   int foo = bar; // look at me, i'm just grabbing at globals! Who knows what they contain!   }GoodFunction( int bar ) {   int foo = bar; // hopefully the function caller knew what "bar" was in the first place.   }GooderFunction( int bar ) {   if ( InputIsYucky(bar) ) { /* do error handling stuph */ }   int foo = bar; // ah, sanity!   }


As for code splitting, basically you want to put each major thing in it's own file. If you are using OOP, each class or small group of classes make up a file. You would have Monster.h, Gun.h, Player.h, GameSpace.h, etc. And it's okay IMHO to have a globals.h or common.h. Most programs do. Eventually, as your program gets bigger, you will realize that some of the so-called globals aren't really so global and in fact inhabit certain domains. For instance, the screen size only means something to the drawing routines, not for the agents. graphics settings are for graphics routines, sound settings are for sound routines, and so on. You'll figure it out soon enough, so i won't grill you on it ;-)
Honestly, I couldn't use a bunch of globals in my engine if I wanted to. Everything is divided into separate DLLs which communicate with each using a global service provider - which incendentally is a global, except that each DLL has it's own instance which is set by a module loader when the DLL is loaded at run-time.

This global is defined as

   extern ComPtr<IServicePool> g_spServicePool;


and the interface definition is

   struct IServicePool      : public IJmUnknown   {      virtual void GetService(const UUID& sid, const UUID& iid, void** ppvObject) = 0;      virtual void ShutdownServicePool() = 0;      virtual void Assign(const UUID& sid, IJmUnknown* pUnk) = 0;      virtual void LogMessage(const wchar_t* message) = 0;      virtual bool CreateInstance(const UUID& clsid, const UUID& iid, void** ppvResult) = 0;      virtual bool UnloadModules() = 0;   };


There was one other game I worked on a long time ago that used a similar system. The system used by the game I will not mention is part of one of the largest franchises of one the largest game publishers in the world, and many of the games produced by the studio use the same framework.
I've learned by diving right in. I'm a self taugh coder, therefore, this advice which follows worked for me the first time through and helped me to learn fast. But if you've made a game before, then this might not be how you want to do your second game.

I find that it is easiest for new coders to just put globals and values into a header file (Globals.h) which is the first file the compiler goes through. There you put all your basics, your #define, enums, and #includes.

You'll find that somethings will need to be defined before it's used, therefore you migrate the information up, or write more declerations. (Your choice) As part of your code grows, you'll find it easier to put it in a file with a topic:

Game.cpp (this #includes "Globals.h" before all other code)
Globals.h
BuildPlayer.cpp
BuySell.cpp
KeyCommands.cpp
Images.cpp
Graphics.cpp
3DObjects.cpp
Audio.cpp
BuildMonsters.cpp
EyeCandy.cpp
Fighting.cpp
DrawScene.cpp
.... etc

Programming from an idea is very organic and things will grow and change over time. Pre-designing your file structure before you've even made a small games isn't what I recommend, but then again, I've learned by diving right in with a teach yourself book.

I hope this helps.
BladeStone
BladeStoneOwner, WolfCrown.com
Hey thanks guys, your advice really helps... BLiTZWiNG the pointer reference totally fixed my error...

One other quick question if you create a class then an array based on that classs, is this a bad practice? Does it create memory for each instance of that class and all the functions?

For example if I use my menu class:
class cMenuType{  public:int	menux, menuy, menuw, menuh, mx, my;int	TotalButtons, ButtonState[7], ButtonWidth, ButtonHeight;...etc  protected:cGlobals g;  protected:BOOL InitMenus(int x, int y, int Width, int Height ...etc);BOOL MenuCalcs();BOOL DrawRect();..etccMenuType Menu[30];


With my Menu[30] does it make a copy of every function for each instance of when I initialize it? Should these functions be in my main game class or are they okay like this?

Thanks,

Vanz

Hey, new here, experienced programmer but new to game programming. To try and answer your last questions:

Arrays of objects are not bad, per se.
Object varName[30] does indeed allocate 30 times the size of Object in memory. If you are absolutely sure you need 30 Objects at any time, that's probably the easiest solution.

However, usually you would create an array/list/vector/hash of pointers to Object, for example like this using STL vectors:

STL:
vector<Object *> listOfObjects;

Object * tmpObject = new Object();
listOfObjects.push_back(tmpObject );

You can also use an array:

Object * arrayOfObjects[FIXED_SIZE];
arrayOfObjects[0] = new Object();

Since you are 'new'ing' the object, it will not go out of scope, BUT you do have to take care of releasing that memory once you're done with it. Also, in the array example, if you're looping through that array, you better check that the pointer != NULL :)

Hope this helps somewhat.
Quote:Original post by rhuala
Hey thanks guys, your advice really helps... BLiTZWiNG the pointer reference totally fixed my error...


Glad I could help :)

I just want to add one thing to what StudMuffin has said, and that is that functions are not copied. Memory is only allocated for variables.

So an array of 30 menu items with say 12 bytes of data (say, 3 ints for example) is just 30 x 12 bytes = 360 bytes + 4 bytes for the pointer to the array.

So, if you were to use C instead, it'd be just like an array of structs and then you writing a loop that called the same function over and over, passing different structs to it each iteration.

Classes are just a tool that help you group the data together with functions that work on that data, making your job as a programmer easier. It's easy to get lost in the concepts and such that current generation programming tools give us, but in the end it all has to be translated to code and data in order to work on the cpu, and cpu's work on very simple levels, like move, add, sub etc.

HTH.
Drop the prefix in front of classes, it should be evident that they are a class. It may be habit but habits can change ;-) It's old school Hungarian notation and from what I've heard most schools have dropped it as it added nothing but more key strokes. That an intellisense is a great thing :)

And it seems you weren't using it right either, you had a cGlobal which I believe with Hungarian would have been gGlobal.

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

This topic is closed to new replies.

Advertisement