HayBox 是用于数字或混合模拟/数字控制器的模块化跨平台固件,主要针对 B0XX 型控制器。
特点包括:
如果您只想使用具有默认引脚映射和配置的预构建固件,请参阅预构建二进制文件部分。如果您想对代码进行任何更改,请参阅从源代码构建部分。
.uf2
文件),只需将其插入 PC 时将其置于 bootsel 模式,然后将.uf2
文件拖放到出现的 RPI-RP2 驱动器上.hex
文件),您可以使用 QMK Toolbox 等程序将.hex
文件刷入其中目前HayBox的构建主要有以下三种方式:
GitHub Actions 和 GitHub Codespaces 都要求您创建 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 的数量设置了一些限制。因此,您需要确保在不使用 Codespace 时将其关闭,以便最大限度地从配额中获得收益。
首先,创建一个 GitHub 帐户,或者如果您已经有帐户,则直接登录,然后分叉此存储库,并通过单击绿色代码按钮 -> Codespaces -> Create codespace on master 在新的 Codespace 中打开您的分叉。这应该会在浏览器中打开 VS Code,并预安装所有必要的扩展和依赖项。从这里开始,该过程与本地构建非常相似,只是您无法使用“上传”按钮来刷新固件。相反,您必须从HayBox/.pio/build/<environment>/
下载已编译的二进制文件并手动刷新它(有关更多信息,请参阅此处)。
此存储库包含一个 GitHub Actions 工作流程定义,该定义会在每次推送时构建矩阵中指定的每个 PlatformIO 环境,并将固件二进制文件作为工件上传,您可以通过单击历史记录中的特定工作流程运行来下载这些工件。您可以创建此存储库的分支,并通过单击设置 -> 操作 -> 常规 -> 选择“允许所有操作和可重用工作流程” -> 保存来启用操作。
如果您只想通过 GitHub Actions 进行构建,最快的更改方法是使用 github.dev。您只需按 即可完成此操作.
当您打开此存储库的分支时,在键盘上输入,它将在浏览器中打开 VS Code 编辑器。这不会为您提供与 Codespace 中相同的开发功能,但它允许您直接从浏览器进行更改并提交它们。更改您想要的任何内容,然后使用左侧的“源代码管理”选项卡添加、提交和推送您的更改。最后,返回到存储库并单击“操作”选项卡,单击您的工作流程运行,然后等待它构建工件。
如果您要添加新的设备配置/PlatformIO 环境,则必须将该环境添加到矩阵中,以便通过 GitHub Actions 工作流程构建它。您还可以从矩阵中删除您不关心的任何环境,以减少资源使用并可能加快构建速度。
要将基于 Pico 的控制器重新启动到 bootsel 模式,请在插件上按住 Start。
要在 GCCPCB2、GCCMX、B0XX R2 或 LBX 上切换到 Brook 板模式,请在插件上按住 B。
根据控制器中使用的微控制器的类型,通信后端的选择略有不同。
在 Pico/RP2040 上,会自动检测 USB、GameCube 和 Nintendo 64。如果未插入控制台,则默认为XInput ,它应该可以与大多数 PC 游戏即插即用。通过按住插件上的以下按钮之一来选择其他后端:
在 Arduino/AVR 上,如果检测到 USB 连接,则选择DInput后端。否则,它默认为 GameCube 后端,除非通过按住插件上的以下按钮之一手动选择另一个后端:
与其他类似的固件不同,HayBox 默认允许您即时切换模式,而无需拔掉控制器。这主要在 PC 上有用,而不是在控制台上有用,在控制台上您通常必须重新启动控制台才能切换游戏。它还可以减少插入时必须用一只手握住的按钮数量。
默认控制器模式按钮组合为:
默认键盘模式按钮组合(仅在使用 DInput 后端时可用,不适用于 XInput):
HayBox 需要与官方 B0XX 固件不同的 Dolphin 控制器配置文件,因为它使用不同的 DInput 映射,这对于在多个游戏中使用更有意义。这些可以在 HayBox 存储库的dolphin
文件夹中找到。配置文件的命名是为了表明它们适用于什么通信后端和操作系统:
要安装配置文件:
dolphin
文件夹复制到文件夹<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 的控制器,您将必须通过按住插件上的 Z 来强制 DInput 模式,即使如此,它也可能无法工作。我对苹果糟糕的控制器支持无能为力(他们似乎在每次其他更新中都破坏了这一点),而且我没有任何苹果设备,所以这也将被视为不受支持的 HayBox 使用。
通信后端(例如 DInput、GameCube 或 N64)部分是通过自动检测选择的,部分是基于插件上的按钮选择的。这是在config/<environment>/config.cpp
的setup()
函数中处理的。逻辑相当简单,即使您没有编程经验,也应该不难看出正在发生的事情并根据需要进行更改。
Arduino环境对应的config文件夹为:
config/arduino_nativeusb/
用于具有本机 USB 支持的 Arduino(例如 Leonardo、Micro)config/arduino/
用于没有本机 USB 支持的 Arduino(例如 Uno、Nano、Mega 2560)对于 Arduino 设备配置,您可能会注意到数字 125 被传递到GamecubeBackend()
中。这可以让您更改轮询率,例如,如果您的 DIY 不支持本机 USB 并且您想将其与超频 GameCube 控制器适配器一起使用。在该示例中,您可以传入 1000 以同步到 1000Hz 轮询率,或传入 0 以完全禁用此滞后修复。轮询率可以按照与此相同的方式传递到 N64Backend 构造函数中。
您可能会注意到 1000Hz 轮询率也适用于控制台。请注意,虽然这有效,但会导致更多的输入延迟。此处设置轮询速率的目的是让 GameCube 后端可以延迟到下一次轮询之前才读取输入,从而使输入是新鲜的且不会过时。
对于 Pico/RP2040,不需要传入控制台轮询率,因为 Pico 在收到控制台轮询后有足够的处理能力来读取/处理输入,因此不需要预测轮询何时到达并做好准备提前。
要配置输入模式(控制器/键盘模式)的按钮保持,请编辑config/mode_selection.hpp
中的select_mode()
函数。每个if
语句都是一个用于选择输入模式的按钮组合。
大多数输入模式支持传入 SOCD 清理模式,例如socd::2IP_NO_REAC
。请参阅此处了解其他可用模式。
对于创建新的输入模式,如果您了解一些 C++,或者至少有一些编程经验,将会有所帮助。也就是说,如果您只是将新模式建立在现有模式的基础上并将其作为示例,那么即使没有经验,您也应该能够顺利进行。
输入模式有两种:ControllerMode 和 KeyboardMode
键盘模式稍微简单一些,所以让我们从这里开始。
KeyboardMode 的行为类似于标准键盘,并且应该适用于任何支持键盘的设备。
您可以在UpdateKeys()
函数中自由使用您喜欢的任何逻辑和编程技巧来根据输入状态决定输出。您可以创建输入图层(例如混战模式下的方向键图层,在按住 Mod X 和 Mod Y 时激活)或其他类型的条件输入。
可用键码列表可以在此处找到。
请记住,键盘模式只能在使用DInput通信后端(而非XInput)时激活。
ControllerMode 接受数字按钮输入状态并将其转换为与标准游戏手柄相对应的输出状态。任何 ControllerMode 都可以与任何 CommunicationBackend 配合使用。 CommunicationBackend 只需从一个或多个输入源读取输入,使用当前的 ControllerMode 根据这些输入更新输出,并处理将输出发送到控制台或 PC。
要创建 ControllerMode,您只需要实现函数UpdateDigitalOutputs()
和UpdateAnalogOutputs()
。
UpdateDigitalOutputs()
与键盘模式下的UpdateKeys()
函数非常相似,不同之处在于,我们只是设置本次迭代的输出状态,而不是调用Press()
函数立即发送输入。顾名思义,我们在此函数中仅处理数字输出。
UpdateAnalogOutputs()
有点复杂。首先,它必须在执行其他操作之前调用UpdateDirections()
。该函数接收的值指示您的左摇杆和右摇杆是否指向左/右/上/下。您还可以传入最小、中性(中心)和最大摇杆模拟值,以便您可以根据每种模式进行配置。所有这些信息都用于根据您传入的输入自动设置摇杆模拟值。除非您想实现修改器,否则这就是您需要做的全部事情。
UpdateDirections()
还使用指示当前摇杆方向的值填充变量directions
,对于两个摇杆的 X 轴和 Y 轴,该值可以是 1、0 或 -1。这些值使编写修饰符逻辑变得更加容易。
调用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-左/C-右和C-下/C-上设置为将应用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 分辨率 - 游戏决定 |
请注意,您不必像在 Melee20Button 和 Melee18Button 模式中那样实现HandleSocd()
函数。它仅在这些模式下被覆盖,以便我们可以在 SOCD 清理之前检查左和右是否都被保持,因为当它们都被保持时(没有垂直方向被保持),我们需要覆盖所有修改器。
如果您的控制器没有遮光罩按钮,您可能需要使用 Mod X 进行遮光罩,并将遮光罩倾斜放在 R 上。您可以使用 Melee18Button 模式而不是 Melee20Button 来完成此操作。
Melee20Button 和 Melee18Button 模式提供了按下 + 向右时使用哪个坐标的选择。默认情况下,按住 + 返回键将允许您自动取消刺拳,这对于某些角色来说是一种有用的技术。
另一种使用下+右对角线的流行技术是所谓的蹲伏/行走选项选择。该技术涉及在蹲伏时以一定角度按住+向前,这样在蹲伏取消攻击后,你将自动开始向对手走去,而不是回到蹲伏状态。这对于技术追逐非常有用,但是用于此技术的坐标不允许您自动取消刺拳。
这可以通过将crouch_walk_os
选项设置为 true 来配置,如config/mode_selection.hpp
中所示:
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 按下的方式与近战不同。近战将 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
- 使用 i2c 从 Wii Nunchuk 读取输入.这可用于混合输入控制器(例如,左手使用双节棍进行移动,右手使用按钮进行其他控制)GamecubeControllerInput
- 与上面类似,但从 GameCube 控制器读取。可以与 GamecubeBackend 类似地实例化。目前仅针对 Pico 实现,您必须在与 GamecubeBackend 的任何实例不同的 pio 实例(pio0 或 pio1)上运行它,或者确保两者使用相同的 PIO 指令内存偏移量。每个输入源都有一个“扫描速度”值,该值大致指示读取输入所需的时间。快速输入源总是在最后可能的时刻读取(至少在 Pico 上),从而导致非常低的延迟。相反,慢速输入源通常在需要之前很久就被读取,因为它们太慢而无法响应轮询而读取。因此,更理想的做法是在单独的内核上不断读取这些输入。这在 AVR MCU 上是不可能的,因为它们都是单核,但在 Pico/RP2040 上是可能的(而且很容易)。默认 Pico 配置config/pico/config.cpp
的底部通过使用 core1 读取 Nunchuk 输入而 core0 处理其他所有内容来说明这一点。有关使用 core1 的更多信息,请参阅下一节。
在每个配置的setup()
函数中,我们构建一个输入源数组,然后将其传递到通信后端。通信后端决定何时读取哪些输入源,因为不同的后端需要在不同的时间点读取输入。我们还构建了一系列通信后端,允许同时使用多个后端。例如,在大多数配置中,每当使用 DInput 后端时,B0XX 输入查看器后端都会用作辅助后端。在每次迭代中,主循环告诉每个后端发送各自的报告。未来,可能会有更多后端用于将信息写入 OLED 显示器等。
在每个配置中,都有函数setup()
和loop()
,其中setup()
首先运行,然后loop()
重复运行,直到设备断电。
在Pico/RP2040上, setup()
和loop()
函数在core0上执行,并且您可以添加函数setup1()
和loop1()
以便在core1上运行任务。
例如,要读取 core1 上的 GameCube 控制器输入:
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
循环确保我们等到 core0 上的setup()
完成通信后端的设置。然后我们创建一个轮询率为 2500Hz 的 GameCube 控制器输入源。我们还在pio1
上运行它,作为避免干扰任何 GameCube/N64 后端的简单方法,除非另有说明,否则这些后端使用pio0
。在loop1()
中,我们假设主后端是backends
数组的第一个元素(无论如何,它是在同一个文件中配置的,所以我们并没有真正假设任何我们不知道的东西)并直接扫描GameCube控制器输入到后端的输入状态。
作为一个稍微疯狂的假设示例,人们甚至可以使用单个 Pico 为两人街机柜的所有控件供电,方法是创建两个开关矩阵输入源,每个输入源使用 10 个引脚,以及两个 GameCube 后端,两者都位于单独的核心上。可能性是无限的。
如果您使用带有基于 Arduino 的控制器的官方适配器,您可能必须在插件上保留 A,该插件通过将 0 的轮询率传递给 GamecubeBackend 构造函数来禁用轮询延迟优化。
如果您使用没有升压电路的基于 Arduino 的控制器,则需要 5V 电源,因此对于 Mayflash 适配器,您需要插入两根 USB 电缆,并且控制台上的隆隆声线需要完好无损。 Pico 本身使用 3.3V 电源工作,因此这不是问题。
我欢迎贡献,如果您想分享一个输入模式,请随时提出拉取请求。请安装 VS Code 的 clang-format 插件并使用它来格式化您想要添加的任何代码。
我们使用 SemVer 进行版本控制。有关可用版本,请参阅此存储库上的标签。
另请参阅参与该项目的贡献者列表。
该项目已获得 GNU GPL 第 3 版许可 - 有关详细信息,请参阅许可证文件