Лучший способ хранить переменные всей игры

23

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

В настоящее время я реализовал Constantsкласс, который содержит все GameOptionперечисления, но как выбрать значение по умолчанию для всех этих параметров? Кроме того, как я могу получить текущий выбранный enum?

Что касается разрешения, в частности, я решил сохранить значения, но я не уверен, как получить значения по умолчанию или сохраненные в настоящий момент значения. Любое направление было бы замечательно; Благодарность! :)

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: Я спросил в SO, и они указали мне на это место. Там есть комментарий, но я хотел бы услышать разные способы сделать это / наиболее часто используемые способы.

Р-Nold
источник
1
Вы спросили в правильном месте; кто бы ни послал тебя сюда, ошибся. В любом случае я ответил на вопрос, чтобы помочь вам, но это не вопрос разработки игр, это общий вопрос программирования.
Джоккинг
Я только что прочитал ТАКУЮ ветку; Мне нравится ответ Скотта Чемберлина.
Джоккинг
@jhocking Я указал на это таким образом, если есть какие-то особенности, относящиеся к разработке игр, которые могут отличаться от обычного приложения. Я также подумал, что у вас, ребята, уже могут быть канонические вопросы и ответы по этой теме, так как это очень часто встречается.
Крис Хейс
Касательно реального вопроса о глобалах, пожалуйста, не думайте, что существует какой-то фиксированный набор решений.
Ларс Виклунд

Ответы:

32

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

CVars:
Как только ваш проект вырастет, вы можете захотеть взглянуть на CVAR . CVAR - это, так сказать, «умная переменная», которую вы можете изменить во время выполнения через консоль, терминал или пользовательский интерфейс. CVAR обычно реализуются в терминах объекта, который оборачивает базовое значение. Затем объект может отслеживать значение, а также сохранять / загружать его в / из файла. Вы можете сохранить объекты CVAR на карте, чтобы получить к ним доступ с помощью имени или другого уникального идентификатора.

Чтобы проиллюстрировать эту концепцию немного ниже, следующий псевдокод представляет собой простой пример типа CVAR, который переносит intзначение:

// 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;

Глобальный доступ:
в приведенном выше примере я предположил, что конструктор CVarвсегда регистрирует переменную в глобальной cvarsкарте; это очень полезно, так как позволяет вам объявить переменную следующим образом:

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

Эта переменная автоматически становится доступной в глобальной карте, и вы можете получить к ней доступ из любого места, проиндексировав карту с именем переменной:

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

Постоянство:
когда игра закрывается, выполните итерацию карты и сохраните все переменные, отмеченные как CVAR_PERSISTENT, в файл. При следующем запуске игры перезагрузите их.

Прецедентное право:
для более конкретного примера надежной системы CVAR ознакомьтесь с реализацией, описанной в Doom 3 .

glampert
источник
4

Ну первый от перечисления определяет , какие значения могут быть , а не то , что значения являются . Таким образом, вам все еще нужно объявить другую переменную после того, как вы объявили enum. Например:

public enum Sound
{
    ON,
    QUIET,
    OFF
}

public Sound soundValue;

В этом примере теперь вы можете soundValueвыбрать ON, QUIET или OFF.


Тогда вам все еще нужно структурировать свой код так, чтобы другие части кода могли получить доступ к этому объекту «настроек». Я не знаю, нужна ли вам помощь с этой частью, но общие шаблоны для решения этой проблемы включают одиночные (которые в наши дни хмурились) или сервисные локаторы или внедрение зависимостей.

jhocking
источник
1

Решение Glampert очень полно, но я добавлю свой личный опыт.

Я столкнулся с этой же проблемой, и я решил использовать статический класс Variables.

Класс Variables внутренне хранит карту от строки к строке (пока все мои переменные являются только строками) и доступен через методы получения и установки.

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

Чтобы избежать этого, я применил следующую семантику: использование setметода выдает исключение, если переменная с таким именем уже существует в словаре, и getудаляет переменную из словаря перед ее возвратом.

Два дополнительных метода обеспечивают то, что вы ожидаете, setAndOverwriteи getAndKeep. Смысл семантики других методов в том, что вы можете легко обнаружить ошибки типа «этот метод должен инициализировать эту переменную, но другой метод делал это раньше».

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

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

angarg12
источник