Organization of Global Variables for my game

Started by
10 comments, last by Zahlman 18 years, 10 months ago
INTRODUCTION _________________________________________________________________________ For my first game I am making a turn based strategy game in SDL , C++. To massivly simplify the idea, think archon (if you dont know this game a) shame on you b) then think chess) meets capture the flag. The gameboard is a 16x10 square board, each team has 10ish units, each unit occupies a square. I am at the stage in development where I can place a unit on a square, and move it to another square at the click of a mouse. Ive got the movement restricted to be confined within the square. I also have a fair amount of interface/structure coded out so I dont have to redo this as Im building the game. Ive spent 95% of the time on this part and think ive developed a fairly flexible system. PROBLEM ---------------------------------------------------------------------- Now my problem is for how to deal with global variables or "game state" variables. EG boolean to store if a unit is currently being selected or the mouse (X,Y)position after a mouse click has taken place. I have come up with two possible solutions , and both present their advantages/disadvantages. SOLUTION1 Keep global "game state"variables in the main file that contains both the event loop and the main method. PROS This is what I typically see on all the tutorials / sample coding that ive read (although their size pale in comparison to my games estimated size), it works easy enough and doesn't convulute the code. CONS since my event loop is in this file, I have a feeling its going to get quite massive in size already by the end of my game. I want to avoid code in this file as much as is reasonable. Also, if I want to call a method say from the "unit" file.. My "unit" wants access to a lot of these global states. I dont want to also store these wanted global states in the unit file because of the extra memory required. So when the game increases in size i'm going to have to keep altering the arguments for these methods in the "unit " file eg unit1->attack(int xPos,int yPos,bool hasFlag........) SOLUTION 2 This is the current solution I have implemented, Keep global "game state" variables in their own class, outside of the event loop and the main method. PROS Solves both the cons of the previous method If I want a method from the "unit" file to acess the game states, I set it up like unit1->attack(GameBoard *board) and I can then access all of the states in the game. Creates massive flexibility and barely any overhead. CONS MAKES THE CODE MASSIVLY CONVULUTED!!!! I instantiate the class like GameBoard * board = new GameBoard(.....); the effect is I have so many board->'s in my event loop its almost silly :( example of it at its worst: if ( event.type == SDL_MOUSEBUTTONDOWN && board->mouseOnBoard()) { if ((board->unitAtCurrentPos() > -1) && (board->selectedID() < 0)) board->setSelectedID(board->unitAtCurrentPos()) ; board->setOldMouseX(board->mouseX()); board->setOldMouseY(board->mouseY()); ----------------------------------------------------------------------------- Conclusion: Im kind of torn between the two, as it is right now the flexibility option number 2 provides seems hard to pass up.... Ive considered a hybrid between the two, but I feel it may confuse me in the future which variables are in the main file, and which are in the GameBoard. Any advice the gurus could offer me would be much apreciated
Advertisement
well, I have always used the second method. It may end up with millions of 'board->'s but it keeps it neat and you know where you are going. Added to this, intellisense (if you have it) will bring up all the variables within the class which makes finding what you need a bit easier. It keeps everything within its neat little group. I look grouping things.
With the first method I just found it got really really messy and I kept having to refer back to make sure I spelt the bloody variable correctly.

You have no idea how many 'gConsole->[function/variable]' calls I have within my engines source.

But in the end, using the class system does make everything look far neater and easier to navigate. Well IMHO anyway.

I dunno, go with the one that feels the most comfortable.
For some reason, having board->method(board->member...) smells a bit to me. If that method needs state information from the instance on which it is operating, it shouldn't need it as a parameter. If there's some case where it can be something else, then you might prefer to use default parameters.

That alone would make a lot of things tidier...

-Auron

actually, yea i didn't notice that [smile]. It shouldn't need to call on its own instance to retrieve variables from itself if that makes any sense whatsoever [wink]
Quote:Original post by Auron
For some reason, having board->method(board->member...) smells a bit to me. If that method needs state information from the instance on which it is operating, it shouldn't need it as a parameter. If there's some case where it can be something else, then you might prefer to use default parameters.

That alone would make a lot of things tidier...

-Auron


basically im equating one variable in my gameboard to another.
Since I am doing this from the main method and the variables are private, I have to use mutators/accessors.

eg

board->setX(board->getSomeOtherX());

which would be defined in GameBoard as
public:
void setX(int x); //{this-x = x;}
int getSomeOtherX(); //returns someOtherX

private:
int someOtherX;
int x;

If I was using my solution 1, I would basically just be doing
x = someOtherX;...

Do you have a suggestion that would alow me not to type
board->setX(board->getSomeOtherX());??

I guess I could create a new method equateX() which provides the same
functionality as board->setX(board->getSomeOtherX())
so I would just use board->equateX();
However, wont this require lots of additonal methods since I have a fair number of variables...

Man this is the first time ive ever had to organize this much code and its quite annoying :P
I'm not sure If I understand your problem fully so this might not aply. Why not have a gamestate class that gameobjects have a pointer to. then in your game objects constructor set the pointer the the main and only instance of gamestate.
This is the kind of thing that just takes experience to learn. It's also something hard to help you with without seeing to much code. Really it comes down to this. If your using to many set/gets your not writing object oriented code to begin with. Your just making cluttered and over verbose C.

The idea you should concentrate on when creating methods is encapsulation of functionality. A class should completely abstract a single part of your design. The interface to the class should be as simple as possible. Even if this means making "alot of methods."

I'll try to give an example based on the code you posted.

// in event loopif ( event.type == SDL_MOUSEBUTTONDOWN)  board->onMouseDown();// in board.cppBoard::onMouseDown(){  if (mouseOnBoard())  {    if (getUnit(mCurrentPos) > -1 && mSelectedID < 0)      selectUnit(getUnit(mCurrentPos));      storeMousePos();  }}


Obviously this wont fit directly into your code, but thats roughly the idea. One thing you should definitely do whereever possible is use a struct for x,y position of units/mouse (like I did for my mCurrentPos), that way you only need to call one function when your passing around position info.
A general rule of thumb is to avoid global variables as much as possible.

If you have a set of related variables which are at the program scope, group them together into a Singleton or a static class.

If you have a set of related variables which aren't at program scope, i.e. map, sprite, and weapon data, group them into a class, along with their associated operations.

Also, in general the fewer 'if's and 'switch's there are governing high-level program flow, the better. Illimuni's code example shows an example of what I mean.

Illimuni put it excellently. Pay heed to his post.

BTW: if you think pointer syntax looks ugly, wait till you start writing "true" low-level code, and when you start messing with templates. You ain't seen nothin' yet. Just take a look at the standard library files.
First, it is a bad idea to use a class as a collection of unrelated variables. Don't create a class that simply contains global variables.

Second, global variables should be avoided. Do you actually need a global variables to contain the mouse position and the currently selected unit? That seems like something relevant to only an input or player module. Why does the renderer care where you clicked or which is the currently selected unit? And what do they have to do with the state of the game, BTW?

In my chess engine, I have a game state variable, but it isn't global at all. It is the current state of the game and what the players operate on to advance the game. Here is the game state class for my chess engine.
#pragma once#include "Board.h"#include "Move.h"#include "ZHash.h"#include "Sequence.h"#include <vector>#include <queue>class PieceList;class Move;class Piece; class GameState{public:	union CastleStatus	{		struct		{			uint8	castled		: 4;	// Which castles have occurred			uint8	unavailable	: 4;	// Which castles are no longer possible		};		uint8	status;	};	GameState() {}	GameState( GameState const & old_state, Color color, Move const & move );	GameState( Board const & board, Move const & move, int value, CastleStatus castleStatus );	// Resets the game	void Initialize( PieceList const & white_pieces, PieceList const & black_pieces );	// Equality operator	bool operator==( GameState const & y ) const;	// Returns true if a castle is allowed	bool CastleIsAllowed( Color c ) const;	// Returns true if a king-side castle is allowed	bool KingSideCastleIsAllowed( Color c ) const;	// Returns true if a queen-side castle is allowed	bool QueenSideCastleIsAllowed( Color c ) const;	// Updates the game state with the specified move	void MakeMove( Color color, Move const & move );	// Returns the hash code for the state	ZHash GetHashCode() const;	Board		m_Board;		// The board	Move		m_Move;		// The move that resulted in this state	int		m_Value;		// Value of the game	int8		m_Quality;	// Quality of the value	int8		m_Priority;	// Priority of this state (determines sorting order)	CastleStatus	m_castleStatus;	// Which side has castled and which castles are still possible	bool		m_bInCheck;	// True if the king is in checkprivate:	// Updates the game state with a normal move	void MakeNormalMove( Color color, Move const & move );	// Updates the game state with a castle move	void MakeCastleMove( Color color, Move const & move );	// Updates the game state with a pawn promotion (after moving). Returns the new piece.	Piece const * Promote( Color color, Position const & position );	ZHash			m_HashCode;	// Hash code for this state	// Returns the queen of the specified color	static Piece const *	GetQueen( Color color );	static Piece const *	m_pWhiteQueen;	static Piece const *	m_pBlackQueen;}; 
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:Original post by Kryodus
Do you have a suggestion that would alow me not to type
board->setX(board->getSomeOtherX());??

I guess I could create a new method equateX() which provides the same
functionality as board->setX(board->getSomeOtherX())
so I would just use board->equateX();
However, wont this require lots of additonal methods since I have a fair number of variables...

Man this is the first time ive ever had to organize this much code and its quite annoying :P


the equateX() method should be a good idea. Consider the advantages:

1) reading the code which use this function is easier, because equateX() contains more semantic value then obj.setX(obj.getAnotherX()).
2) nothing forbids you to write obj.setX(obj.getY()), which is wrong. Don't trust the programmer which will use your library; even if you are this programmer. You don't know if you'll be able to understand this code next year.
3) the code equateX() is correctly encapsulated. You can do whatever you want in equateX(), you can even completely change the way it works. You can do setX(getAnotherX()/2) for example, without needing to modify neither setX() not getAnotherX().

A good encapsulation of data is a good base for a proper OO design.

Regards,

This topic is closed to new replies.

Advertisement