4
\$\begingroup\$

Assume you're working on an RPG like Fallout and you have all sorts of flags and variables tracking the state of the world. Stuff like:

  • Talked to NPC X
  • Opened chest Y
  • Completed quest 1
  • Visited some dungeon
  • Defeated 30 goblins
  • etc.

You also want to save and load this information. My question is about the best way to structure this information. You could simply have a big global list of variables, but it's obviously better to break it up into smaller scopes. You could associate certain states with the current location, objects/NPCs, quests, or other scopes, but occasionally you have truly global information shared between multiple quests and locations. Furthermore, sometimes it's difficult to tell where the state belongs and you have to spend time thinking where to store it or find it.

The answers to this question suggest using a finite state automation, which might be fine for some examples like badge progress in Pokemon or keeping track of quests in a game, but it can get really complex if you have a more open game with a large number of states and hidden variables that influence multiple quests and interactions.

So to iterate, what are some good approaches for structuring game state? (particularly for RPGs and adventure games)

\$\endgroup\$
2
  • 1
    \$\begingroup\$ What do you want to achieve exactly? Your structure might be dependent on what you want - in games such as Skyrim, they basically just store all the flags since the gameplay is nonlinear, and a FSM is far from being the most optimal solution. The quests then look up their own flags in the database when you get close enough to them to trigger. \$\endgroup\$
    – Kanadaj
    Jun 16, 2014 at 17:08
  • \$\begingroup\$ Dracor: I'm referring to something like Skyrim. Do you store all the flags in a global list or break it up into smaller scopes to reduce clashes and simplify reasoning and finding these flags? If it's the latter, what's the best way to structure it in the context of an RPG with locations, maps, npcs, quests, etc. \$\endgroup\$ Jun 16, 2014 at 17:11

1 Answer 1

6
\$\begingroup\$

Your list includes several things which should have their tracking abstracted separately, rather than pooled into a single global pile of state.

For example, quest completion is something that all quests will track. Similarly, all dungeons will likely need to have their completion status tracked. However, dungeons are generally a binary value (you completed it, or you didn't) whereas quests might not be (you may have finished it, but did you succeed or fail?). So while you might have functions like DidFinishDungeon(dungeon) and DidFinishQuest(quest), the former will probably just return a Boolean value while the latter could very well return some kind of QuestResult object.

By way of contrast, you probably won't want to track whether or not every NPC has or has not been talked to. That's generally handed by setting and checking scripting flags within the dialog or script system.

You'll probably want to track chest opening, at least for special chests with unique rewards, but you'll probably want to store that data in a dedicated block of character data representing chest-recovery state (or perhaps tie it to dungeon completion state, if your game mechanics allow so).


Underneath the abstraction, you can store all the Boolean values for a character together in a single blob of storage, using "progress bits." This sort of storage is basically an array of integer values used to represent a collection of bits; you assign bit indices to particular events (such as "talked to Farmer Bob") and provide a way to expose the checking and setting of those bits to dialog or scripting systems in your game. You'll also want a want to recycle the indices of bits you remove or repurpose, so you don't leave holes in the address space; good toolset support really helps here, although if you game is small enough in scope you can simply track this manually in a spreadsheet or something.

It generally makes the most sense to use this system for more freeform "player has done stuff" flags, like talking to NPCs, reaching certain parts of a level to trigger an event, et cetera. One-off things for which you only need to store a true/false value. In the interest of reducing dependencies and keeping the progress bit space confined, I'd store things like quest/dungeon completion and chest-opening data elsewhere, in a section dedicated to that storage.

Achievement-like data ("kill 30 goblins") is similarly better off stored with or by the achievement itself, because it likely varies so wildly between achievements and often requires more than a single bit to store progress towards the achievement. You could also extend the progress bit system to allow for both toggle bits and range bits, since you are only really limited in storage by the memory/storage limits you impose on yourself for you game.

\$\endgroup\$
3
  • \$\begingroup\$ This is a good answer. I can certainly see how some things could be abstracted (like what you said about quests and dungeons). I'm a little worried about the large number of scripting flags and keeping track of them. Additionally, something like "kill 30 goblins" could be used in multiple contexts: achievement, quest, hidden modifier in NPC interaction (goblins won't talk to you any more!), etc. \$\endgroup\$ Jun 16, 2014 at 17:17
  • \$\begingroup\$ Guild Wars 2 uses a progress bit system like I described; the number of bits is large and tools are effectively required to track them. GW2 has far fewer such bits as compared to a single-player RPG as well. You'll definitely want good tools if you choose that route. It can be far more manageable to split everything into specific categories. \$\endgroup\$
    – user1430
    Jun 16, 2014 at 17:26
  • \$\begingroup\$ Good point re: "kill 30 goblins," you could provide APIs for querying that stuff from whatever you decide is the primary authority for the data. \$\endgroup\$
    – user1430
    Jun 16, 2014 at 17:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .