23
\$\begingroup\$

I have an options screen for things like difficulty, resolution, full-screen, etc., but I'm struggling to find the "best" way to store/obtain these variables at run-time.

Currently, I've implemented a Constants class which contains all the GameOption enums, but how do I choose a default for all of these options? Also, how do I get the currently selected enum?

Regarding the resolution, specifically, I've decided to store the values, but I'm unsure of how to get the default, or currently stored, values. Any direction would be great; thanks! :)

namespace V1.test.RPG
{
  public class GameOptions
  {
    public enum Difficulty { EASY, MEDIUM, HARD }
    public enum Sound { ON, QUIET, OFF }
    public enum Music { ON, QUIET, OFF }
    public enum ResolutionWidth
    {
        SMALL      = 1280,
        MEDIUM     = 1366,
        LARGE      = 1920,
        WIDESCREEN = 2560
    }
    public enum ResolutionHeight
    {
        SMALL      = 800,
        MEDIUM     = 768,
        LARGE      = 1080,
        WIDESCREEN = 1080
    }
    public Boolean fullScreen = false;
  }
}

NB: I asked over at SO and they pointed me to this place. There is a comment there but I'd like to hear different ways of doing it / the most used ways.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ You asked in the right place; whoever sent you over here was mistaken. I answered the question anyway to help you out, but this isn't a game development specific question, this is a general programming question. \$\endgroup\$
    – jhocking
    Jul 13, 2014 at 19:16
  • \$\begingroup\$ I just read the SO thread; I like Scott Chamberlin's answer. \$\endgroup\$
    – jhocking
    Jul 13, 2014 at 19:28
  • \$\begingroup\$ @jhocking I pointed him this way in case there's any aspects which are particular to game development that might differ from an ordinary application. I also figured you guys might already have a canonical Q&A on this topic since it's so common. \$\endgroup\$ Jul 14, 2014 at 16:25
  • \$\begingroup\$ Tangential to the actual question about globals, please don't assume there's a fixed set of resolutions out there. \$\endgroup\$ Apr 16, 2015 at 6:26

3 Answers 3

32
\$\begingroup\$

Planning to grow:
Hard-coded constants are fine for small projects but, eventually, as your software grows in size, you will wish you could change those settings without having to recompile everything. There are many times you will want to change settings while the game is running and you can't do that with hard-coded constants.

CVars:
Once your project grows, you might want to take a look at CVARs. A CVAR is a "smart variable", so to speak, that you can modify during run-time via a console, terminal, or UI. CVARs are usually implemented in terms of an object that wraps an underlying value. The object can, then, keep track of the value as well as save/load it to/from file. You can store the CVAR objects into a map to access them with a name or other unique identifier.

To illustrate the concept a bit further, the following pseudo-code is a simple example of a CVAR type that wraps an int value:

// just some flags to exemplify:
enum CVarFlags {
    CVAR_PERSISTENT, // saved to file once app exits
    CVAR_VOLATILE    // never saved to file
};

class CVar {
public:
    // the constructor registers this variable with the global list of CVars
    CVar(string name, int value, int defaultValue, int flags);

    int getValue();
    void setValue(int v);
    void reset(); // reset to the default value

    // etcetera...

private:
    int flags; // flags like: save-to-file, etc.
    int value; // the actual value
    int defaultValue; // the default value to reset the variable to
};

// global list of all CVars:
map<string, CVar> g_cvars;

Global access:
In the example above, I've assumed that the constructor of CVar always registers the variable with the global cvars map; this is quite useful, since it allows you to declare a variable like so:

CVar my_var = new CVar("my_var", 0, 42, CVAR_PERSISTENT);

That variable is automatically made available in the global map and you can access it from anywhere else by indexing the map with the variable's name:

CVar v = g_cvars.find("my_var");

Persistence:
When the game is shutting down, iterate the map and save all of the variables marked as CVAR_PERSISTENT, to a file. The next time the game starts, reload them.

Case law:
For a more-specific example of a robust CVAR system, check out the implementation featured in Doom 3.

\$\endgroup\$
4
\$\begingroup\$

Well first off an enum defines what the values can be, not what the values are. Thus you still need to declare another variable after you've declared the enum. For example:

public enum Sound
{
    ON,
    QUIET,
    OFF
}

public Sound soundValue;

In this example, you can now set soundValue to ON, QUIET, or OFF.


Then you still need to structure your code so that other parts of your code can access this "settings" object. I don't know if you need help with that part too, but common patterns to address this problem include singletons (those are frowned on these days) or service locators or dependency injection.

\$\endgroup\$
1
\$\begingroup\$

glampert solution is very complete, but I will add my personal experience.

I ran into this same problem, and my solution was to use an static Variables class.

The Variables class internally keeps a map from string to string (so far all my variables are only strings) and is accessed via getters and setters.

The point is that getting access to global variables can introduce all kind of subtle errors as totally unrelated parts of the code are suddenly interfering with each other.

In order to avoid this, I imposed the following semantics: using the set method throws an exception if a variable with that name already exists in the dictionary, and get deletes the variable from the dictionary before returning it.

Two additional methods provide what you would expect, setAndOverwrite and getAndKeep. The point of the semantic of the other methods is that you can easily spot errors of the kind "this method is supposed to initialize this variable, but other method did it before".

In order to initialize the dictionary, initial variables are stored in a json file, and then read when the game starts.

Sadly I haven't got too far away with my game yet so I cannot testify for the robustness of this approach. Still, maybe it can provide something interesting on top of CVARs.

\$\endgroup\$

You must log in to answer this question.

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