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


16

Чи є стандартна методика управління введенням у великих іграх. Наразі в моєму проекті вся обробка вводу виконується в ігровому циклі, наприклад:

while(SDL_PollEvent(&event)){
            switch(event.type){
                case SDL_QUIT:
                    exit = 1;
                    break;
                case SDL_KEYDOWN:
                    switch(event.key.keysym.sym){
                        case SDLK_c:
                            //do stuff
                            break;
                    }
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    switch(event.button.button){
                        case SDL_BUTTON_MIDDLE:
                                //do stuff
                                break;
                            }
                    }
                    break;
            }

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


За допомогою диспетчера подій ви можете запустити подію на вхід і дозволити всім іншим частинам вашої гри зареєструватися в них.
danijar

@danijar, що саме ти маєш на увазі під керівником подій, чи можливо, якщо ти можеш надати якийсь псевдо-код скелета, щоб показати, про яку річ ти говориш?
w4etwetewtwet


1
Я написав відповідь, щоб детально ознайомитись із менеджерами подій, які є способом подати інформацію для обробки інформації.
danijar

Відповіді:


12

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

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

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

// in character controller
// at initialization time
Events->Register("Jump", [=]{
    // perform the movement
});

// in input controller
// inside the game loop
// note that I took the code structure from the question
case SDL_KEYDOWN:
    switch(event.key.keysym.sym) {
    case SDLK_c:
        Events->Fire("Jump");
        break;
    }
    break;

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

Events->Register<int>("LevelUp", [=](int NewLevel){ ... });

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

Використовуючи менеджер подій, ви можете легко транслювати вхідну інформацію у вашій програмі. Крім того, це дозволяє приємний спосіб дозволити користувачеві налаштовувати ключові прив’язки. Компоненти слухають смислові події замість клавіш безпосередньо ( "PlayerJump"замість "KeyPressedSpace"). Тоді ви можете мати компонент відображення вхідних даних, який слухає "KeyPressedSpace"і запускає будь-які дії, які користувач пов'язав із цією клавішею.


4
Чудова відповідь, дякую. Хоча я хотів би бачити код, я не хочу його копіювати, тому я не прошу вас розміщувати його до тих пір, поки я не реалізую свій власний, оскільки я дізнаюся більше цього.
w4etwetewtwet

Я просто щось придумав, чи можу я передати будь-яку функцію-члена, як ця, чи не буде функція реєстру приймати AClass :: func, обмежуючи її функціями одного члена класу
w4etwetewtwet

Це чудова річ щодо лямбда-виразів на C ++, ви можете вказати пункт захоплення, [=]а посилання на всі локальні змінні, до яких звертається лямбда, буде скопійовано. Тож вам не потрібно передавати цей покажчик чи щось подібне. Але зверніть увагу , що ви не можете зберігати лямбда з пунктом захоплення в старих функцій C покажчиків . Однак C ++ std::functionпрацює чудово.
danijar

std :: функція дуже повільна
TheStatehz

22

Розділіть це на кілька шарів.

На найнижчому шарі у вас є необроблені події введення з ОС. Введення SDL-клавіатури, введення миші, введення джойстика тощо. Можливо, у вас є кілька платформ (SDL - найменш поширений знаменник, якому не вистачає декількох форм введення, наприклад, про які пізніше ви можете піклуватися).

Можна абстрагувати їх за допомогою користувацького типу події дуже низького рівня, наприклад "кнопка клавіатури вниз" тощо. Коли ваш рівень платформи (цикл ігор SDL) отримує вхід, він повинен створити ці події низького рівня і потім переслати їх менеджеру введення. Зробити це можна за допомогою простих методів викликів, функцій зворотного дзвінка, складної системи подій, що завгодно.

Тепер система введення має завдання переводити вхід низького рівня в логічні події високого рівня. Логіку гри зовсім не хвилює те, що натиснули SPACE. Байдуже, що JUMP було натиснуто. Завдання менеджера вводу полягає в зборі цих подій низького рівня та генеруванні подій на високому рівні. Він несе відповідальність за те, що пробіл та кнопка «A» ігрової панелі відображаються в логічній команді «Перейти». Він стосується управління геймпадом проти миші тощо. Він випромінює логічні події високого рівня, які є максимально абстрактними від елементів управління низького рівня (тут є деякі обмеження, але ви можете повністю абстрагувати речі у звичайному випадку).

Потім ваш контролер символів отримує ці події та обробляє ці події високого рівня, щоб реально реагувати. Шар платформи надіслав подію "Клавіша пробілу вниз". Система вводу отримала це, переглядає свої таблиці / логіку відображення, а потім надсилає подію "Натиснутий стрибок". Контролер логіки гри / персонажів отримує цю подію, перевіряє, чи гравцю насправді дозволено стрибати, а потім випромінює подію "Гравець, що стрибнув" (або просто безпосередньо спричиняє стрибок), яку решта логіки гри використовує, щоб робити все, що завгодно .

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

Ось кілька аматорських мистецтв ASCII, щоб описати це:

-----------------------------------------------------------------------
Platform Abstraction | Collect and forward OS input events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
    Input Manager    | Translate OS input events into logical events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
Character Controller | React to logical events and affect game play
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
      Game Logic     | React to player actions and provides feedback
-----------------------------------------------------------------------

Класне мистецтво ASCII, але це не так потрібно, вибачте. Я пропоную використовувати замість них нумерований список. У всякому разі хороша відповідь!
danijar

1
@danijar: Е, я експериментував, ніколи раніше не намагався скласти відповідь. Більше роботи, ніж коштувало, але набагато менше роботи, ніж займатися фарбою. :)
Шон Міддлічч

Добре, зрозуміло :-)
danijar

8
Особисто я віддаю перевагу художньому способу ASCII більше, ніж нудному нумерованому списку.
Джессі Емонд

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