Sign in to follow this  
Shadowwoelf

Data Structures and refrences!

Recommended Posts

I have almost completed the basic setup of my game but I have one simple question. I am currently trying to eliminate all my global variables, and for now i am using references to change them. Heres my basic set up.
while(!done){
gameloop(variables go here);
}

gameloop(typedef &variables){
   switch{gamestate){
      case menu: logicmenu(certain variables)
                 rendermenu(certain variables);
                 break;
   }
}

So wouldn't it be better just to put all my variables into one struct and then pass that? My other idea was to create a couple of structs instead of one giant one like put all the x,ys in one struct and maybe logic stuff in an other one. So which way should I go about using variables?

Share this post


Link to post
Share on other sites
Can you give me some more info about what's exactly in the structs, and what the structs are meant to represent?

For example, the struct with the coordinates, is that a character struct that also contains hp, etc.?

But yeah, if you have a lot of certain types of variables you want to pass, you should pass it as a struct.

[Edited by - Moonshoe on July 7, 2008 6:22:30 PM]

Share this post


Link to post
Share on other sites
Here are most of the variables that most of my functions will need


BOOL done=FALSE; // Bool Variable To Exit Loop
GameState_E Gamestate=MENU;
int selector=0, max_selector;
GLubyte Ycolor = 255;
GLfloat Width, Height;
GLfloat roll; // Rolling Texture
bool colordown=true;
GLfloat yOffset=0;
bool change=false;



some of these variables are for specific functions only... And I am planning on having a character class instead of a struct. The only thing is that I don't want to keep having to add to my function certain variables. so instead of

while(!done){
gameloop(variables go here);
}

gameloop(typedef &variables){
switch{gamestate){
case menu: logicmenu(certain variables)
rendermenu(certain variables);
break;
}
}


It would be like

struct GameVariables;
while(!done){
gameloop(GameVariables);
}

gameloop(struct &GameVariables){
switch{gamestate){
case menu: logicmenu(GameVariables)
rendermenu(GameVariables);
break;
}
}





This way If i wanted to add a new variable I would simply add it to the struct.

Share this post


Link to post
Share on other sites
Ok, that's good!

But when you have the character class, you're going to pass that too, right?


struct GameVariables;
class Character;
while(!done){
gameloop(GameVariables, Character);
}

gameloop(struct &GameVariables, class &Character){
switch{gamestate){
case menu: logicmenu(GameVariables)
rendermenu(GameVariables);
break;
}
}



Share this post


Link to post
Share on other sites
If you have a gigantic struct of whatever you need that you're passing around everywhere, you may as well just go with globals. The struct doesn't really gain you anything.

Share this post


Link to post
Share on other sites
Quote:
Original post by Shadowwoelf
Well I was taught that global variables are bad and that I should at least limit them when I can

Were you taught why global variables are bad?

Share this post


Link to post
Share on other sites
Spaghetti code he basically called it since anything in the program can change it(and harder to debug). Of course the current way I've done referencing that still allow some functions to change things they shouldn't.

Share this post


Link to post
Share on other sites
Quote:
Original post by Shadowwoelf
Spaghetti code he basically called it since anything in the program can change it(and harder to debug).

"Spaghetti code" is generally used to refer to control flow rather than data sharing, but yeah, that's a good description of the problem.
Quote:
Of course the current way I've done referencing that still allow some functions to change things they shouldn't.
Exactly. Since everything in the program will be touching that struct, anything in the program can change it. You're running into the same issue as people who use singletons because they were taught that globals are bad: What's bad about globals is how they're used, not what they're named.

BTW, this thread has some good info, particularly from Rydinare.

Share this post


Link to post
Share on other sites
You should only ever pass objects parameters that they need. Class heirarchies with loose relationships (coupling) is often a better design choice than tighty coupled methods.

Your code looks a lot like a C game structure. Structures are used in functions instead of class calling their own functions passing data to their children which call their own methods with their own sub-data.

Instead of what you have done with structures and global functions, try implementing it into an Application class that is created inside your program's entry point, not globally, so that an object may only get a pointer to the Application if the parent of that object hands it out (and the parent knows about it itself).

class Application
{
public:
SceneGraph* GetSceneGraph();
bool Open (void);
void Logic (double delta_time);
void Render ();
void Close (void);
bool IsRunning (void);

private:
/* application objects */
bool m_Running;
std::list <IGameObject*> m_Objects;
Renderer* m_Renderer;
};

int main ()
{
Application app;
if (app.Open ()) {
Timer timer;
while (app.IsRunning ()) {
timer.Start ();
app.Logic (timer.DeltaTime); // delta time of last frame..
app.Render ();
timer.End ();
}
app.Close();
}
}

// Application::Logic () function
void Application::Logic (double dt)
{
std::list <IGameObject*>::iterator obj = m_Objects.begin ();
while (obj != m_Objects.end ()) {
(*obj)->Update (this, dt); // pass the Application object and delta time
++obj;
}
}



In this design, the heirarchy is only loosely coupled. Objects don't know about their parents until its time to update them every frame. Theyre not explicitly coupled to one particular Application or one particular Renderer.

Alternatively to relinquish immediate access to the Application object, have a World class or something which contains a list of the game objects. The World class would further abstract the Application from its contents so that the Application can simply update whatever 'world' its currently loaded, and the world class manages changes and whatnot to all objects.

The game objects can either be passed a World* object into their Update functions or they may contain a pointer to their parent World class.

Share this post


Link to post
Share on other sites
perhaps this is a better solution? (The other thread just confused me because I haven't learned what a singleton is yet)


struct GameVariables;
class Character;
while(!done){
gameloop(GameVariables, Character);
}

gameloop(struct &GameVariables, class &Character){
switch{gamestate){
case menu: logicmenu(Specific Variables);
rendermenu(Specific Variables);
break;
}
}




At least this way logic menu does not gain all the variables only the one it needs.

Share this post


Link to post
Share on other sites
Have you learnt about classes yet? Have a read up on polymorphism too.

A game state mechanism is usually implemented through virtual base classes.

So you have a base class called IGameState then a bunch of derived classes called MenuGameState and PlayGameState. Each derived class performs its own operations in an Update/Render set of member functions.

class IGameState
{
public:
virtual void Update (double dt);
virtual void Render (void);
};

class MenuGameState : public IGameState
{
public:
void Update (State*& state, double dt)
{
if (playButton->Pressed) {
state = new PlayGameState();
delete this;
}
}
void Render (void);
};

IGameState* state = new MenuGameState;
while (state) {
state->Update (dt);
state->Render ();
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Shadowwoelf
Ive learned about classes(Nothing 2 deep), but I am trying to keep this reasonably simple.
btw what is this? "State*& state" a pointer to a reference?


Well actually that was a slightly poor example of managing the state changes. You usually have a state manager or similar class which handles changes of states so that a state might call StateManager::changeTo (new PlayState()) and the StateManager will queue up a change to the new state next update.

Alternatively you can push states onto a stack with something like StateManager::pushState (new PlayState())

Share this post


Link to post
Share on other sites
Quote:
Original post by Shadowwoelf
Ive learned about classes(Nothing 2 deep), but I am trying to keep this reasonably simple.
btw what is this? "State*& state" a pointer to a reference?


A reference to a pointer. &, * and 'const' on type names are read right to left, basically.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this