Author Topic: "Game state"-based game loop.  (Read 36145 times)

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
"Game state"-based game loop.
« 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.

ExtremoPenguin

  • Newcomer
  • Posts: 19
  • Karma: +0/-0
    • View Profile
    • Entropy Interactive
Re: "Game state"-based game loop.
« Reply #1 on: May 28, 2013, 01:07:22 AM »
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.

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: "Game state"-based game loop.
« Reply #2 on: May 28, 2013, 01:35:00 AM »
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?

ExtremoPenguin

  • Newcomer
  • Posts: 19
  • Karma: +0/-0
    • View Profile
    • Entropy Interactive
Re: "Game state"-based game loop.
« Reply #3 on: May 28, 2013, 02:02:17 AM »
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.

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: "Game state"-based game loop.
« Reply #4 on: May 28, 2013, 03:16:21 AM »
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.)

ExtremoPenguin

  • Newcomer
  • Posts: 19
  • Karma: +0/-0
    • View Profile
    • Entropy Interactive
Re: "Game state"-based game loop.
« Reply #5 on: May 28, 2013, 03:34:41 AM »
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).

tuturto

  • Rogueliker
  • ***
  • Posts: 259
  • Karma: +0/-0
    • View Profile
    • pyherc
Re: "Game state"-based game loop.
« Reply #6 on: May 28, 2013, 05:06:32 AM »
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.

Everyone you will ever meet knows something you don't.
 - Bill Nye

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: "Game state"-based game loop.
« Reply #7 on: May 28, 2013, 11:10:28 AM »
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?

miki151

  • Rogueliker
  • ***
  • Posts: 264
  • Karma: +0/-0
    • View Profile
Re: "Game state"-based game loop.
« Reply #8 on: May 28, 2013, 12:28:17 PM »
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).
KeeperRL, Dungeon Keeper in roguelike style:
http://keeperrl.com

ExtremoPenguin

  • Newcomer
  • Posts: 19
  • Karma: +0/-0
    • View Profile
    • Entropy Interactive
Re: "Game state"-based game loop.
« Reply #9 on: May 28, 2013, 02:39:18 PM »
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.

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: "Game state"-based game loop.
« Reply #10 on: May 29, 2013, 02:54:25 AM »
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)?
« Last Edit: May 29, 2013, 03:09:53 AM by mike3 »

ExtremoPenguin

  • Newcomer
  • Posts: 19
  • Karma: +0/-0
    • View Profile
    • Entropy Interactive
Re: "Game state"-based game loop.
« Reply #11 on: May 29, 2013, 03:05:28 AM »
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.

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: "Game state"-based game loop.
« Reply #12 on: May 29, 2013, 03:31:17 AM »
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?
« Last Edit: May 29, 2013, 03:36:31 AM by mike3 »

loom_weaver

  • Newcomer
  • Posts: 23
  • Karma: +0/-0
    • View Profile
Re: "Game state"-based game loop.
« Reply #13 on: May 29, 2013, 03:59:44 AM »
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.

loom_weaver

  • Newcomer
  • Posts: 23
  • Karma: +0/-0
    • View Profile
Re: "Game state"-based game loop.
« Reply #14 on: May 29, 2013, 04:07:05 AM »
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.
« Last Edit: May 29, 2013, 04:13:32 AM by loom_weaver »