Игровая система состояния C++

Хорошо: я довольно плохо знаком с C++ и статическими языками на целом. При прибытии с лет рубина (и другие динамические языки) я не знаю, возможно ли это.

Я делал игровую систему состояния для... хорошо игры. Я хочу сделать систему легкой для меня вырезать и вставить в другие игры ни с кем (или очень немногие) изменения.

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

Могло быть любое количество состояний, но всегда будет по крайней мере 2 - 3 состояния, активные в памяти.

Уродство № 1.

В настоящее время у меня есть класс менеджера состояния с чем-то вроде этого в нем:

void StateManager::changeState(StateID nextStateID)
{
    // UNFOCUS THE CURRENT STATE //
    if (currentState())
    {
        currentState()->onUnFocus();

        // DESTROY THE STATE IF IT WANTS IT //
        if(currentState()->isDestroyedOnUnFocus()) {
            destroyCurrentState();
        }
    }

    if (m_GameStates[nextStateID]) {
        // SWITCH TO NEXT STATE //
        setCurrentState(nextStateID);
    }
    else
    {
        // CREATE NEW STATE //
        switch (nextStateID)
        {
        case MainMenuStateID:
            m_GameStates[MainMenuStateID] = new MainMenuState;
            break;
        case GameStateID:
                        m_GameStates[MainMenuStateID] = new GameStates;
            break;
        };
        setCurrentState(nextStateID);
    }

    // FOCUS NEXT STATE //
    currentState()->onFocus();
}

Этот подход работает, но я не чувствую, что это очень хорошо.

Действительно ли возможно передать тип? И затем назовите новыми на нем?

new NextGameState;  // Whatever type that may be.

poloymophism может помочь здесь? Все состояния получены из a class State.

Уродство № 2.

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

State* m_GameStates[MaxNumberOfStates];

Все состояния инициализируются к ПУСТОМУ УКАЗАТЕЛЮ, таким образом, я могу протестировать, если состояние там, и если не это создает тот при необходимости.

Это работает хорошо, как я могу назвать текущее состояние:

m_GameStates[m_CurrentState];

Однако мне не нравится это по двум причинам. Кажется чем-то вроде отходов, имеющих массив, полный Нулевых указателей, когда только будет 2 или трехочковые, активные в любой момент. [Примечание редактора: какова вторая причина?]

Я думал о смещении этого в a vector_ptr, но не сделал, поскольку это создаст дополнительные сложности с проверкой, чтобы видеть, существует ли состояние. И вектор, кажется, укрепляет Уродство № 1. поскольку у меня должен быть список для проверки каждого состояния.

Любой совет или направление ценятся.

Спасибо, Phil.

6
задан jalf 13 July 2010 в 17:28
поделиться

6 ответов

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

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

Вы можете создать шаблон функции, которому можно передать тип, например, следующим образом:

template <typename T>
void Foo() {
  T* x = new T();
  ...
}

Foo<int>() // call Foo with the type T set to 'int'

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

Другой вариант, который может работать лучше, поскольку у вас есть связь между переменной ( MainState ) и типом ( MainMenu ), может быть использование классов признаков. Опять же, я не уверен, как именно это будет сделано в вашем случае, поскольку мы не видели всю функцию (в частности, какой тип MainState , и как / когда это created?)

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

Для решения второй проблемы вы можете использовать стандартную библиотеку map :

#include <map>

// I'm not sure what type m_CurrentState is, so use its type instead of KeyType below
std::map<KeyType, State*> m_GameStates;

// and to perform a lookup in the map:
GameStates[m_CurrentState];

И, наконец, очень важный совет:

Прекратите использовать указатели повсюду. Прекратите вызывать новый для создания новых объектов. Как правило, объекты должны создаваться в стеке (вместо Foo * f = new Foo; просто выполните Foo f;

И вместо использования указателей вы часто хотите просто скопировать сам объект или создать ссылки вместо указателей.

И когда вам действительно нужно использовать динамическое распределение памяти, вы по-прежнему не должны использовать new напрямую. Вместо этого создайте объект-оболочку, который внутренне выделяет то, что ему нужно, с помощью new в своем конструкторе и снова освобождает его в деструкторе.

Если вы сделаете это правильно, это в значительной степени решит все проблемы управления памятью.

Общая методика называется RAII .

0
ответ дан 17 December 2019 в 00:02
поделиться

Как только вы говорите "Состояния", я думаю о схеме состояний .

По сути, вы можете получить множество объектов из базового класса State. Все действия, связанные с состоянием, происходят против текущего состояния, поддерживаемого менеджером состояний. Состояния будут переходить из состояния в состояние через менеджера.

Например, у вас может быть состояние Paused и Unpaused, каждое из которых имеет событие buttonPressed. Когда вы нажимаете кнопку, текущее состояние доставляется событию. Если он находится в режиме «Приостановлено», а кнопка была кнопкой паузы, перейдите к «Без паузы». И наоборот для Unpaused.

2
ответ дан 17 December 2019 в 00:02
поделиться
void StateManager::changeState(StateID nextStateID)
{
     leaveState(actualState); 
     enterState(nextStateID);
}

Мне очень нравится этот - настолько простой, насколько это возможно. ; -)

Что я хочу вам сказать - я думаю, что создание / удаление вашей статистики в функции changeState слишком логично - это просто должно изменить состояние, верно?

Edit: Чтобы перейти к вашим двум вопросам - я не думаю, что использование этого массива действительно бесполезно - вы говорите о трех полях, а не о 300 или около того. Так что если вам нравится использовать массивы - дерзайте. Если вы этого не сделаете, я выберу карту, она упрощает задачу, если вы хотите проверить, создано ли состояние или нет, и вы не ограничены магическим числом «maxStates». Вы можете проверить, достаточно ли оперативной памяти, а затем создать состояния X, а не фиксированные 2-3.

2
ответ дан 17 December 2019 в 00:02
поделиться

Для генерации состояний вам нужна фабрика. Таким образом, идентификатор состояния остается универсальным. Для хранения состояний я бы использовал std :: map

1
ответ дан 17 December 2019 в 00:02
поделиться

Посмотрите на Boost Statechart Library

0
ответ дан 17 December 2019 в 00:02
поделиться

Используйте перечисление (eration) для определения всех возможных состояний (это что-то вроде списка с константами). Просто создайте для одного объекта одну переменную, которая хранит состояние, и меняйте ее всякий раз, когда вам нужно ее изменить.

2
ответ дан 17 December 2019 в 00:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: