Как я могу обновить настройки дисплея с экрана параметров без перезагрузки?

9

В настоящее время я создаю 2D RPG на C ++ 11 с Allegro 5 и boost.

Моя цель состоит в том, чтобы каким-то образом обновить мои игровые настройки при изменении параметра в меню параметров. Я не хочу заставлять пользователя перезагружать мою игру. Другие игры не требуют перезапуска при смене разрешения или переходе от полноэкранного к оконному, поэтому моя игра также не должна этого делать. Пожалуйста, смотрите упрощенный вид системы ниже.

Обратите внимание, что я не обязательно хочу напрямую вызывать мой объект Game с экрана настроек. Пунктирная линия просто иллюстрирует эффект, которого я пытаюсь достичь; каким-то образом вызывать обновление игры, когда опция меняется в другой части системы.

Детальное объяснение

ScreenManager содержит список всех GameScreenсуществующих в данный момент объектов. Это будут различные экраны в игре, включая всплывающие окна. Этот дизайн более или менее соответствует образцу Game State Management в C # / XNA .

ScreenManagerСодержит ссылку на мой Gameобъект. В Gameобъект инициализируется и изменяет настройки игры. Если я хочу изменить разрешение, перейти в полноэкранный режим или отключить звук, который я сделал бы в Gameклассе.

Тем не менее, в настоящее время OptionsScreen не может получить доступ к классу Game. Смотрите ниже схему:

GameScreen может сигнализировать о трех событиях onFinished, onTransitionStartи onTransitionEnd. Нет, onOptionsChangedпотому что только один экран делает это. ScreenManager не может настроить обработку событий для этого, потому что он обрабатывает все экраны как GameScreens.

У меня вопрос, как я могу изменить свой дизайн, чтобы изменение в OptionsMenu не требовало перезапуска, а было изменено немедленно? Желательно, чтобы мой Gameобъект обновлялся после нажатия кнопки «Применить».

IAE
источник
Этот вопрос (несмотря на то, что он связан с игрой) выглядит идеально подходящим для programmers.stackexchange.com, потому что он больше касается общего дизайна ОО, чем реальной игры
bughi
Где вы взяли такие потрясающие графики?
joltmode
@psycketom: первый из Visio 2013, а второй генерируется из кода VS2012. Надеюсь, это поможет!
IAE

Ответы:

1

Из того, что я видел, самый простой подход - это прочитать файл опций при запуске, чтобы определить текущие настройки дисплея; затем при отображении экрана параметров загрузите все текущие параметры из файла.

Когда изменения завершаются с помощью кнопки applyили ok, они сохраняются обратно в файл. Если какие-либо изменения влияют на отображение, уведомите пользователя, что игра должна быть перезапущена, чтобы он вступил в силу.

Когда игра перезапускается, настройки дисплея (теперь новые) снова считываются из файла.

--РЕДАКТИРОВАТЬ--

И ... это помогло бы, если бы я заметил это последнее предложение. Вы не хотите перезапускать. Делает все немного сложнее в зависимости от вашей реализации и вашей внутренней библиотеки графики.

IIRC, Allegro имеет функцию вызова, которая позволяет изменять настройки дисплея на лету. Я пока не изучаю Allegro 5, но я знаю, что вы могли бы в 4.

Casey
источник
Я не думаю, что проблема связана с Allegro и его возможностями. «У меня вопрос, как я могу изменить свой дизайн, чтобы изменение в OptionsMenu не требовало перезапуска, а было изменено немедленно?» Перезапуск происходит потому, что «OptionsScreen в настоящее время не может получить доступ к классу Game» и должен загрузить их из файла настроек, если я правильно понимаю.
ClassicThunder
@Casey: Привет, Кейси! :) Да, я использую файл конфигурации для хранения соответствующих данных дисплея, но я хочу избежать перезагрузки. Это небольшая игра, и другие игры могут изменять разрешение без перезагрузки, поэтому я не понимаю, почему я должен заставлять пользователя перезагружаться вместе с моей. Это проблема юзабилити. Хотя я мог просто вызывать функции allegro dispay, я стараюсь не использовать ООП и не смешивать графические вызовы только потому, что могу; Я не считаю это хорошим дизайном.
IAE
Я выделил бит «без перезапуска», чтобы другие не запутались в одном и том же последнем предложении. Такой важный факт не должен был прийти в конце, я прошу прощения):
IAE
@SoulBeaver Кто сказал, что ты должен сделать это, чтобы не перезапускать? Это жизнеспособная вещь, требующая этого, фактически она становится все более распространенной в наши дни. Некоторые недавние выпуски (XCOM: Enemy Unknown, Skyrim и т. Д.) Высокобюджетных игр требуют перезапуска. (тот факт, что они находятся в Steam, может быть виновником из-за синхронизации параметров на стороне сервера, но давайте не будем там ...)
Кейси,
1
@Casey: Правда, и для определенных опций я вижу, почему это даже необходимо, но для таких вещей, как полноэкранный / оконный или разрешение экрана? Я никогда не видел игру, которая требовала перезапуска для этих настроек. Мой основной вопрос: зачем мне заставлять пользователя перезагружаться, когда игра может автоматически изменять настройки?
IAE
1

Это то, что я делаю для своей игры. У меня есть 2 отдельные функции для инициализации вещи, 'init' и 'reset'. Init вызывается только один раз при запуске и выполняет действия, которые не зависят от каких-либо настроек, такие как загрузка основных ресурсов. Reset выполняет раскладку пользовательского интерфейса в зависимости от разрешения экрана, поэтому вызывается каждый раз при изменении настроек.

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, но мой ответ довольно общий, поэтому я надеюсь, что он поможет вам или кому-либо еще с подобной проблемой.

DaleyPaley
источник
Это интересное решение. Я пытался жестко контролировать каждый раз, когда вызывается сброс, но вы делаете это независимо от изменений. Это определенно то, что я мог бы реализовать, и я посмотрю на это, спасибо!
IAE
1

Не портя вашу нынешнюю архитектуру, я вижу два пути. Во-первых, вы можете сохранить указатель на Gameэкземпляр в OptionsScreenклассе. Во-вторых, вы могли бы Gameкласс извлекать текущие настройки в заданном интервале, скажем каждую секунду.

Чтобы на самом деле адаптироваться к новым настройкам, Gameкласс должен реализовать какие-то функции сброса, которые выбирают текущие настройки и реинициализируют на их основе.

Для чистого решения вам нужен какой-то глобальный менеджер, поэтому для его реализации требуется больше усилий. Например, система событий или система обмена сообщениями. Очень полезно, чтобы классы общались без таких сильных привязок, как агрегация или композиция и т. Д.

С глобальным менеджером событий, он OptionsScreenможет просто глобально запустить событие перерисовки, которое Gameзарегистрировано для прослушивания ранее.

Обычно вы можете реализовать класс менеджера, хранящий события и обратные вызовы, слушая их в хэш-карте. Затем вы можете создать один экземпляр этого менеджера и передать ему указатели на ваши компоненты. Используя более новый C ++, это довольно просто, так как вы можете использовать std::unordered_mapкак хэш-карту и std::functionхранить обратные вызовы. Есть разные подходы, которые вы можете использовать в качестве ключа. Например, вы можете создать строку менеджера событий, которая сделает компоненты еще более независимыми. В этом случае вы будете использовать в std::stringкачестве ключа в хэш-карте. Мне лично это нравится, и это определенно не проблема производительности, но большинство традиционных систем событий работают с событиями как с классами.

Данияр
источник
Привет Данияр. Я уже использую boost :: сигналы, чтобы установить элементарную схему обработки событий. Сильная линия от GameScreen к ScreenManager - это на самом деле обработка событий. Есть ли у вас какие-либо ресурсы, подробно описывающие архитектуру глобального менеджера событий? Больше работы или нет, похоже, что это может привести к чистой реализации.
IAE
Хотя я не читал никаких исследований по этому поводу, я реализовал глобальный менеджер событий для своего собственного маленького движка. Если вы заинтересованы в реализации, я вышлю вам ссылку. Я обновил свой ответ, чтобы покрыть более подробно.
Данияр
1

Ну, это какой-то особый случай паттерна Observer.

Существует решение, которое включает в себя обратные вызовы. Это лучший способ сделать это, если вы хотите слабую связь, и я думаю, что это также самый чистый. Это не будет включать каких-либо глобальных менеджеров или синглетонов.

По сути, вам нужно иметь какой-то SettingsStore. Там вы храните настройки. При создании нового экрана им понадобится указатель на магазин. В случае этого OptionsScreenон сам изменит некоторые значения настроек. В случае с GameScreenним просто прочтут их. Итак, в вашей игре вы бы создали только один экземпляр, который будет передаваться по всем экранам, для которых он нужен.

Теперь этот SettingsStoreкласс будет иметь список notifiables. Это классы, которые реализуют определенный ISettingChangedинтерфейс. Интерфейс будет простым, который содержит следующий метод:

void settingChanged(std::string setting);

Затем на своем экране вы реализуете логику для каждого параметра, который вас интересует. Затем, добавьте себя в магазин , чтобы получать уведомления: store->notifyOnChange(this);. Когда настройка изменяется, вызывается обратный вызов с именем настройки. Новое значение настройки затем может быть получено из SettingsStore.

Теперь это может быть дополнено следующими идеями:

  • Используйте класс, который хранит строки настроек - может быть даже SettingsStore(const strings), чтобы предотвратить копирование строк вокруг.
  • Еще лучше, вместо строки, используйте перечисление, чтобы указать, какие настройки у вас есть
  • Вы могли бы даже указать старые и новые значения в качестве аргументов в обратном вызове, но это было бы более запутанным, поскольку у вас могут быть строковые или целые значения, или еще много чего. Тебе решать.
Timotei
источник
0

Читайте ваши настройки из файла в переменные. Попросите вашего менеджера экрана отслеживать, был ли экран, с которого он только что появился, экраном настроек, и если это так, перезагрузите настройки из переменных. Когда пользователь выходит из вашей игры, запишите настройки переменных в файл.

131nary
источник