Temple of The Roguelike Forums
Development => Programming => Topic started by: mike3 on May 28, 2013, 12:09:01 AM
-
Hi.
I recently found this:
http://gamedevgeek.com/tutorials/managing-game-states-in-c/
and I was thinking of utilizing this idea for a roguelike game, to handle the menus and what not. Would it work? If so, then I'm curious about some fine points:
1. is it possible to dispense with the singletons (singletons are usually considered bad practice and for good reason -- see here: http://blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140827.aspx and here: http://gameprogrammingpatterns.com/singleton.html)? E.g. they say "only one copy of the state objects is needed" -- but then to me it's obvious: just make one copy, don't bother with a singleton!
2. thinking about the future: suppose, instead of just having a simple text-based ASCII game, we move on up to one with more sophisticated graphics, such as sprites, and we have animations going on in the world view in the background of our menus. Yet the game loop would be in "menu state" at that time, yet the background still needs to be updated. Edit: I suppose this isn't much of a problem, since the renderer would render the background first anyway every frame, so it should know about the animations and so update them accordingly.
3. would a separate game state be needed to handle (c)hatting with various NPCs in the game? Note this can display menus (alternative dialogue options) when it's happening.
4. commands that change game state: to get to the menus, you obviously have to input a command. But suppose the game loop is like this:
1. Handle input
2. Do logic
3. Render
Then if the game is in "Game" mode, if it gets an input to bring up a menu, thus switching to "Menu" mode, the state of the game FSM has changed and so now the loop will call the logic function on the menu, but the menu hasn't rendered yet. So what do we do?
5. return from commands: suppose we press "d" for "drop item". The game changes to menu state, with the inventory menu. But now, the menu will return an item index of what item to drop... and then what? The system switches back to game mode? If so, then how does that returned index get to whatever (and where should this code be) actually does the dropping? I.e. when it goes back to game mode, how do you make sure it remembers that it was in the middle of a "drop" command and more importantly, how does the menu system return the value of the selected item to the game mode?
Thanks.
-
Game states are used all the time in games and a very useful. Let's see if I can answer your questions.
1. Yes, it is possible to remove the singletons. In fact, I really have no idea why that tutorial used singletons. Going with individual instances will make your code much more flexible and it will cause fewer bugs and workarounds.
2. A finite state machine (where you only have one state at a time) does, not quite fit games. Instead I would have something more like a state stack. You push new states onto the stack and pop them off when they are done. You could go with something as simple as calling render() for everything in the stack or you could get a little bit more clever and only call render() down the stack until you reach a state to renders the entire screen. Similar rules can be applied to the other common functions.
3. Depends on how general you want to make your states. I would probably make conversations a state then just create an instance of it with the appropriate NPC specific data when you start a conversation.
4. This is another problem I have with that tutorial you linked. Changing the state at arbitrary times can lead to inconsistent behavior. Instead I would have a function like setNextState() and when the game loop will handle the switch at the end of the current loop.
5. Try to separate your game logic from the UI. When the player selects drop item, call some function in your game logic that says drop(item) or whatever. The game states should also probably know very little if anything about each other.
Hope that helps.
-
Game states are used all the time in games and a very useful. Let's see if I can answer your questions.
1. Yes, it is possible to remove the singletons. In fact, I really have no idea why that tutorial used singletons. Going with individual instances will make your code much more flexible and it will cause fewer bugs and workarounds.
2. A finite state machine (where you only have one state at a time) does, not quite fit games. Instead I would have something more like a state stack. You push new states onto the stack and pop them off when they are done. You could go with something as simple as calling render() for everything in the stack or you could get a little bit more clever and only call render() down the stack until you reach a state to renders the entire screen. Similar rules can be applied to the other common functions.
3. Depends on how general you want to make your states. I would probably make conversations a state then just create an instance of it with the appropriate NPC specific data when you start a conversation.
4. This is another problem I have with that tutorial you linked. Changing the state at arbitrary times can lead to inconsistent behavior. Instead I would have a function like setNextState() and when the game loop will handle the switch at the end of the current loop.
5. Try to separate your game logic from the UI. When the player selects drop item, call some function in your game logic that says drop(item) or whatever. The game states should also probably know very little if anything about each other.
Hope that helps.
Thanks. I have another question about 5: you say "try to separate your game logic from the UI", but:
1. things happening in logic (e.g. combat) may need to access the UI ("you hit the monster for X damage" appears on screen)
2. when you do that drop command, the UI needs to be invoked to get the item to drop. In other words, to get the parameter "item" for the "drop(item)" function.
So what should one do?
-
Thanks. I have another question about 5: you say "try to separate your game logic from the UI", but:
1. things happening in logic (e.g. combat) may need to access the UI ("you hit the monster for X damage" appears on screen)
2. when you do that drop command, the UI needs to be invoked to get the item to drop. In other words, to get the parameter "item" for the "drop(item)" function.
So what should one do?
There may be times when it is too inconvenient to separate the two, but you should try to avoid it. I also should have phrased this a little different. The UI can depend on the game logic (how else can it do anything useful), but the game logic should not need to know about the UI. In your damage example, instead of the game telling the UI every time someone is hit, just store those events. The UI can then grab that list and do what it wants with it. This way, when you want to change your graphics, you don't need to worry about breaking your game logic.
-
Thanks. I have another question about 5: you say "try to separate your game logic from the UI", but:
1. things happening in logic (e.g. combat) may need to access the UI ("you hit the monster for X damage" appears on screen)
2. when you do that drop command, the UI needs to be invoked to get the item to drop. In other words, to get the parameter "item" for the "drop(item)" function.
So what should one do?
There may be times when it is too inconvenient to separate the two, but you should try to avoid it. I also should have phrased this a little different. The UI can depend on the game logic (how else can it do anything useful), but the game logic should not need to know about the UI. In your damage example, instead of the game telling the UI every time someone is hit, just store those events. The UI can then grab that list and do what it wants with it. This way, when you want to change your graphics, you don't need to worry about breaking your game logic.
So then you display all messages for the turn after the turn logic is finished, right? (And update the display.)
-
So then you display all messages for the turn after the turn logic is finished, right? (And update the display.)
Yes, you can do it that way. Typically you will have an update()->draw()->update()... loop. The UI grabs whatever info it needs about the game state and draws it. If you want messages to last more than one loop, just save the results somewhere in the UI (the game logic could also store the history, but I would put this in the UI unless the game actually needs that information later).
-
One option, albeit a bit more complex, is to run the UI in its' own thread and the game in another. When the game logic wants to display something, it can raise an event describing the action that just happened. The UI would be listening to these events and would react accordingly. This way the game does not need to care about UI, it can just raise events when something happens and UI will react accordingly. It would be even possible to have multiple UIs if needed (say, one for graphics and one for text).
If threads are synchronized, this would even help with the pesky - more - prompt. The UI would happily display messages when required and if there are more than just few in any given turn, it would start showing the - more - prompt and would return the control to game thread only after user has pressed a key.
-
So then you display all messages for the turn after the turn logic is finished, right? (And update the display.)
Yes, you can do it that way. Typically you will have an update()->draw()->update()... loop. The UI grabs whatever info it needs about the game state and draws it. If you want messages to last more than one loop, just save the results somewhere in the UI (the game logic could also store the history, but I would put this in the UI unless the game actually needs that information later).
When you say "grabs whatever it needs", does that include grabbing the messages that need to be displayed from a buffer or something?
Also, I'm still not clear on how to handle the giving of the data from the menus in the "menu" game state over to the main game logic (like when one does that "drop" -- how exactly does that work?), especially without requiring the "menu" state to know about the "main" state (you say the states should not know much if anything about each other). How does one do that? As the way it'd seem the drop command would have to work is something like this:
1. The game loop is in "main" or "game" mode.
2. It's at the "waiting for input" point, and receives a key press (say, "d").
3. The keypress handler logic switches the game over to "menu" mode (state) (or perhaps a more specific "inventory menu" state).
4. The player selects the item to be dropped.
???
X. The game goes back to the "main" state.
X+1. The game logic, informed by some unknown process of the item chosen, handles the drop in this turn.
So, what goes in the "???" and what is the way in which the logic gets informed? How is that communication handled?
-
Here's how I did it, I think it's much simpler:
* The game logic loops through all creatures and it's the player's turn
* The player object calls something like UI.getKeypress(), and receives a 'd' (or better a DROP constant or something)
* The player object goes through the inventory, turns it into a list of strings and calls UI.chooseItem(items)
* UI.chooseItem() obviously draws a dialog on top of whatever is currently in the game and waits for player input
* Upon receiving the item or its index, the player object calls drop(item).
-
4. The player selects the item to be dropped.
???
X. The game goes back to the "main" state.
X+1. The game logic, informed by some unknown process of the item chosen, handles the drop in this turn.
So, what goes in the "???" and what is the way in which the logic gets informed? How is that communication handled?
Once you have selected the item to drop, just call Player.drop(item_selected), then you can close the menu state and return back to the main UI state. It's probably easiest just to handle the item drop logic as soon as it is called, but you could also queue up the event so that it is done next time update is called. The main UI does not need to know that the item drop menu was just used. When it draws, there will just be another item on the ground and it doesn't really matter where it came from.
-
2. A finite state machine (where you only have one state at a time) does, not quite fit games. Instead I would have something more like a state stack. You push new states onto the stack and pop them off when they are done. You could go with something as simple as calling render() for everything in the stack or you could get a little bit more clever and only call render() down the stack until you reach a state to renders the entire screen. Similar rules can be applied to the other common functions.
However, what about the input and logic phases? Do you call that for every state on the stack or just the "top" one? And what pushes states on the stack (e.g. when going from "main" to "menu", where does the push occur)?
-
2. A finite state machine (where you only have one state at a time) does, not quite fit games. Instead I would have something more like a state stack. You push new states onto the stack and pop them off when they are done. You could go with something as simple as calling render() for everything in the stack or you could get a little bit more clever and only call render() down the stack until you reach a state to renders the entire screen. Similar rules can be applied to the other common functions.
However, what about the input and logic phases? Do you call that for every state on the stack or just the "top" one?
In a roguelike, you can probably go with just updating and drawing the top state.
-
2. A finite state machine (where you only have one state at a time) does, not quite fit games. Instead I would have something more like a state stack. You push new states onto the stack and pop them off when they are done. You could go with something as simple as calling render() for everything in the stack or you could get a little bit more clever and only call render() down the stack until you reach a state to renders the entire screen. Similar rules can be applied to the other common functions.
However, what about the input and logic phases? Do you call that for every state on the stack or just the "top" one?
In a roguelike, you can probably go with just updating and drawing the top state.
OK.
I've noticed something else now: If one has the game loop like:
Loop:
Input
Logic
Render
then, suppose we have to go to a menu. So the input tells the game to go to the menu state, via the "setNextState()" function. But... the logic and render phases of the loop will still execute on the main state, meaning it will try to expend a turn. In addition, when transitioning back to the main state, the game will try to get input again, and may not have done the logic for the turn (which requires the command to be done first, e.g. after dropping, then a turn should lapse, which means when the menu closes and the drop is called, we want to go directly to Logic and skip over Input for that one cycle of the loop.). How does one solve these problems?
-
1. Yeah, I have no problem with global variables. There's rarely the need to add further restrictions by turning it into a singleton.
...
5. It's always good idea to keep the core state of the game (data describing your player, items, creatures, etc.) separate from the UI. This is done by allowing UI code to read and write to it but the core state and the classes within do not know about the UI in reverse.
Generally, when a menu item is selected you will know what item was chosen. Given the context you're in you can then directly modify the core state e.g. drop the item from the player's pack.
-
OK.
I've noticed something else now: If one has the game loop like:
Loop:
Input
Logic
Render
then, suppose we have to go to a menu. So the input tells the game to go to the menu state, via the "setNextState()" function. But... the logic and render phases of the loop will still execute on the main state, meaning it will try to expend a turn. In addition, when transitioning back to the main state, the game will try to get input again, and may not have done the logic for the turn (which requires the command to be done first, e.g. after dropping, then a turn should lapse, which means when the menu closes and the drop is called, we want to go directly to Logic and skip over Input for that one cycle of the loop.). How does one solve these problems?
Unless you're doing fancy animations, the logic step should only be triggered off specific UI events. E.g., you won't advance the turn until the player actually does something (typically via a key press)
So it looks like this:
Loop:
Input (wait for key press)
Logic
Render
With fancy animations a similar concept still applies:
Loop (happens many times per second):
Detect UI input
Dispatch UI events
Logic
Update animations
Render
Note that animation state may be triggered off core state changes but, like UI state, it should be kept separate.
-
How does this address the problems with menus mentioned -- namely, when the game state changes back from the menu mode, how do we avoid getting input again for that one round of the loop where we do the logic for the turn?
-
You are always gathering input. However, when a significant input occurs, you need to act on it and resolve it. For example, if someone dismisses a menu then your UI state must be updated as such so that it is only handled once. In UI libraries this is commonly done by detecting the key press and dispatching a single KEY_PRESSED event. When your code handles that event, the core game state is updated and the menu is dismissed.
When you have a continually running UI input/render loop, you no longer necessarily do logic every single pass. Instead you might have a timer or something that invokes the logic update only once per second.
To learn more about UI states, I would study your favourite UI library to see how it does it. For example, if you're using Java then you can study awt/Swing.
-
You are always gathering input. However, when a significant input occurs, you need to act on it and resolve it. For example, if someone dismisses a menu then your UI state must be updated as such so that it is only handled once. In UI libraries this is commonly done by detecting the key press and dispatching a single KEY_PRESSED event. When your code handles that event, the core game state is updated and the menu is dismissed.
When you have a continually running UI input/render loop, you no longer necessarily do logic every single pass. Instead you might have a timer or something that invokes the logic update only once per second.
To learn more about UI states, I would study your favourite UI library to see how it does it. For example, if you're using Java then you can study awt/Swing.
Why would the logic update by timer, as opposed to by turn?
-
2. A finite state machine (where you only have one state at a time) does, not quite fit games. Instead I would have something more like a state stack. You push new states onto the stack and pop them off when they are done. You could go with something as simple as calling render() for everything in the stack or you could get a little bit more clever and only call render() down the stack until you reach a state to renders the entire screen. Similar rules can be applied to the other common functions.
I've been doing something like that with my Python roguelike projects.
The pyglet library's window object allows you to stack event handlers, and you can end them with "return False" to pass the handler down the stack or "return True" to stop it. So for example I have a main game mode which catches "ESC" and opens a menu mode. The menu mode is "on top" of the main game.
Rendering routines go bottom up, so the game is drawn and then the menu is superimposed on it, blocking part of it from view. Event handlers go in reverse. The menu mode, for example, catches "ESC" and closes the menu, but sends "return True" so that the "ESC" handler for the main game mode does not re-open the menu.
-
I've got another question about this "game state object"-based game loop: where should the data (like the map) the logic functions use go? I'd usually have it in the game object itself, if the logic function was a member of the game object, but here the logic function is a member of the game state object.
-
I've got another question about this "game state object"-based game loop: where should the data (like the map) the logic functions use go? I'd usually have it in the game object itself, if the logic function was a member of the game object, but here the logic function is a member of the game state object.
If you have a hierarchical state machine (stack of state effectively) I'd put the map the the root Game state. An example state hierarchy for a roguelike could look something like:
MainMenu
EnterName
ChooseRace
ChooseClass
Game
Main
Inventory
EndGame
HiScores
-
I've got another question about this "game state object"-based game loop: where should the data (like the map) the logic functions use go? I'd usually have it in the game object itself, if the logic function was a member of the game object, but here the logic function is a member of the game state object.
If you have a hierarchical state machine (stack of state effectively) I'd put the map the the root Game state. An example state hierarchy for a roguelike could look something like:
MainMenu
EnterName
ChooseRace
ChooseClass
Game
Main
Inventory
EndGame
HiScores
So then the Main and Inventory game sub-state objects would operate on the data in the Game object, right?
-
...
So then the Main and Inventory game sub-state objects would operate on the data in the Game object, right?
Yep, because those states always exist while a Game state exists it's ok for them to access data that Game is the owner of.
-
I also use a stack of state objects (I call them Screens in my code) but instead of allowing the states to grab each other's stuff, the shared data gets passed in to the sub-state objects.
That means the code ends up looking like this:
enterScreen(new ThrowItemScreen(world, player));
And each state object (such as ThrowItemScreen or PlayScreen) is completely isolated from other state objects. So the map is referenced by the PlayScreen, but also the ThrowItemScreen, ExamineScreen, and probably a few others.
-
I also use a stack of state objects (I call them Screens in my code) but instead of allowing the states to grab each other's stuff, the shared data gets passed in to the sub-state objects.
That means the code ends up looking like this:
enterScreen(new ThrowItemScreen(world, player));
And each state object (such as ThrowItemScreen or PlayScreen) is completely isolated from other state objects. So the map is referenced by the PlayScreen, but also the ThrowItemScreen, ExamineScreen, and probably a few others.
This is definitely the tidiest way to do it but it depends on the state library or pattern you're using. Some of them don't allow arguments on state constructors.
-
I've got another question about this "game state object"-based game loop: where should the data (like the map) the logic functions use go? I'd usually have it in the game object itself, if the logic function was a member of the game object, but here the logic function is a member of the game state object.
If you have a hierarchical state machine (stack of state effectively) I'd put the map the the root Game state. An example state hierarchy for a roguelike could look something like:
MainMenu
EnterName
ChooseRace
ChooseClass
Game
Main
Inventory
EndGame
HiScores
I just recently made a system with states and "sub-states" similar to this. However, I've hit a new snag: Consider the "inventory" state. The title of the inventory window may vary depending on what command is given to access it, like drop, for example. How should this be communicated? It also needs this in order to know what action to take after finishing with the window, like the just-mentioned drop.
-
...
I just recently made a system with states and "sub-states" similar to this. However, I've hit a new snag: Consider the "inventory" state. The title of the inventory window may vary depending on what command is given to access it, like drop, for example. How should this be communicated? It also needs this in order to know what action to take after finishing with the window, like the just-mentioned drop.
Normally I organise it so that my states have arguments when you change to them. So I would pass the title and action on selection into that. It can be tricky to arrange it though depending on the language.
-
So I would pass the title and action on selection into that. It can be tricky to arrange it though depending on the language.
Parameterizing things like the title and the action is a good idea and keeps things simple. Another alternative is to create subclasses or substates for each variation. One for Drop, one for Quaff, etc.
-
Thanks for the answer.
Now I have another question. Suppose we have the player performing an action which takes multiple turns. During the monster actions for one of the turns, a monster attacks the player. We want to put a confirmation to continue or abort the action right there. But that requires switching out of the logic for that loop cycle while we go into the "confirmation prompt" state, and then back to resume the logic at that point. Or is there some other way of doing it (like making each cycle of the input-logic-render loop be at a finer scale than "one turn"?)?
-
Or is there some other way of doing it (like making each cycle of the input-logic-render loop be at a finer scale than "one turn"?)?
There's a lot of ways it could be done, but if you're already using different states, then I'd start with using a new "confirmation state". If it becomes a bother to use then try different things. Relying on the same way to do things (like with states) will keep your code much cleaner and easier to understand and modify. If you start doing the same thing a bunch of different ways then your codebase can quickly become a bunch of hacks and workarounds and conflicting ideas.
-
Or is there some other way of doing it (like making each cycle of the input-logic-render loop be at a finer scale than "one turn"?)?
There's a lot of ways it could be done, but if you're already using different states, then I'd start with using a new "confirmation state". If it becomes a bother to use then try different things. Relying on the same way to do things (like with states) will keep your code much cleaner and easier to understand and modify. If you start doing the same thing a bunch of different ways then your codebase can quickly become a bunch of hacks and workarounds and conflicting ideas.
Yes, that's what I was thinking of, but I then run into the problem of resuming the logic routine at the right point depending on what was selected in the confirmation prompt. What to do about that?
-
Would having some kind of variable in the game state objects telling where we left off and resuming at that point be good (since the confirmation prompts occur as part of a command, this could be done in the command-processing stage of the logic)?
-
You could use variables to track what you need to do next. Another - I think cleaner - possibility is passing in what to do next. So in my game Pugnacious Wizards 2, there's a TargetDirectionScreen that takes a callback that get's called with the direction the user chose. I'm not sure what language you're using but most modern ones allow something like that.
Here's the game-state object where the user picks a direction:
https://github.com/trystan/PugnaciousWizards2/blob/master/src/screens/TargetDirectionScreen.as
And here's one case where it's used. The "cast" function is passed in as a callback:
https://github.com/trystan/PugnaciousWizards2/blob/master/src/spells/MagicMissile.as#L24
The MagicMissile spell itself also takes a callback so it can end the current turn after being cast without having a direct dependency on the core update logic.
That's one way of handling these multi-step processes.
-
You could use variables to track what you need to do next. Another - I think cleaner - possibility is passing in what to do next. So in my game Pugnacious Wizards 2, there's a TargetDirectionScreen that takes a callback that get's called with the direction the user chose. I'm not sure what language you're using but most modern ones allow something like that.
Here's the game-state object where the user picks a direction:
https://github.com/trystan/PugnaciousWizards2/blob/master/src/screens/TargetDirectionScreen.as
And here's one case where it's used. The "cast" function is passed in as a callback:
https://github.com/trystan/PugnaciousWizards2/blob/master/src/spells/MagicMissile.as#L24
The MagicMissile spell itself also takes a callback so it can end the current turn after being cast without having a direct dependency on the core update logic.
That's one way of handling these multi-step processes.
But how does this reconcile with the loop as specified? I notice this program isn't set up with explicitly-labeled "input, logic, render" methods in the states. In that system, transitions between states would only happen at the end of an input-logic-render cycle. So if we were to use the callback approach, how would it work there? It's due to that fact that the input-logic-render cycle has to end before transitions, as opposed to being able to enter new states "immediately", that leads to the need for some kind of control variable. How would you handle it in that situation with a callback? Suppose the handling of a command requires a menu, and then after the menu, the logic for the turn must be processed. So the menu state would take a callback to perform the command logic, which it then goes to upon completion of the menu, but if we now have a callback to perform the turn logic, we're still "in the menu state", as it's still on the stack and we have called the turn logic from the command logic which in turn was called from inside the menu state. We want to go back to the main state to handle the logic for the turn. How do you handle this?
-
But how does this reconcile with the loop as specified? I notice this program isn't set up with explicitly-labeled "input, logic, render" methods in the states. In that system, transitions between states would only happen at the end of an input-logic-render cycle. So if we were to use the callback approach, how would it work there? It's due to that fact that the input-logic-render cycle has to end before transitions, as opposed to being able to enter new states "immediately", that leads to the need for some kind of control variable. How would you handle it in that situation with a callback? Suppose the handling of a command requires a menu, and then after the menu, the logic for the turn must be processed. So the menu state would take a callback to perform the command logic, which it then goes to upon completion of the menu, but if we now have a callback to perform the turn logic, we're still "in the menu state", as it's still on the stack and we have called the turn logic from the command logic which in turn was called from inside the menu state. We want to go back to the main state to handle the logic for the turn. How do you handle this?
I don't know mike3. All this talk is just speculation. Why don't you sit down and actually code it and see how it turns out? Don't make a full-blown roguelike - just make the minimum needed to validate some of these ideas. Try one where you use a bunch of variables to track what you were doing before transitioning to a new state. Try one where you use callbacks to say what to do next (it's called continuation passing style and is very common in javascript frameworks among other places). Try one where you have objects that specifically control the program flow and move all the transitioning logic there. Try one where states can 'freeze' their internal state and 'thaw' it when control returns to them. Try one where the states are implemented as statemachines and each main-loop moves it to the next state with a final 'update world' state.
A stack of loosely-coupled states that can be chained together with callbacks is the approach I've been trying lately and it works well for me. If you're unable to do that, or if it doesn't work for you, then try something else.
-
I'm going to go ahead and recommend you try Game Coding Complete 4th Ed. again. I've been interested in these issues for a while, and had a system working already, but after reading it decided for an almost complete rewrite, which I'm doing at the moment.
I've already written about it elsewhere, but their system is pretty flexible, and you really only need to read a few of the first chapters to get the gist of how to organise an engine in a very flexible way. It includes the events discussion that has been part of this thread, as well as organisation of the main loop in a generic and, again, flexible way, composition instead of inheritance, and GUIs and stuff in there as well.
There's so much stuff around that at some point you're just going to have to make a decision and live with it. Eventually perhaps do a rewrite, refactor, or whatever you want to call it. Perhaps just a new iteration of the engine :)