9
\$\begingroup\$

I'm currently creating a 2D RPG in C++11 with Allegro 5 and boost.

My goal is to somehow update my game settings when an option is changed in the Options Menu. I don't want to force the user to restart my game. Other games don't require a restart when changing resolution or going from fullscreen to windowed, so my game shouldn't either. Please see a simplified view of the system below.

Please note that I am not necessarily wanting to directly call my Game object from the OptionsScreen. The dashed line is merely to illustrate the effect I am trying to achieve; to somehow cause an update of the game when an option is changed in a different part of the system.

Detailed explanation

The ScreenManager contains a list of all GameScreen objects currently in existence. These will be various screens in the game including popups. This design adheres more or less to the Game State Management sample in C#/XNA.

The ScreenManager contains a reference to my Game object. The Game object initializes and modifies the game's settings. If I want to change the resolution, go fullscreen, or mute the volume I would do so in the Game class.

However, the OptionsScreen currently cannot access the Game class. See below diagram:

A GameScreen can signal three events, onFinished, onTransitionStart and onTransitionEnd. There is no onOptionsChanged because only one screen does that. The ScreenManager cannot set-up the event handling for that because it handles all screens as GameScreens.

My question is, how can I change my design so that a change in the OptionsMenu doesn't require a restart, but is changed immediately? I would preferably request my Game object to update once the apply button is clicked.

\$\endgroup\$
3
  • \$\begingroup\$ This question (even though it is game related) looks like a perfect fit for programmers.stackexchange.com because it's more about general OO design than about the actual game \$\endgroup\$
    – bughi
    May 4, 2013 at 19:58
  • \$\begingroup\$ Where'd you get such an awesome looking graphs? \$\endgroup\$ May 13, 2013 at 13:01
  • \$\begingroup\$ @psycketom: The first is from Visio 2013 and the second are generated from code by VS2012. Hope that helps! \$\endgroup\$
    – IAE
    May 13, 2013 at 14:29

5 Answers 5

1
\$\begingroup\$

From what I've seen, the easiest approach is read an options file on startup to determine current display settings; then, when your options screen is displayed, load all current options from a file.

When changes are finalized via an apply or ok button, they are saved back to a file. If any changes effect the display, notify the user that the game must be restarted for them to take effect.

When the game is restarted, the (now new) display settings are again read from the file.

--EDIT--

Annd...it would have helped if I noticed that last sentence. You don't want to have to restart. Makes things a little more difficult depending on your implementation and your back-end Graphics Library.

IIRC, Allegro has a function call that allows you to change the display settings on the fly. I'm not up-and-up on Allegro 5 just yet, but I know you could in 4.

\$\endgroup\$
6
  • \$\begingroup\$ I don't think the issue is related to Allegro and its capabilities. "My question is, how can I change my design so that a change in the OptionsMenu doesn't require a restart, but is changed immediately?" The restart is because the "OptionsScreen currently cannot access the Game class" and has to load them from the setting file if I understand correctly. \$\endgroup\$ May 1, 2013 at 21:39
  • \$\begingroup\$ @Casey: Hello Casey! :) Yes, I am using a configuration file to store the relevant display data, but I want to be able to avoid the restart. It's a small game, and other games can change resolution without having to restart, so I don't see why I should force the user to restart with mine. It's a usability issue. While I could just call the allegro dispay functions, I'm trying to stay OOP and not mix my graphics calls just because I can; I don't consider that good design. \$\endgroup\$
    – IAE
    May 2, 2013 at 3:03
  • \$\begingroup\$ I highlighted the "without a restart" bit so others don't trip up on the same last sentence. Such an important fact should not have come at the end, I apologize ): \$\endgroup\$
    – IAE
    May 2, 2013 at 3:09
  • \$\begingroup\$ @SoulBeaver Who says you must make it so you don't restart? It's a viable thing to require that, in fact it's becoming more common now-a-days. Some recent releases (XCOM: Enemy Unknown, Skyrim, etc.) of big-budget games require a restart. (the fact that they are on Steam may be the culprit due to its server-side syncing of options but let's not go there...) \$\endgroup\$
    – Casey
    May 2, 2013 at 6:09
  • 1
    \$\begingroup\$ @Casey: True, and for certain options I can see why that is even necessary, but for things like fullscreen/windowed or the screen resolution? I've never seen a game that required a restart for those settings. My basic question is: why should I force my user to restart when the game can change the settings automatically? \$\endgroup\$
    – IAE
    May 2, 2013 at 6:31
1
\$\begingroup\$

This is what I do for my game. I have 2 separate functions for initialising stuff, 'init' and 'reset'. Init is only called once at startup and does things that do not rely on any settings, such as loading main assets. Reset does things like laying out the UI based on screen resolution, so is called every time the settings change.

init();
bool quit = false;
while( !quit )
{
    createWindow();
    reset();

    bool settings_changed = false;
    while( !quit && !settings_changed )
    {
        ... main loop
        // set quit=true if user clicks 'quit' in main menu
        // set settings_changed=true if user clicks 'apply' in options
    }

    destroyCurrentWindow();
}

I am not familiar with Allegro, but my answer is quite general, so I hope it helps you or anyone else with a similar problem.

\$\endgroup\$
1
  • \$\begingroup\$ That's an interesting solution. I was trying to have a tight control over whenever a reset is called, but you do it regardless of a change. That's definitely something I could implement, and I'll look into it, thanks! \$\endgroup\$
    – IAE
    May 2, 2013 at 5:45
1
\$\begingroup\$

Without messing your current architecture, I see two ways. First, you could store a pointer to the Game instance in the OptionsScreen class. Second, you could the Game class fetch current settings in a given interval, say every second.

To actually adapt to the new settings, the Game class has to implement some sort of reset functions which fetches the current settings and reinitializes based on those.

For a clean solution you need a global manager of some kind, thus it is more effort to implement. For example a event system or messaging system. It is very useful to let classes communicate without such strong bindings like aggregation or composition, and so on.

With a global event manager, the OptionsScreen could simply globally fire a redraw event which Game has registered to listen to before.

Generally you can implement a manager class storing events and callbacks listening to them in a hash map. Then you can create a single instance of that manager and pass pointers to it to your components. Using newer C++ it's quite easy since you can use std::unordered_map as hash map and std::function to store callbacks. There are different approaches what you can use as key. For example, you could make the event manager string based which makes components even more independent. In that case you would use std::string as key in the hash map. I personally like this and it is definitely no performance issue, but most traditional event systems work with events as classes.

\$\endgroup\$
2
  • \$\begingroup\$ Hello danijar. I'm already using boost::signals to set up a rudimentary event-handling scheme. The strong line from the GameScreen to the ScreenManager is actually that event-handling. Do you have any resources detailing the architecture of a global event manager? More work or not, this sounds like it could lead to a clean implementation. \$\endgroup\$
    – IAE
    May 2, 2013 at 8:14
  • \$\begingroup\$ Though I haven't read any research about this, I implemented a global event event manager for my own little engine. If you are interested in the implementation, I'll send you a link. I updated my answer to cover more detail. \$\endgroup\$
    – danijar
    May 2, 2013 at 17:27
1
\$\begingroup\$

Well, this is some specific case of the Observer pattern.

There is a solution that involves callbacks. This is the best way of doing this if you want loose coupling, and I think it's also the cleanest one. This won't involve any global managers or singletons.

Basically, you'll need to have some sort of SettingsStore. There you store the settings. When you create a new screen they'll need a pointer to the store. In the case of the OptionsScreen it will modify some of the settings value itself. In the case of the GameScreen it will just read them. So, in your game you would create just one instance, which will be passed along all screens that require one.

Now, that SettingsStore class will have a list of notifiables. They are classes that implement a certain ISettingChanged interface. The interface would be a simple one that contains the following method:

void settingChanged(std::string setting);

Then in your screen you will implement the logic for each setting you care about. Then, add yourself to the store to be notified: store->notifyOnChange(this);. When a setting is changed, the callback is called with the setting name. The new setting value can then be retrieved from the SettingsStore.

Now, this can be further augmented with the following ideas:

  • Use a class that stores the settings strings - may be even the SettingsStore (const strings) in order to prevent copy-pasting strings around.
  • Even better, instead of a string, use an enum to specify at what settings you have
  • You could even specify the old and new values as arguments in the callback, but that would be more convoluted, since you can have string or int values, or whatnot. It's up to you.
\$\endgroup\$
0
\$\begingroup\$

Read your settings from a file into into variables. Have your Screen Manager track if the screen it just came from was the Options screen, and if it was, reload your settings from the variables. When the user is exiting your game, write the settings in the variables back to the file.

\$\endgroup\$

You must log in to answer this question.

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