• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
inanhour&

code review - basic state system

4 posts in this topic

I am writing a simple board game where the players place pieces on a board. (theres more to it than that, but you get the idea)

 

I needed a system where, on one screen the player can view the board and select tiles, and on the other they can select from a menu the type of tile they want to place.

 

For this I needed two 'states', each one handling input and output differently. So I created these as classes, both inheriting from the 'state' class. The thing I have had difficulty with is switching between these classes, since they cant see each other, and how the current state pointer switches from one state to the next will depend on the original state. What I have done is given the state class a 'states' static vector, which stores pointers to all the states that have been created, and a non-static 'keys' vector which stores the input needed to access that state from the this state (so the two vectors sort of line-up). 

 

Anyway this seemed kind of messy and there is probably a reason why you shouldn't do this and i'm probably doing everything wrong anyway. So I'm posting this on here to ask for some feedback before I make it even worse.

 

thanks!

 

Here is the program. (its not actually a board game here. I've reduced it to just the basic state system)

0

Share this post


Link to post
Share on other sites

Your two states should be completely isolated from one another.  The thing that you pass between them is a tile so one should return a tile and the other should accept a tile.  Beyond that the two sides of your game do not need to interact with one another.

 

For example in the game of Scrabble, you have a bag of tiles and you have a board containing tiles.  You could implement it this way.

std::vector<Tile*> _bagOfTiles;
std::vector<std::vector<Tile*>> _gameBoard;

Tile* getTile() {
   //bag is shuffled already or what ever before this is called
   //don't forget to add error checking when your bag is empty!
   Tile* pTile = _bagOfTiles.back();
   _bagOfTiles.pop_back();
   return pTile;
}

void placeTile( int x, int y, Tile* pTile ) {
   _gameBoard[x][y] = pTile;

   //game logic based on placement of tile goes here
}

The _bagOfTiles and _gameBoard would be members inside their own classes used to deal with the game logic

2

Share this post


Link to post
Share on other sites

I don't think there is any particular right or wrong with these systems.
 
As a general opinion though, I think that global (including static) state is to be avoided; it's not thread-safe and pretty much assumes/requires that you only have one instance of the system overall. Generally it's not a good idea to solve problems by reaching for global state out of the programmer's toolbox without a good reason.
 
I would move the management of all states more towards your "currentState" pointer and whatever it is that owns that pointer: "the state manager/machine", in your case it's main.cpp.
 
In fact you can kind of simplify things, what you've implementing with two vectors and a few loops is basically a map. So just use a map!
 
Each state has a local (non-static!) member, which is a map that maps the key to the subsequent state:
 
std::map<char, State*> keysToStates;
 
Then you just use the map to lookup the state based on the input key.
 
 
I wrote a similar system recently for a game. It was similar to yours in a few ways, but different also. Described thusly:
 
Each state had a render() function which was my (graphical) equivalent to your giveOutput() function.
 
There was also a process() function which returns a State pointer; this is the pointer to the next state. So my process() function acts as a combination of your nextState() and getInput() functions. You could even make the return optional (using boost::optional, or by returning null if you use raw pointers) and then the absence of a returned state can be interpreted as a quit, so it could also subsume your quit function.
 
My process() function was responsible for gathering input (like your getInput) and doing the main 'thinking' that the state needs to do.
 
Another difference between my states and yours is the way that states are driven to transition. Your transitions are based on key inputs - there is absolutely nothing wrong with this so long as state transitions are _always_ driven by key inputs. That's a decision you need to make. This was not the case for me however, for example a gameover state is reached when the player loses all their health, in this case clearly it is not based on an input key but on some internal game logic. Therefore I couldn't just directly map key inputs to the state transitions.
 
To achieve this I wire states together a little differently. Each state knows about the states that it can transition to in an abstract way. For example the main gameplay state knows that when the main character dies it needs to move to a gameover state. So it has a member function on it, something like this: setGameoverState(State * gameover);
Now, the transition occurs when the process() function decides the transition should take place. It could be looking at the key input (in which case it works similarly to your system), or in this case it is looking at the remaining health and once it hits zero it returns the gameover state from the process() function.

 

Note however that the main gameplay state doesn't *really* know anything about the game-over state. It simply knows that some kind of gameover state exists, but it doesn't care what that actually is. So states are still isolated. You might wire in a GameoverScreenState or HighscoresScreenState, or a DeathSceneState, whatever you like.

 

Perhaps, generally speaking, states would be driven by input, in my system I might create a convenient AbstractKeyDrivenState, which holds the map of keys-to-states that was discussed above.

 

One final point. My states also have abstract functions for entering and leaving a state. If the nextState != currentState then these functions would be called. They allow the states to setup/init and de-init themselves (releasing resources, etc). For example when entering (or re-entering) the main gameplay state it has to (re)setup the world, load all the resources, etc and unload them all at the end as well. The enter() function was actually an enter(State*) function, where the previous state was passed into the next state. This allowed states to implement a backup feature where they can return you back to the last state you came from without having to explicitly wire them up beforehand (and explicitly wiring them wouldn't work if a state can be entered from multiple other states as you wouldn't know which state was the correct one return to).

Edited by dmatter
2

Share this post


Link to post
Share on other sites

Thanks for the replies!

 

I don't think there is any particular right or wrong with these systems.
 
As a general opinion though, I think that global (including static) state is to be avoided; it's not thread-safe and pretty much assumes/requires that you only have one instance of the system overall. Generally it's not a good idea to solve problems by reaching for global state out of the programmer's toolbox without a good reason.
 
I would move the management of all states more towards your "currentState" pointer and whatever it is that owns that pointer: "the state manager/machine", in your case it's main.cpp.
 
In fact you can kind of simplify things, what you've implementing with two vectors and a few loops is basically a map. So just use a map!
 
Each state has a local (non-static!) member, which is a map that maps the key to the subsequent state:
 
std::map<char, State*> keysToStates;
 
Then you just use the map to lookup the state based on the input key.
 
 
I wrote a similar system recently for a game. It was similar to yours in a few ways, but different also. Described thusly:
 
Each state had a render() function which was my (graphical) equivalent to your giveOutput() function.
 
There was also a process() function which returns a State pointer; this is the pointer to the next state. So my process() function acts as a combination of your nextState() and getInput() functions. You could even make the return optional (using boost::optional, or by returning null if you use raw pointers) and then the absence of a returned state can be interpreted as a quit, so it could also subsume your quit function.
 
My process() function was responsible for gathering input (like your getInput) and doing the main 'thinking' that the state needs to do.
 
Another difference between my states and yours is the way that states are driven to transition. Your transitions are based on key inputs - there is absolutely nothing wrong with this so long as state transitions are _always_ driven by key inputs. That's a decision you need to make. This was not the case for me however, for example a gameover state is reached when the player loses all their health, in this case clearly it is not based on an input key but on some internal game logic. Therefore I couldn't just directly map key inputs to the state transitions.
 
To achieve this I wire states together a little differently. Each state knows about the states that it can transition to in an abstract way. For example the main gameplay state knows that when the main character dies it needs to move to a gameover state. So it has a member function on it, something like this: setGameoverState(State * gameover);
Now, the transition occurs when the process() function decides the transition should take place. It could be looking at the key input (in which case it works similarly to your system), or in this case it is looking at the remaining health and once it hits zero it returns the gameover state from the process() function.

 

Note however that the main gameplay state doesn't *really* know anything about the game-over state. It simply knows that some kind of gameover state exists, but it doesn't care what that actually is. So states are still isolated. You might wire in a GameoverScreenState or HighscoresScreenState, or a DeathSceneState, whatever you like.

 

Perhaps, generally speaking, states would be driven by input, in my system I might create a convenient AbstractKeyDrivenState, which holds the map of keys-to-states that was discussed above.

 

One final point. My states also have abstract functions for entering and leaving a state. If the nextState != currentState then these functions would be called. They allow the states to setup/init and de-init themselves (releasing resources, etc). For example when entering (or re-entering) the main gameplay state it has to (re)setup the world, load all the resources, etc and unload them all at the end as well. The enter() function was actually an enter(State*) function, where the previous state was passed into the next state. This allowed states to implement a backup feature where they can return you back to the last state you came from without having to explicitly wire them up beforehand (and explicitly wiring them wouldn't work if a state can be entered from multiple other states as you wouldn't know which state was the correct one return to).

 

Ok, I have made a few changes to the program. It is now closer to yours, in that there is no global/static 'currentState' pointer (I understand thread safety a bit better now) and instead of a 'getInput()' function there is a 'process()' function which returns a state pointer which works just as you described.
 
Although, how would you not change state after a process()? Since, returning NULL is used for quit. I returned 'this', so it sets the current state to the same as it was before. I don't know if there is a way of returning something else, so it knows not to bother changing the state.
 
I also decided to change state transitions so they don’t have to be driven by input. For my simple program I wasn’t thinking I would need this, but I might as well just in case I want to later on. I still used the map of keys-to-states idea, but instead of keys, it uses state indexes (I used an enum). So, like you described, states know about the other states in an abstract way, but you need to 'setTransition(stateIndex index,  state* const state)' so that 'process()' can return a pointer to that state, using the map and index.
 
here is the updated version, if you want to see what i mean.
 
So yeah, thanks for the help! I think it works much better now (:
0

Share this post


Link to post
Share on other sites

Although, how would you not change state after a process()? Since, returning NULL is used for quit. I returned 'this', so it sets the current state to the same as it was before. I don't know if there is a way of returning something else, so it knows not to bother changing the state.

Yeah I just return the [tt]this[/tt] pointer.

 

You could switch things about and have a special quit state that you return for quitting and then you can free up NULL as another way to remain in the same state.

 

One idea is to define a quitState() function in your abstract State class that returns the appropriate pointer value (even if that is the null pointer). Then you just do: [tt]return quitState();[/tt] and you don't have to care about whether that's returning null or some special state.

Edit: Or define it as a constant somewhere: [tt]const QUIT_STATE = null;[/tt] then even you main loop can compare against that constant too.

Edited by dmatter
0

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  
Followers 0