HayBox — это модульная кроссплатформенная прошивка для цифровых или смешанных аналого-цифровых контроллеров, в первую очередь ориентированная на контроллеры типа B0XX.
Особенности включают в себя:
Если вы хотите просто использовать предварительно созданную прошивку с назначением контактов и конфигурацией по умолчанию, обратитесь к разделу предварительно созданных двоичных файлов. Если вы хотите внести какие-либо изменения в код, обратитесь к разделу «Сборка из исходного кода».
.uf2
), просто переведите ее в режим загрузки, подключив к компьютеру, и перетащите файл .uf2
на появившийся диск RPI-RP2..hex
), вы можете использовать программу, например QMK Toolbox, для прошивки в нее файла .hex
В настоящее время существует три основных способа сборки HayBox:
И действия GitHub, и кодовые пространства GitHub требуют создания учетной записи GitHub, но не требуют установки каких-либо зависимостей на локальном компьютере.
При локальной сборке необходимы следующие зависимости:
После установки всех требований загрузите и извлеките последнюю версию HayBox или клонируйте репозиторий, если у вас установлен git (что облегчит вам получение обновлений).
После этого:
git config --global core.longpaths true
в любом терминале (в VS Code или обычном cmd/PowerShell все в порядке).config/<environment>/config.cpp
). Любые кнопки, которых нет на вашем контроллере, можно просто удалить из списка.HayBox/.pio/build/<environment>/firmware.uf2
на RPI. -Появляется диск RP2.Вероятно, это самый удобный способ изменить и перестроить HayBox, но имейте в виду, что уровень бесплатного пользования GitHub накладывает некоторые ограничения на то, сколько вы можете использовать Codespaces каждый месяц. По этой причине вам необходимо обязательно закрывать свои кодовые пространства, когда вы их не используете, чтобы максимизировать то, что вы можете получить от своей квоты.
Сначала создайте учетную запись GitHub или просто войдите в систему, если она у вас уже есть, затем создайте форк этого репозитория и откройте форк в новом кодовом пространстве, нажав зеленую кнопку «Код» -> «Кодовые пространства» -> «Создать кодовое пространство на мастере». В вашем браузере должен открыться VS Code со всеми предустановленными необходимыми расширениями и зависимостями. Отсюда процесс во многом аналогичен локальной сборке, за исключением того, что вы не можете использовать кнопку «Загрузить» для прошивки прошивки. Вместо этого вам придется загрузить скомпилированный двоичный файл из HayBox/.pio/build/<environment>/
и прошить его вручную (подробнее об этом см. здесь).
Этот репозиторий содержит определение рабочего процесса GitHub Actions, которое создает каждую среду PlatformIO, указанную в матрице, при каждом нажатии и загружает двоичные файлы встроенного ПО в качестве артефактов, которые вы можете скачать, щелкнув конкретный запуск рабочего процесса в истории. Вы можете создать вилку этого репозитория и включить действия, нажав «Настройки» -> «Действия» -> «Основные» -> «Разрешить все действия и повторно используемые рабочие процессы» -> «Сохранить».
Самый быстрый способ внести изменения, если вы хотите выполнять сборку только с помощью GitHub Actions, — использовать github.dev. Вы можете сделать это, просто нажав .
на клавиатуре, пока у вас открыта вилка этого репозитория, и в вашем браузере откроется редактор VS Code. Это не дает вам тех же возможностей разработки, которые вы получаете в Codespace, но позволяет вносить изменения и фиксировать их прямо из браузера. Измените все, что хотите, а затем используйте вкладку «Управление версиями» слева, чтобы добавить, зафиксировать и отправить изменения. Наконец, вернитесь в репозиторий и щелкните вкладку «Действия», щелкните запуск рабочего процесса и подождите, пока он создаст артефакт.
Если вы добавляете новую среду конфигурации устройства/PlatformIO, вам придется добавить среду в матрицу, чтобы ее можно было построить с помощью рабочего процесса GitHub Actions. Вы также можете удалить из матрицы любые среды, которые вам не нужны, чтобы сократить использование ресурсов и потенциально ускорить сборку.
Чтобы перезагрузить контроллеры на базе Pico в режим загрузки, удерживайте кнопку «Пуск» на плагине.
Чтобы переключиться в режим платы Brook на GCCPCB2, GCCMX, B0XX R2 или LBX, удерживайте B на плагине.
Коммуникационные серверы выбираются немного по-разному в зависимости от типа микроконтроллера, используемого в контроллере.
На Pico/RP2040 USB vs GameCube vs Nintendo 64 определяется автоматически. Если он не подключен к консоли, по умолчанию используется XInput , который должен работать по принципу «подключи и работай» с большинством компьютерных игр. Другие бэкэнды выбираются, удерживая одну из следующих кнопок на плагине:
В Arduino/AVR серверная часть DInput выбирается, если обнаружено USB-соединение. В противном случае по умолчанию используется серверная часть GameCube, если другой сервер не выбран вручную, удерживая одну из следующих кнопок на плагине:
В отличие от других подобных прошивок, HayBox по умолчанию позволяет переключать режимы на лету, не отключая контроллер. Это в основном полезно на ПК, в отличие от консоли, где вам все равно приходится перезапускать консоль, чтобы переключить игру. Это также позволяет уменьшить количество кнопок, которые приходится удерживать одной рукой при подключении к сети.
Комбинации кнопок режима контроллера по умолчанию:
Комбинации кнопок режима клавиатуры по умолчанию (доступны только при использовании серверной части DInput, но не при использовании XInput):
HayBox нужен другой профиль контроллера Dolphin, чем официальная прошивка B0XX, поскольку он использует другие сопоставления DInput, которые имеют больше смысла для использования в нескольких играх. Их можно найти в папке dolphin
в репозитории HayBox. Файлы профилей названы так, чтобы указать, для какой коммуникационной системы и операционной системы они предназначены:
Чтобы установить профиль:
dolphin
в HayBox в папку <YourDolphinInstallation>UserConfigProfilesGCPad
(создайте его, если он не существует).%appdata%Slippi LaunchernetplayUserConfigProfilesGCPad
~/.config/SlippiOnline/Config/Profiles/GCPad/
Cmd + Shift + G
и введите путь /Users/<USER>/Library/Application Support/Slippi Launcher/netplay/Slippi Dolphin.app/Contents/Resources/Sys/Config/Profiles/GCPad
%userprofile%DocumentsDolphin EmulatorConfigProfilesGCPad
~/.config/dolphin-emu/Profiles/GCPad/
* macOS поддерживает только DInput (и не очень хорошо), поэтому, если вы используете контроллер на базе Pico/RP2040, вам придется принудительно включить режим DInput, удерживая Z на плагине, и даже тогда он может не работать. Я ничего не могу поделать с плохой поддержкой контроллеров Apple (которая, похоже, нарушается с каждым новым обновлением), и у меня нет никаких устройств Apple, поэтому это также будет считаться неподдерживаемым использованием HayBox.
Серверная часть связи (например, DInput, GameCube или N64) выбирается частично посредством автоматического определения, а частично на основе кнопок, удерживаемых на плагине. Это обрабатывается в config/<environment>/config.cpp
в функции setup()
. Логика довольно проста, и даже если у вас нет опыта программирования, вам не составит труда увидеть, что происходит, и при желании изменить ситуацию.
Папки конфигурации, соответствующие средам Arduino:
config/arduino_nativeusb/
для Arduino со встроенной поддержкой USB (например, Leonardo, Micro)config/arduino/
для Arduino без встроенной поддержки USB (например, Uno, Nano, Mega 2560) В конфигурациях устройств Arduino вы можете заметить, что в GamecubeBackend()
передается число 125. Это позволяет вам изменить частоту опроса, например, если ваш DIY не поддерживает собственный USB и вы хотите использовать его с разогнанным адаптером контроллера GameCube. В этом примере вы можете передать 1000 для синхронизации до частоты опроса 1000 Гц или 0, чтобы полностью отключить это исправление задержки. Частоту опроса можно передать в конструктор N64Backend таким же образом.
Вы можете заметить, что частота опроса 1000 Гц работает и на консоли. Имейте в виду, что хотя это и работает, это приведет к большей задержке ввода. Смысл установки частоты опроса здесь заключается в том, чтобы серверная часть GameCube могла задерживать до следующего опроса перед чтением входных данных, чтобы входные данные были свежими и не устаревшими.
Для Pico/RP2040 нет необходимости передавать частоту опроса консоли, поскольку у Pico достаточно вычислительной мощности для чтения/обработки входных данных после получения опроса консоли, поэтому нет необходимости прогнозировать, когда опрос поступит, и готовиться. заранее.
Чтобы настроить удержание кнопки для режимов ввода (режимы контроллера/клавиатуры), отредактируйте функцию select_mode()
в config/mode_selection.hpp
. Каждый оператор if
представляет собой комбинацию кнопок для выбора режима ввода.
Большинство режимов ввода поддерживают переход в режим очистки SOCD, например socd::2IP_NO_REAC
. См. здесь другие доступные режимы.
Для создания новых режимов ввода полезно знать немного C++ или хотя бы иметь некоторый опыт программирования. Тем не менее, вы сможете обойтись даже без предварительного опыта, если просто создадите свой новый режим на основе существующих и будете ссылаться на них в качестве примеров.
Существует два типа режимов ввода: ControllerMode и KeyboardMode.
Режимы клавиатуры немного проще, поэтому начнем с них.
KeyboardMode ведет себя как стандартная клавиатура и должен работать с любым устройством, поддерживающим клавиатуры.
Вы можете использовать любые логические и программные приемы, которые вам нравятся, в функции UpdateKeys()
чтобы определять выходные данные на основе состояния ввода. Вы можете создавать слои ввода (например, слой D-Pad в режиме ближнего боя, который активируется при удерживании Mod X и Mod Y) или другие типы условных входов.
Список доступных кодов клавиш можно найти здесь.
Помните, что режимы клавиатуры можно активировать только при использовании коммуникационного интерфейса DInput ( а не XInput).
ControllerMode принимает состояние ввода цифровой кнопки и преобразует его в состояние вывода, соответствующее стандартному геймпаду. Любой ControllerMode будет работать с любым CommunicationBackend. CommunicationBackend просто считывает входные данные из одного или нескольких источников входных данных, использует текущий ControllerMode для обновления выходных данных на основе этих входных данных и обрабатывает отправку выходных данных на консоль или ПК.
Чтобы создать ControllerMode, вам просто нужно реализовать функции UpdateDigitalOutputs()
и UpdateAnalogOutputs()
.
UpdateDigitalOutputs()
очень похожа на функцию UpdateKeys()
в режимах клавиатуры, с той разницей, что вместо вызова функции Press()
для немедленной отправки входных данных мы просто устанавливаем состояние вывода для этой итерации. Как следует из названия, в этой функции мы будем иметь дело только с цифровыми выходами.
UpdateAnalogOutputs()
немного сложнее. Во-первых, он должен вызвать UpdateDirections()
прежде чем делать что-либо еще. Эта функция принимает значения, указывающие, направлены ли ваши левый и правый джойстики влево/вправо/вверх/вниз. Вы также передаете минимальное, нейтральное (центральное) и максимальное аналоговые значения джойстика, поэтому вы можете настроить их для каждого режима отдельно. Вся эта информация используется для автоматической установки аналоговых значений джойстика на основе введенных вами входных данных. Это все, что вам нужно сделать, если вы не хотите реализовать модификаторы.
UpdateDirections()
также заполняет переменные directions
значениями, указывающими текущее направление джойстика, которое может быть 1, 0 или -1 для осей X и Y для обоих джойстиков. Эти значения значительно упрощают написание логики модификаторов.
После вызова UpdateDirections()
добавьте любую логику обработки модификаторов, которую вы хотите. Помните, что UpdateDirections()
уже установил значения аналоговых джойстиков по умолчанию, поэтому при работе с модификаторами вам нужно только вручную установить значения для осей, которые фактически изменяются. Кроме этого, я не могу научить писать логику модификаторов, поэтому просто смотрите примеры и экспериментируйте.
Наконец, установите необходимые вам значения аналогового триггера.
Примечание. Выходы аналогового триггера с таким же успехом можно обрабатывать в UpdateDigitalOutputs()
, но я думаю, что обычно выглядит проще, если хранить их вместе с другими аналоговыми выходами.
Также обратите внимание: вам не нужно беспокоиться о сбросе состояния вывода или очистке чего-либо из него. Это делается автоматически в начале каждой итерации.
В конструкторе каждого режима (для режимов контроллера и режимов клавиатуры) вы можете настроить пары входов противоположного направления для применения очистки SOCD.
Например, в src/modes/Melee20Button.cpp
:
_socd_pair_count = 4;
_socd_pairs = new socd::SocdPair[_socd_pair_count]{
socd::SocdPair{&InputState::left, &InputState::right, socd_type},
socd::SocdPair{ &InputState::down, &InputState::up, socd_type},
socd::SocdPair{ &InputState::c_left, &InputState::c_right, socd_type},
socd::SocdPair{ &InputState::c_down, &InputState::c_up, socd_type},
};
Это настраивает лево/право, вниз/вверх, C-Left/C-Right и C-Down/C-Up как пары противоположных кардинальных направлений, для которых будет применяться очистка SOCD. Очистка SOCD автоматически выполняется перед UpdateDigitalOutputs()
и UpdateAnalogOutputs()
, и вам не нужно беспокоиться об этом больше.
Для каждой SocdPair
вы можете передать SocdType
по вашему выбору. По умолчанию для большинства режимов это передается как один параметр конструктора, но можно передать несколько параметров или просто использовать жестко запрограммированное значение. Оба этих подхода проиллюстрированы в src/modes/FgcMode.cpp
.
SocdType | Описание |
---|---|
SOCD_NEUTRAL | Левый + правый = нейтральный — значение по умолчанию, если в SocdPair не указан SocdType . |
SOCD_2IP | Второй приоритет ввода — лево -> лево + право = право, и наоборот. Отпуск второго направления дает исходное направление. |
SOCD_2IP_NO_REAC | Приоритет второго входа без повторной активации – то же, что и выше, за исключением того, что отключение второго направления приводит к нейтральному состоянию. Исходное направление должно быть физически восстановлено. |
SOCD_DIR1_PRIORITY | Первая кнопка в SocdPair всегда имеет приоритет над второй. |
SOCD_DIR2_PRIORITY | Вторая кнопка в SocdPair всегда имеет приоритет над первой. |
SOCD_NONE | Нет разрешения SOCD — решает игра |
Обратите внимание, что вам не нужно реализовывать функцию HandleSocd()
как в режимах Melee20Button и Melee18Button. В этих режимах оно переопределяется только для того, чтобы мы могли проверить, удерживаются ли оба левого и правого направления перед очисткой SOCD, потому что, когда они оба удерживаются (без удержания вертикального направления), нам необходимо переопределить все модификаторы.
Если на вашем контроллере нет кнопок светового экрана, вы можете использовать Mod X для светового экрана и вместо этого установить наклон экрана на R. Вы можете сделать это, используя режим Melee18Button вместо Melee20Button.
Режимы Melee20Button и Melee18Button предоставляют выбор, какие координаты использовать при нажатии вниз+вправо. По умолчанию, удерживая + Назад, вы сможете автоматически отменять удары, что является полезным приемом для некоторых персонажей.
Еще одна популярная техника, использующая диагональ вниз + вправо, — это так называемый выбор опции «приседать/ходить». Эта техника включает в себя удержание кнопки «+ вперед» под определенным углом во время приседания, так что после отмены атаки приседанием вы автоматически начнете идти к противнику, а не снова приседать. Это может быть очень полезно для погони за техникой, но координаты, используемые для этой техники, не позволяют вам автоматически отменять джеб.
Это можно настроить, как показано в config/mode_selection.hpp
, установив для параметра crouch_walk_os
значение true:
new Melee20Button(socd::SOCD_2IP_NO_REAC, { .crouch_walk_os = false })
Вам также придется изменить это в вашем config/<environment>/config.cpp
, чтобы его можно было применить к плагину, поскольку mode_selection.hpp
контролирует только то, что происходит при переключении режима.
В режиме ProjectM есть несколько дополнительных опций для настройки определенного поведения. Как видно из config/mode_selection.hpp
:
new ProjectM(
socd::SOCD_2IP_NO_REAC,
{ .true_z_press = false, .ledgedash_max_jump_traj = true }
)
Во-первых, ledgedash_max_jump_traj
позволяет вам включать или отключать поведение, заимствованное из режима ближнего боя, где удержание влево и вправо (без вертикальных направлений) даст кардинальное значение 1,0 независимо от удерживаемых модификаторов.
Если вы измените режим SOCD на 2IP (с повторной активацией), вам также следует изменить этот параметр на false, если вы хотите плавного игрового процесса.
Во-вторых, опция true_z_press
существует, потому что Project M/Project+ не обрабатывает нажатия Z так же, как Melee. В ближнем бою нажатие Z интерпретируется как световой щит + A, поэтому его можно использовать для отмены L, не блокируя при этом доступ к технологиям. В PM/P+ нажатие Z запускает технологию и, таким образом, вызывает нежелательную блокировку технологии, если она используется для отмены L. По умолчанию в HayBox режим ProjectM настроен на использование макроса Lightshield + A, чтобы сохранить ожидаемое поведение от ближнего боя. Однако этот макрос не позволяет вам использовать атаки с помощью троса/захвата или захватывать предметы. Чтобы обойти эту проблему, вы можете нажать Mod X + Z, чтобы отправить настоящий ввод Z.
Если вас это беспокоит, и вы просто хотите по умолчанию отправлять ввод истинного Z при нажатии Z, вы можете установить для параметра true_z_press
значение true.
HayBox поддерживает несколько источников ввода, которые можно прочитать для обновления состояния ввода:
GpioButtonInput
— наиболее часто используемый для чтения переключателей/кнопок, подключенных непосредственно к контактам GPIO. Входные сопоставления определяются массивом GpioButtonMapping
, что можно увидеть практически во всех существующих конфигурациях.SwitchMatrixInput
— аналогично описанному выше, но сканирует матрицу переключателей стиля клавиатуры вместо отдельных переключателей. Конфигурация для модели Crane C<=53 включена в config/c53/config.cpp
и служит примером того, как определить и использовать источник входных данных матрицы переключателей.NunchukInput
— считывает входные данные с Wii Nunchuk с помощью i2c. Это можно использовать для контроллеров смешанного ввода (например, левая рука использует нунчак для движения, а правая рука использует кнопки для других элементов управления).GamecubeControllerInput
— аналогично предыдущему, но считывается с контроллера GameCube. Может быть создан аналогично GamecubeBackend. В настоящее время реализовано только для Pico, и вы должны либо запустить его на экземпляре pio (pio0 или pio1), отличном от любых экземпляров GamecubeBackend, либо убедиться, что оба используют одно и то же смещение памяти инструкций PIO. Каждый источник входных данных имеет значение «скорости сканирования», которое примерно указывает, сколько времени требуется ему для чтения входных данных. Источники быстрого ввода всегда считываются в самый последний момент (по крайней мере, на Pico), что приводит к очень низкой задержке. И наоборот, медленные источники ввода обычно считываются задолго до того, как они потребуются, поскольку они слишком медленны, чтобы их можно было прочитать в ответ на опрос. По этой причине лучше постоянно читать эти входные данные на отдельном ядре. Это невозможно на микроконтроллерах AVR, поскольку все они одноядерные, но это возможно (и легко) на Pico/RP2040. В нижней части стандартного файла конфигурации Pico config config/pico/config.cpp
это иллюстрируется использованием core1 для чтения входных данных Nunchuk, в то время как core0 обрабатывает все остальное. Дополнительную информацию об использовании core1 см. в следующем разделе.
В функции setup()
каждой конфигурации мы создаем массив входных источников, а затем передаем его в коммуникационный бэкэнд. Серверная часть связи решает, когда и какие источники ввода читать, поскольку входные данные необходимо считывать в разные моменты времени для разных серверных частей. Мы также создаем массив коммуникационных бэкэндов, позволяющий использовать более одного бэкэнда одновременно. Например, в большинстве конфигураций бэкэнд просмотра входных данных B0XX используется в качестве вторичного бэкэнда всякий раз, когда используется бэкэнд DInput. На каждой итерации основной цикл сообщает каждому из бэкэндов отправить соответствующие отчеты. В будущем может появиться больше серверов для таких вещей, как запись информации на OLED-дисплей.
В каждой конфигурации есть функции setup()
loop()
, где сначала запускается setup()
, а затем loop()
запускается повторно, пока устройство не выключится.
В Pico/RP2040 функции setup()
loop()
выполняются на ядре0, и вы можете добавить функции setup1()
loop1()
для запуска задач на ядре1.
Например, чтобы прочитать входные данные контроллера GameCube на ядре1:
GamecubeControllerInput *gcc = nullptr;
void setup1() {
while (backends == nullptr) {
tight_loop_contents();
}
gcc = new GamecubeControllerInput(gcc_pin, 2500, pio1);
}
void loop1() {
if (backends != nullptr) {
gcc->UpdateInputs(backends[0]->GetInputs());
}
}
Цикл while
гарантирует, что мы дождемся, пока setup()
на core0 завершит настройку коммуникационных серверов. Затем мы создаем источник входного сигнала контроллера GameCube с частотой опроса 2500 Гц. Мы также запускаем его на pio1
чтобы избежать вмешательства в любые серверные части GameCube/N64, которые используют pio0
если не указано иное. В loop1()
мы предполагаем, что основной бэкэнд является первым элементом массива backends
(который в любом случае настраивается в том же файле, поэтому на самом деле мы не предполагаем ничего, чего не знаем) и напрямую сканируем контроллер GameCube. входы в состояние ввода серверной части.
В качестве немного более безумного гипотетического примера можно даже привести в действие все элементы управления аркадного шкафа для двух человек, используя один Pico, создав два источника входного сигнала матрицы переключателей, используя, скажем, 10 контактов каждый, и два серверных модуля GameCube, оба на разных ядрах. Возможности безграничны.
Если вы используете официальный адаптер с контроллером на базе Arduino, вам, вероятно, придется удерживать A на плагине, который отключает оптимизацию задержки опроса, передавая частоту опроса 0 конструктору GamecubeBackend.
Если вы используете контроллер на базе Arduino без схемы повышения напряжения, вам потребуется питание 5 В, поэтому для адаптера Mayflash вам понадобятся оба USB-кабеля, подключенных к консоли, а на консоли линия вибрации должна быть неповрежденной. Pico изначально работает с напряжением 3,3 В, так что это не проблема.
Я приветствую вклад, и если вы создадите режим ввода, которым хотите поделиться, не стесняйтесь делать запрос на включение. Установите плагин clang-format для VS Code и используйте его для форматирования любого кода, который вы хотите добавить.
Мы используем SemVer для управления версиями. Доступные версии см. в тегах этого репозитория.
См. также список участников, принявших участие в этом проекте.
Этот проект распространяется под лицензией GNU GPL версии 3 — подробности см. в файле ЛИЦЕНЗИИ.