如何在不重新启动的情况下从选项屏幕更新显示设置?


9

我目前正在使用Allegro 5和boost在C ++ 11中创建2D RPG。

我的目标是在“选项”菜单中更改选项后以某种方式更新我的游戏设置。我不想强迫用户重新启动我的游戏。其他游戏在更改分辨率或从全屏模式切换到全屏模式时不需要重启,因此我的游戏也不需要重启。请在下面查看系统的简化视图。

请注意,我不一定要直接从OptionsScreen调用我的Game对象。虚线仅仅是为了说明我要达到的效果。当在系统的不同部分中更改选项时,以某种方式导致游戏更新。

详细说明

ScreenManager包含GameScreen当前存在的所有对象的列表。这些将是游戏中的各种屏幕,包括弹出窗口。该设计或多或少地遵循C#/ XNA中游戏状态管理示例

ScreenManager包含了我的参考Game对象。该Game对象初始化和修改游戏的设置。如果要更改分辨率,请全屏播放或静音,这是我在Game课堂上要做的。

但是,OptionsScreen当前无法访问Game类。见下图:

GameScreen可以发信号三个事件onFinishedonTransitionStartonTransitionEnd。没有,onOptionsChanged因为只有一个屏幕可以做到这一点。ScreenManager无法为此设置事件处理,因为它将所有屏幕都处理为GameScreen

我的问题是,如何更改设计以使OptionsMenu中的更改不需要重新启动,而是立即更改?我最好Game在单击“应用”按钮后请求更新我的对象。


这个问题(即使是与游戏相关的)也很适合程序员
。stackexchange.com,

您从哪里得到如此出色的图形?
joltmode

@psycketom:第一个来自Visio 2013,第二个来自VS2012的代码。希望有帮助!
IAE

Answers:


1

据我所知,最简单的方法是在启动时读取选项文件,以确定当前的显示设置。然后,在显示选项屏幕时,从文件中加载所有当前选项。

通过applyok按钮完成更改后,会将更改保存回文件。如果有任何更改影响显示,请通知用户必须重新启动游戏才能使它们生效。

重新启动游戏后,再次从文件中读取(现在是新的)显示设置。

- 编辑 -

安德...如果我注意到最后一句话,那会有所帮助。您不需要重启。根据您的实现和后端图形库,使事情变得有些困难。

IIRC,Allegro有一个函数调用,可让您即时更改显示设置。我还没有对Allegro 5进行全面了解,但我知道您可以在Allegro 5中进行。


我认为该问题与Allegro及其功能无关。“我的问题是,如何更改我的设计,以使OptionsMenu中的更改不需要重新启动,而是立即更改?” 重新启动是因为“ OptionsScreen当前无法访问Game类”,如果我理解正确的话,必须从设置文件中加载它们。
ClassicThunder13年

@Casey:你好,Casey!:)是的,我正在使用配置文件来存储相关的显示数据,但是我希望能够避免重新启动。这是一款小游戏,其他游戏无需重新启动即可更改分辨率,因此我不明白为什么要强制用户使用我的重新启动。这是一个可用性问题。虽然我可以调用Allegro Dispay函数,但是我试图保持OOP而不是仅仅因为我可以就混用我的图形调用。我认为设计不是很好。
IAE

我突出显示了“不重新启动”位,以使其他人不会在最后一个句子上不满意。抱歉,这样重要的事实不应该在结尾出现):
IAE

@SoulBeaver谁说您必须做到这一点,才能不重新启动?要求这样做是可行的,事实上,如今它正变得越来越普遍。一些最新的大型预算游戏(XCOM:未知敌人,天际等)需要重新启动。(由于它们在服务器端同步了选项,因此它们之所以在Steam上是罪魁祸首,但我们不要去那里……)
Casey

1
@Casey:是的,对于某些选项,我可以看到为什么这样做甚至是必要的,但是对于全屏/窗口式或屏幕分辨率而言呢?我从未见过需要重新启动这些设置的游戏。我的基本问题是:当游戏可以自动更改设置时,为什么要强制用户重新启动?
IAE

1

这就是我为游戏所做的。我有2个单独的函数来初始化东西,“ init”和“ reset”。Init在启动时仅被调用一次,并且执行不依赖任何设置的事情,例如加载主资产。重置会执行一些操作,例如根据屏幕分辨率对用户界面进行布局,因此每次更改设置都会被称为重置。

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();
}

我不熟悉Allegro,但是我的回答很笼统,因此希望它对您或其他有类似问题的人有所帮助。


这是一个有趣的解决方案。我试图严格控制每次调用重置的时间,但是无论更改如何,您都可以这样做。这绝对是我可以实现的东西,我会仔细研究,谢谢!
IAE

1

在不弄乱您当前的体系结构的前提下,我看到了两种方法。首先,您可以GameOptionsScreen类中存储一个指向实例的指针。其次,您可以让Game类在给定的间隔(例如每秒)中获取当前设置。

为了实际适应新设置,Game该类必须实现某种重置功能,该功能可以获取当前设置并根据这些设置进行初始化。

对于一个干净的解决方案,您需要某种类型的全球经理,因此实施起来会更加费力。例如事件系统或消息传递系统。让类在没有诸如聚合或组合之类的强约束的情况下进行通信非常有用。

使用全局事件管理器,OptionsScreen可以简单地全局触发Game已注册以供收听的重绘事件。

通常,您可以实现一个管理器类,用于在哈希图中存储事件和回调以监听事件。然后,您可以创建该管理器的单个实例,并将指向它的指针传递给您的组件。使用较新的C ++非常容易,因为您可以std::unordered_map用作哈希映射并std::function存储回调。您可以使用多种方法作为密钥。例如,您可以基于事件管理器字符串,从而使组件更加独立。在这种情况下,您可以将其std::string用作哈希图中的键。我个人喜欢这样,这绝对不是性能问题,但是大多数传统事件系统都将事件作为类来使用。


你好danijar。我已经在使用boost :: signals来设置基本的事件处理方案。从GameScreen到ScreenManager的强项实际上就是事件处理。您是否有任何资源详细介绍了全局事件管理器的体系结构?是否需要做更多的工作,这听起来可能会导致干净的实现。
IAE

尽管我尚未阅读有关此方面的任何研究,但我为自己的小引擎实现了一个全局事件事件管理器。如果您对实现感兴趣,我将向您发送一个链接。我更新了答案以涵盖更多细节。
danijar

1

好吧,这是观察者模式的一些特定情况。

有一个涉及回调的解决方案。如果您要松散耦合,这是最好的方法,我认为这也是最干净的方法。这不会涉及任何全球经理或单身人士。

基本上,您需要使用某种SettingsStore。在此存储设置。创建新屏幕时,他们将需要一个指向商店的指针。在的情况下,OptionsScreen它将修改一些设置值本身。在这种情况下,GameScreen它将只读取它们。因此,在您的游戏中,您将只创建一个实例,该实例将在需要一个的所有屏幕中传递。

现在,该SettingsStore课程将有一个列表notifiables。它们是实现特定ISettingChanged接口的类。该接口将是一个简单的接口,其中包含以下方法:

void settingChanged(std::string setting);

然后,在屏幕上,您将为您关心的每个设置实现逻辑。然后,将您自己添加到商店中,以得到通知:store->notifyOnChange(this);。更改设置后,将使用设置名称来调用回调。然后可以从中检索新的设置值SettingsStore

现在,可以通过以下构想进一步增强它:

  • 请使用存储设置字符串的类-甚至可以是SettingsStore(常量字符串),以防止周围出现复制粘贴字符串。
  • 更好的是,使用一个枚举来指定您所拥有的设置,而不是字符串
  • 您甚至可以将旧值和新值指定为回调中的参数,但这会更加复杂,因为您可以具有字符串或整数值,或者没有其他值。由你决定。

0

将您的设置从文件读入变量。让屏幕管理器跟踪它刚来自的屏幕是否是“选项”屏幕,如果是,则从变量中重新加载设置。当用户退出游戏时,将变量中的设置写回到文件中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.