Управління станом гри (Гра, Меню, Заголовок екрана тощо)


11

В основному, в кожній грі, яку я зробив до цих пір, у мене завжди є така змінна, як "current_state", яка може бути "грою", "titlecreen", "gameoverscreen" тощо.

А потім у своїй функції оновлення у мене величезна кількість:

if current_state == "game" 
  game stuf
  ...
else if current_state == "titlescreen"
  ...

Однак я не відчуваю, що це професійний / чистий спосіб поводження зі станами. Будь-які ідеї, як зробити це кращим чином? Або це стандартний спосіб?


Яку мову, рамки тощо ви використовуєте?
Петро Абдулін

Зазвичай Lua + ЛЮБОВ. Я також нещодавно виявив, що різні структури мають різні способи вирішення цього питання. Здається, SFML має дуже гарний клас екрану.
Девід Гомес

1
Ви заглянули в державні машини?
Даркара

1
Ви також можете шукати іграти на панелі пошуку вгорі праворуч. Слід дати певні результати.
TravisG

Обов'язково другий Даркара - це, здається, саме те, для чого використовуються Державні машини.
balajeerc

Відповіді:


14

Оскільки ви говорите про екрани, я вважаю, що найкраще розділити всю цю логіку на різні екрани. Що я зазвичай роблю:

Визначте інтерфейс, який називається екран, і реалізуйте його на декількох екранах. Як і LoadingScreen, MainMenuScreen, GameScreen, GameOverScreen, HighScoreScreen тощо. У грі ви ставите змінну, яка містить поточний екран. Кожен цикл ви викликаєте screen.update () та надаєте поточний екран. Це дозволить вам значно заощадити "якщо цей стан зробить це", оскільки ваш стан визначено поточним екраном.

Це дуже добре розділить вашу логіку.

Приклад коду:

### Screen interface ###
public interface Screen {

    public void show();

    public void update(float delta);

    public void render(float delta);

    public void hide ();
}

### An implementation of screen ###
public class MainMenuScreen implements Screen {

    private Game game;

    public MainMenuScreen(Game game) {
        this.game = game;
    }

    public void show() {
        // init stuff
    }

    public void update(float delta) {
        // react to clicks, update animations etc.
        if (buttonwasclicked) {
            game.setScreen(new GameScreen(game)); // change the screen
        }
    }

    public void render(float delta) {
        // draw everything
    }

    public void hide() {
        // release all resources, as the screen is being hidden
    }
}

### Game, drawing the appropriate screen ###
public class Game {

    public Screen screen;

    public void update() {
        screen.update(getDeltaTime);
        screen.render();
    }

    public void setScreen(Screen screen) {
        this.screen.hide();

        this.screen = screen;
        this.screen.show();
    }
}

Або залежно від налаштування гри, можливо, у вашій грі є нескінченний цикл.

while(true) {
    calculatetimesincelastframe()
    screen.update(time);
    screen.render(time);
}

5

Якщо ви вже використовуєте Middleclass, є чудова бібліотека, яка складається з машин, яка разом з ним називається Statefull . Він простий у використанні та висуває ті самі ідеї, що і Матсеман.


2

Якщо ваша current_stateзмінна - це рядок, то в Луї це справді просто:

game_states = {}
function game_states.game()
    -- game stuff
end
function game_states.titlescreen()
    -- title screen stuff
end

-- then, inside the Update function:
game_states[current_state]()

1

Що я роблю приблизно так:

У мене є спрямована ациклічна структура даних графіків , яка по суті є лише купою вузлів, які вказують один на одного. Кожен вузол представляє ігрову систему. наприклад, інтерфейс користувача, світ, введення, візуалізація. І кожен вузол вказує на інші вузли, що надходять до нього або після нього. Після того, як всі вузли знаходяться на місці, їх легко вирівняти в простий список. Налаштування цього DAG - це перше, що я роблю під час запуску гри. Кожен раз, коли я хочу додати нову систему, скажімо, AI, я можу просто сказати написати цей код, а потім розповісти моїй грі від чого вона залежить і від чого вона повинна залежати.

Мій основний цикл гри йде після цього і просто запускає кожну систему в порядку. Спочатку обробляється вхід, потім оновлення світу, потім інші речі ... користувацький інтерфейс підходить до кінця, а візуалізація - остання. Коли гра починається вперше, немає ні світу, ні фізики, ні штучного інтелекту, тому ці кроки по суті пропускаються, і відображається лише заголовок екрана. Коли ви запускаєте гру належним чином, інтерфейс користувача надсилає повідомлення світовій системі, щоб увімкнути, і він просто подбає про себе. Управління ігровим станом означає лише включення та вимкнення різних систем. Кожна система має власний набір інформації про стан, яка обробляється більш-менш незалежно від усіх інших (Це не зовсімДійсно, багато систем діють на одному і тому ж наборі даних - наприклад, система користувальницького інтерфейсу захоплює дані зі світу для відображення інформації, наприклад. Системі AI також потрібно переглядати та надсилати повідомлення суб’єктам у світі).


Ця відповідь є гарною відповіддю на інше запитання.
Matsemann

Як так? Він запитав, як налаштувати різні його ігрові стани, і моє рішення не використовувати державну машину, як зараз, а замість того, щоб розділити біти на різні системи, які не є державною машиною, а швидше DAG.
Алекс Еймс

1

Ось як я впорядковую свої штати в Lua + Love2d. Це дозволяє уникнути довгих тверджень if / then.

По-перше, я створюю базовий клас, який містить методи оновлення (dt) та render (). Ви також можете надати йому методи обробки подій, як-от onKeyDown (ключ). Я називаю цей клас Stage, але будь-який об’єкт, який реалізує методи, буде працювати. Потім я роблю примірник цього класу для кожного стану гри, реалізуючи необхідні методи. Потім я створюю ключ / значення таблиці з назвою стану та екземпляром стану. Потім слідкуйте за поточною державою в глобальному масштабі, щоб держави могли змінити її, коли буде виконано певну умову.

states = {}
states["title"] = title   -- Where title implements Stage class.
states["game"] = game     -- You could create the instance of 'game' lazily too.
currentState = "title"

function love.update(dt)
    if states[currentState] ~= nil then
       states[currentState]:update(dt) 
    end
end

-1

Ну, хоча це не дуже добре, так поводитися з державами, ІМО. Ви можете зробити його набагато більш чистим, використовуючи функції для кожного стану, наприклад:

if current_state == "game" 
  game()
else if current_state == "titlescreen"
  titlescreen()

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

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