Adapt game input for Switch controllers

This commit is contained in:
2026-06-09 13:28:31 +08:00
parent 8bbb847a1b
commit d5e876e542
4 changed files with 137 additions and 12 deletions
+111 -4
View File
@@ -1,34 +1,141 @@
#include "input_state.h"
#include <SDL2/SDL.h>
#include <algorithm>
#ifdef __SWITCH__
#include <switch.h>
#endif
namespace ns_game {
namespace {
constexpr Sint16 kControllerAxisDeadZone = 10000;
bool isDown(const Uint8* keys, SDL_Scancode key) {
return keys && keys[key] != 0;
}
bool controllerButtonDown(SDL_GameController* controller,
SDL_GameControllerButton button) {
return controller &&
SDL_GameControllerGetButton(controller, button) == SDL_PRESSED;
}
Sint16 controllerAxis(SDL_GameController* controller,
SDL_GameControllerAxis axis) {
if (!controller) {
return 0;
}
return SDL_GameControllerGetAxis(controller, axis);
}
#ifdef __SWITCH__
PadState& switchPad() {
static PadState pad;
static bool initialized = false;
if (!initialized) {
padInitializeDefault(&pad);
initialized = true;
}
return pad;
}
#endif
} // namespace
InputStateProvider::InputStateProvider() {
ensureController();
}
InputStateProvider::~InputStateProvider() {
if (controller_) {
SDL_GameControllerClose(controller_);
controller_ = nullptr;
}
}
void InputStateProvider::ensureController() {
if (controller_) {
return;
}
const int joystickCount = SDL_NumJoysticks();
for (int i = 0; i < joystickCount; ++i) {
if (!SDL_IsGameController(i)) {
continue;
}
controller_ = SDL_GameControllerOpen(i);
if (controller_) {
SDL_Log("InputStateProvider opened controller: %s",
SDL_GameControllerName(controller_));
return;
}
}
}
InputState InputStateProvider::Sample() {
ensureController();
const Uint8* keys = SDL_GetKeyboardState(nullptr);
InputState state;
bool moveLeftHeld = false;
bool moveRightHeld = false;
if (isDown(keys, SDL_SCANCODE_A) || isDown(keys, SDL_SCANCODE_LEFT)) {
state.moveX -= 1.0f;
moveLeftHeld = true;
}
if (isDown(keys, SDL_SCANCODE_D) || isDown(keys, SDL_SCANCODE_RIGHT)) {
state.moveX += 1.0f;
moveRightHeld = true;
}
const bool jumpHeld =
bool jumpHeld =
isDown(keys, SDL_SCANCODE_SPACE) || isDown(keys, SDL_SCANCODE_W) ||
isDown(keys, SDL_SCANCODE_UP);
const bool attackHeld =
bool attackHeld =
isDown(keys, SDL_SCANCODE_J) || isDown(keys, SDL_SCANCODE_K);
if (controller_) {
const Sint16 leftX =
controllerAxis(controller_, SDL_CONTROLLER_AXIS_LEFTX);
moveLeftHeld = moveLeftHeld ||
leftX < -kControllerAxisDeadZone ||
controllerButtonDown(controller_,
SDL_CONTROLLER_BUTTON_DPAD_LEFT);
moveRightHeld = moveRightHeld ||
leftX > kControllerAxisDeadZone ||
controllerButtonDown(controller_,
SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
jumpHeld = jumpHeld ||
controllerButtonDown(controller_, SDL_CONTROLLER_BUTTON_A) ||
controllerButtonDown(controller_, SDL_CONTROLLER_BUTTON_B);
attackHeld = attackHeld ||
controllerButtonDown(controller_, SDL_CONTROLLER_BUTTON_X) ||
controllerButtonDown(controller_, SDL_CONTROLLER_BUTTON_Y);
}
#ifdef __SWITCH__
PadState& pad = switchPad();
padUpdate(&pad);
const u64 buttons = padGetButtons(&pad);
moveLeftHeld = moveLeftHeld || (buttons & HidNpadButton_AnyLeft) != 0;
moveRightHeld = moveRightHeld || (buttons & HidNpadButton_AnyRight) != 0;
jumpHeld = jumpHeld ||
(buttons & (HidNpadButton_A | HidNpadButton_B)) != 0;
attackHeld = attackHeld ||
(buttons & (HidNpadButton_X | HidNpadButton_Y)) != 0;
#endif
if (moveLeftHeld) {
state.moveX -= 1.0f;
}
if (moveRightHeld) {
state.moveX += 1.0f;
}
state.moveX = std::clamp(state.moveX, -1.0f, 1.0f);
state.jumpPressed = jumpHeld && !jumpHeldLastFrame_;
state.attackPressed = attackHeld && !attackHeldLastFrame_;
+11
View File
@@ -1,5 +1,7 @@
#pragma once
#include <SDL2/SDL_gamecontroller.h>
namespace ns_game {
struct InputState {
@@ -10,9 +12,18 @@ struct InputState {
class InputStateProvider {
public:
InputStateProvider();
~InputStateProvider();
InputStateProvider(const InputStateProvider&) = delete;
InputStateProvider& operator=(const InputStateProvider&) = delete;
InputState Sample();
private:
void ensureController();
SDL_GameController* controller_ = nullptr;
bool jumpHeldLastFrame_ = false;
bool attackHeldLastFrame_ = false;
};