Правильний спосіб абстрагувати контролер XBox


12

У мене є контролер XBox360, який я хотів би використовувати як вхід для програми.

Що я не можу розробити - це найкращий спосіб викрити це через інтерфейс.

За лаштунками клас, який обробляє контролер (и), покладається на стан кнопки опитування.

Я спочатку спробував щось посилання:

Event ButtonPressed() as ButtonEnum

де ButtonEnumбув ButtonRed, ButtonStartі т.д ...

Це трохи обмежено тим, що він підтримує лише натискання кнопок, а не утримування / шаблони (натискання двічі тощо)

Наступна ідея полягала в тому, щоб просто розкрити стан програми, наприклад, наприклад

Property RedPressed as Boolean
Property StartPressed as Boolean
Property Thumb1XAxis as Double

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

Я розглядав можливість додавання декількох подій, наприклад:

Event ButtonPressed(Button as ButtonEnum)
Event ButtonPressedTwice(Button as ButtonEnum)
Event ButtonHeldStart(Button as ButtonEnum)
Event ButtonHeldEnd(Button as ButtonEnum)

але це здається трохи незграбним і справді боліло на екрані "Прив’язати кнопку".

Може хтось, будь ласка, вкаже мені на "правильний" спосіб обробляти входи від контролерів.

NB: Я використовую SlimDX всередині класу, який реалізує інтерфейс. Це дозволяє мені дуже легко читати стан. Вдячні також будь-які альтернативи, які вирішили б мою проблему

Відповіді:


21

Не існує жодного ідеального відображення, яке дає вам певну абстракцію для платформи, оскільки, очевидно, більшість ідентифікаторів, які мають сенс для контролера 360, неправильні для контролера PlayStation (A замість X, B замість Circle). І звичайно, контролер Wii - це зовсім інша річ.

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

Середній шар - це фактичне відображення керування від реальних кнопок до ігрових концепцій (наприклад, A -> Jump). Ми називаємо їх імпульсами замість кнопок, оскільки вони більше не прив'язані до певного типу контролера. Саме на цьому шарі ви можете перевстановити елементи керування (під час розробки або під час виконання за бажанням користувача). Кожна платформа має власне відображення елементів управління на віртуальні імпульси . Ви не можете і не повинні намагатися піти від цього. Кожен контролер унікальний і потребує власного відображення. Кнопки можуть відображати більше одного імпульсу (залежно від режиму гри), і більше однієї кнопки можуть відображати один і той же імпульс (наприклад, A і X прискорюються, B і Y обидва сповільнюються). Зображення визначає все це,

Верхній шар - ігровий шар. Він приймає імпульси, і не байдуже, як вони були створені. Можливо, вони прийшли від контролера, або запису контролера, або, можливо, вони прийшли з AI. На цьому рівні вам все одно. Вас хвилює те, що з'явився новий імпульс стрибка або імпульс прискорення продовжився або що занурений імпульс має значення 0,35 для цього галочки.

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


Це здається дуже чистим та елегантним підходом. Дякую!
Базова

1
Дуже хороша. Набагато краще, ніж у мене: P
Йордан Мілонас

3
Дуже приємна абстракція. Але будьте обережні при впровадженні: не створюйте та руйнуйте новий імпульсний об’єкт для кожної дії користувача. Використовуйте об'єднання. Збирач сміття вам вдячний.
grega g

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

@grega дякую обом - я не думав про це.
Основна

1

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

Нижній рівень може вважатися рівнем "на основі опитування" і показує поточний стан кнопки. Один такий інтерфейс може просто мати 2 функції, GetAnalogState(InputIdentifier)і GetDigitalState(InputIdentifier)деInputIdentifier - перелічене значення, яке відображає, проти якої кнопки, тригера чи палички ви перевіряєте. (Якщо запустити GetAnalogState для кнопки, повернеться 1,0 або 0,0, а запуск GetDigitalState для аналогової палички поверне справжнє значення, якщо буде встановлено перевищення встановленого порогу, або помилково).

Потім другий ярус використовує нижній рівень для генерування подій при зміні стану та дозволяє елементам реєструватися для зворотних викликів (події C # є славними). Ці зворотні дзвінки включатимуть події для преси, випуску, торкання, longhold тощо. Для аналогової палички ви можете включити жести, наприклад QCF для бойової гри. Кількість викритих подій залежатиме від того, наскільки детально про подію ви хочете відправити. Ви можете просто запустити простий, ButtonStateChanged(InputIdentifier)якби хочете обробляти все інше за окремою логікою.

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

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