Методи управління державними іграми?


24

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

Як конкретний приклад, скажімо, це гра класичної Battlechess. Після того, як я зроблю крок, щоб взяти ще одну частину гравця, відтворюється коротка послідовність бою. Під час цієї послідовності гравцю не можна дозволяти переміщати фігури. То як би ви відстежували такий перехід держави? Кінцева машина машини? Проста булева перевірка? Здається, останній би добре працював лише для гри з дуже невеликими змінами стану такого типу.

Я можу придумати безліч простих способів поводження з цим за допомогою машин з кінцевим станом, але я також бачу, як вони швидко виходять з ладу. Мені просто цікаво, чи існує більш елегантний спосіб відстежувати стан / перехід гри.


Ви перевірили gamedev.stackexchange.com/questions/1783/game-state-stack та gamedev.stackexchange.com/questions/2423/… ? Це ніби все, стрибаючи навколо однієї і тієї ж концепції, але я не можу придумати нічого, що було б краще, ніж державна машина для ігрового стану.
michael.bartnett

Можливий дублікат: gamedev.stackexchange.com/questions/12664/…
Tetrad

Відповіді:


18

Я одного разу натрапив на статтю, яка вирішує вашу проблему досить елегантно. Це основна реалізація FSM, яка викликається у вашому головному циклі. В решті цієї відповіді я окреслив основний перелік статті.

Ваш основний стан гри виглядає так:

class CGameState
{
    public:
        // Setup and destroy the state
        void Init();
        void Cleanup();

        // Used when temporarily transitioning to another state
        void Pause();
        void Resume();

        // The three important actions within a game loop
        void HandleEvents();
        void Update();
        void Draw();
};

Кожен ігровий стан представлений реалізацією цього інтерфейсу. Для вашого прикладу Battlechess це може означати такі стани:

  • інтро-анімація
  • головне меню
  • анімація налаштування шахової дошки
  • вхід для переміщення гравця
  • анімація переміщення гравця
  • супротивник рухається анімацією
  • меню пауз
  • екран гри

Штати керують у вашому штатному двигуні

class CGameEngine
{
    public:
        // Creating and destroying the state machine
        void Init();
        void Cleanup();

        // Transit between states
        void ChangeState(CGameState* state);
        void PushState(CGameState* state);
        void PopState();

        // The three important actions within a game loop
        // (these will be handled by the top state in the stack)
        void HandleEvents();
        void Update();
        void Draw();

        // ...
};

Зауважте, що кожен стан потребує вказівника на CGameEngine в якийсь момент, тому сам стан може вирішити, чи потрібно вводити новий стан. У статті пропонується передати CGameEngine як параметр для HandleEvents, Update and Draw.

Зрештою, ваш основний цикл має справу лише з двигуном стану:

int main ( int argc, char *argv[] )
{
    CGameEngine game;

    // initialize the engine
    game.Init( "Engine Test v1.0" );

    // load the intro
    game.ChangeState( CIntroState::Instance() );

    // main loop
    while ( game.Running() )
    {
        game.HandleEvents();
        game.Update();
        game.Draw();
    }

    // cleanup the engine
    game.Cleanup();
    return 0;
}

17
C для класу? Ew Однак це гарна стаття - +1.
Качка комуністична

З того, що я можу зібрати, це така річ, про яку явно не питається. Це не означає, що ви не могли впоратися з цим так, як ви, звичайно, могли, але якщо все, що ви хотіли зробити, це тимчасово відключити введення, я думаю, що це і надмір, і погано для обслуговування, щоб отримати новий підклас CGameState, який збирається бути 99% ідентичним іншому підкласу.
Кілотан

Я думаю, що це сильно залежить від того, як код з'єднався. Я можу уявити собі чітке розмежування між вибором фрагмента та пунктом призначення (головним чином, індикаторами інтерфейсу користувача та керуванням введенням) та анімацією шахового твору до цього пункту призначення (ціла анімація на дошці, де інші фігури рухаються зі свого шляху, взаємодіють із рухомим т. д.), що робить держави далеко не однаковими. Це розділяє відповідальність, що забезпечує просте обслуговування та навіть повторне використання (вступний демонстраційний режим, режим відтворення). Я думаю, що це також відповідає на питання, показуючи, що використовувати FSM не потрібно зайвих клопотів.
привид

Це справді чудово, дякую. Ключовий момент, який ви зробили, був у вашому останньому коментарі: "використання FSM не потребує клопоту". Я помилково уявляв, що використання FSM передбачає використання операторів перемикання, що не обов'язково відповідає дійсності. Ще одне ключове підтвердження - це те, що кожна держава потребує посилання на ігровий движок; Мені було цікаво, як це буде працювати інакше.
варгонська

2

Почну з обробки подібних речей найпростішим можливим способом.

bool isPieceMoving;

Тоді я додам чеки проти цього булевого прапора у відповідних місцях.

Якщо пізніше я виявляю, що мені потрібно більше особливих випадків, ніж це - і лише це - я переосмислююсь на щось краще. Зазвичай я використовую 3 підходи:

  • Перетворюйте будь-які ексклюзивні прапори, що представляють субстати, на перерахунки. напр. enum { PRE_MOVE, MOVE, POST_MOVE }і додайте переходи, де потрібно. Тоді я можу перевірити цей перелік, де я перевіряв булевий прапор. Це проста зміна, але така, яка зменшує кількість речей, з якими потрібно перевірити, дозволяє використовувати оператори перемикання для ефективного управління поведінкою тощо.
  • Вимкніть окремі підсистеми, як вам потрібно. Якщо єдиною різницею під час бойової послідовності є те, що ви не можете переміщувати шматки, ви можете зателефонувати pieceSelectionManager->disable()або подібне на початку послідовності, і pieceSelectionManager->enable(). Ви все ще маєте прапори, але тепер вони зберігаються ближче до об'єкта, яким вони керують, і вам не потрібно підтримувати зайвий стан у вашому ігровому коді.
  • Попередня частина передбачає існування PieceSelectionManager: загалом, ви можете розподілити частини свого ігрового стану та поведінки на більш дрібні об'єкти, які узгоджують підмножину загального стану. Кожен з цих об'єктів матиме свій власний стан, який визначає його поведінку, але керувати ним легко, оскільки він ізольований від інших об'єктів. Стримайте прагнення дозволити вашому ігровому об'єкту чи основній петлі стати демпінгом для псевдоглобалів та факторів, які вичерпують!

Взагалі кажучи, мені ніколи не потрібно йти далі, ніж це стосується підстанцій спеціального корпусу, тому я не думаю, що існує ризик того, що він "швидко вийде з рук".


1
Так, я гадаю, що існує межа між загальним розгортанням станів і просто використанням bool / enums, коли це доречно. Але знаючи мої педантичні тенденції, я, мабуть, збираюся зробити майже кожен штат своїм класом.
варгонська

Ви робите це звучанням, як клас правильніший за альтернативи, але пам’ятайте, що це суб’єктивно. Якщо ви почнете створювати занадто багато невеликих класів для речей, які легше можуть бути представлені іншими мовними конструкціями, то це може затьмарити наміри коду.
Kylotan

1

http://www.ai-junkie.com/architecture/state_driven/tut_state1.html - прекрасний підручник з управління державними іграми! Ви можете використовувати його або для ігрових об'єктів, або для системи меню, як вище.

Він починає викладати схему державного дизайну , а потім продовжує впроваджувати State Machineта послідовно розширює її все далі та далі. Це дуже добре читати! Дасть вам ґрунтовне розуміння того, як працює вся концепція та як застосувати її до нових типів проблем!


1

Я намагаюся не використовувати для цього державну машину та булеви, тому що обидва не масштабуються. Обидва перетворюються на безлад, коли кількість держав зростає.

Зазвичай я розглядаю геймплей як послідовність дій та наслідків, будь-який ігровий стан просто надходить природним шляхом, не потрібно окремо визначати його.

Наприклад, у вашому випадку з відключенням вводу програвача: у вас є деякий обробник вводу користувача та деяка візуальна індикація того, що введення вимкнено, вам слід зробити їх одним об’єктом або компонентом, тому для відключення введення ви просто відключите весь об'єкт, не потрібно синхронізувати їх у якійсь державній машині або реагувати на якийсь бульний показник.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.