Extra2D/docs/API_Tutorial/05_Input_Handling.md

7.9 KiB
Raw Blame History

05. 输入处理

Extra2D 提供了统一的输入处理系统,支持手柄、键盘等多种输入设备。

输入管理器

通过 Application::instance().input() 访问输入管理器:

auto& input = Application::instance().input();

按键检测

检测方法

// 按键是否按下(持续触发)
if (input.isButtonDown(GamepadButton::A)) {
    // 每帧都会触发,只要按键保持按下
}

// 按键是否刚按下(单次触发)
if (input.isButtonPressed(GamepadButton::A)) {
    // 只在按下瞬间触发一次
}

// 按键是否刚释放
if (input.isButtonReleased(GamepadButton::A)) {
    // 只在释放瞬间触发一次
}

常用按键

按键 说明 Xbox 对应 Switch 对应
GamepadButton::A A 键 A 键 A 键
GamepadButton::B B 键 B 键 B 键
GamepadButton::X X 键 X 键 X 键
GamepadButton::Y Y 键 Y 键 Y 键
GamepadButton::Start 开始键 Menu 键 + 键
GamepadButton::Back 返回键 View 键 - 键
GamepadButton::Guide 导航键 Xbox 键 Home 键
GamepadButton::DPadUp 方向上 方向键上 方向键上
GamepadButton::DPadDown 方向下 方向键下 方向键下
GamepadButton::DPadLeft 方向左 方向键左 方向键左
GamepadButton::DPadRight 方向右 方向键右 方向键右
GamepadButton::LeftStick 左摇杆按下 L3 L3
GamepadButton::RightStick 右摇杆按下 R3 R3
GamepadButton::LeftShoulder 左肩键 LB L
GamepadButton::RightShoulder 右肩键 RB R
GamepadButton::LeftTrigger 左扳机键 LT ZL
GamepadButton::RightTrigger 右扳机键 RT ZR

摇杆输入

获取摇杆值

// 获取左摇杆位置(范围 -1.0 到 1.0
Vec2 leftStick = input.getLeftStick();

// 获取右摇杆位置
Vec2 rightStick = input.getRightStick();

// 应用摇杆输入
float speed = 200.0f;
player->setPosition(player->getPosition() + leftStick * speed * dt);

摇杆死区

// 设置摇杆死区(默认 0.15
input.setStickDeadZone(0.2f);

鼠标输入

鼠标按钮检测

auto& input = Application::instance().input();

// 检测鼠标按钮按下(单次触发)
if (input.isMousePressed(MouseButton::Left)) {
    // 左键刚按下
}

if (input.isMousePressed(MouseButton::Right)) {
    // 右键刚按下
}

// 检测鼠标按钮状态(持续触发)
if (input.isMouseDown(MouseButton::Left)) {
    // 左键保持按下
}

// 检测鼠标按钮释放
if (input.isMouseReleased(MouseButton::Left)) {
    // 左键刚释放
}

鼠标位置

// 获取鼠标位置(屏幕坐标)
Vec2 mousePos = input.getMousePosition();

// 获取鼠标在游戏视口中的位置(考虑视口适配)
// 参考 examples/flappy_bird 的 BaseScene 实现

鼠标按钮枚举

枚举值 说明
MouseButton::Left 左键
MouseButton::Right 右键
MouseButton::Middle 中键

完整示例Flappy Bird 输入处理

参考 examples/flappy_bird/GameScene.cpp

void GameScene::onUpdate(float dt) {
  if (!gameOver_) {
    auto &input = extra2d::Application::instance().input();

    // 同时支持手柄 A 键和鼠标左键
    if (input.isButtonPressed(extra2d::GamepadButton::A) ||
        input.isMousePressed(extra2d::MouseButton::Left)) {
      if (!started_) {
        started_ = true;
        startGame();
      }
      bird_->jump();
    }

    // 游戏逻辑更新...
  }

  BaseScene::onUpdate(dt);
}

完整示例Game Over 界面输入

参考 examples/flappy_bird/GameOverLayer.cpp

void GameOverLayer::onUpdate(float dt) {
  Node::onUpdate(dt);

  // 动画完成后才响应输入
  if (!animationDone_)
    return;

  auto &input = extra2d::Application::instance().input();

  // A 键重新开始游戏
  if (input.isButtonPressed(extra2d::GamepadButton::A)) {
    ResLoader::playMusic(MusicType::Click);
    auto &app = extra2d::Application::instance();
    app.scenes().replaceScene(extra2d::shared<GameScene>(),
                              extra2d::TransitionType::Fade, 0.5f);
  }

  // B 键返回主菜单
  if (input.isButtonPressed(extra2d::GamepadButton::B)) {
    ResLoader::playMusic(MusicType::Click);
    auto &app = extra2d::Application::instance();
    app.scenes().replaceScene(extra2d::shared<StartScene>(),
                              extra2d::TransitionType::Fade, 0.5f);
  }
}

完整示例

菜单导航

参考 examples/flappy_bird/StartScene.cpp

void StartScene::onUpdate(float dt) {
    Scene::onUpdate(dt);

    auto& input = Application::instance().input();

    // A 键开始游戏
    if (input.isButtonPressed(GamepadButton::A)) {
        ResLoader::playMusic(MusicType::Click);
        startGame();
    }

    // BACK 键退出游戏
    if (input.isButtonPressed(GamepadButton::Back)) {
        ResLoader::playMusic(MusicType::Click);
        auto &app = Application::instance();
        app.quit();
    }
}

Game Over 界面手柄控制

参考 examples/flappy_bird/GameOverLayer.cpp

void GameOverLayer::onUpdate(float dt) {
    Node::onUpdate(dt);

    // 检测手柄按键
    auto &input = extra2d::Application::instance().input();
    
    // A 键重新开始游戏
    if (input.isButtonPressed(extra2d::GamepadButton::A)) {
        ResLoader::playMusic(MusicType::Click);
        auto &app = extra2d::Application::instance();
        app.scenes().replaceScene(extra2d::shared<GameScene>(),
                                  extra2d::TransitionType::Fade, 0.5f);
    }
    
    // B 键返回主菜单
    if (input.isButtonPressed(extra2d::GamepadButton::B)) {
        ResLoader::playMusic(MusicType::Click);
        auto &app = extra2d::Application::instance();
        app.scenes().replaceScene(extra2d::shared<StartScene>(),
                                  extra2d::TransitionType::Fade, 0.5f);
    }
}

玩家移动

void Player::update(float dt) {
    auto& input = Application::instance().input();
    
    Vec2 moveDir;
    
    // 方向键移动
    if (input.isButtonDown(GamepadButton::DPadLeft)) {
        moveDir.x -= 1;
    }
    if (input.isButtonDown(GamepadButton::DPadRight)) {
        moveDir.x += 1;
    }
    if (input.isButtonDown(GamepadButton::DPadUp)) {
        moveDir.y -= 1;
    }
    if (input.isButtonDown(GamepadButton::DPadDown)) {
        moveDir.y += 1;
    }
    
    // 摇杆移动
    Vec2 stick = input.getLeftStick();
    if (stick.length() > 0.1f) {
        moveDir = stick;
    }
    
    // 应用移动
    if (moveDir.length() > 0) {
        moveDir.normalize();
        setPosition(getPosition() + moveDir * speed_ * dt);
    }
    
    // 跳跃
    if (input.isButtonPressed(GamepadButton::A)) {
        jump();
    }
}

输入映射

自定义按键映射

// 定义动作
enum class Action {
    Jump,
    Attack,
    Pause
};

// 映射按键到动作
std::unordered_map<Action, GamepadButton> actionMap = {
    {Action::Jump, GamepadButton::A},
    {Action::Attack, GamepadButton::B},
    {Action::Pause, GamepadButton::Start}
};

// 检查动作
bool isActionPressed(Action action) {
    auto& input = Application::instance().input();
    return input.isButtonPressed(actionMap[action]);
}

最佳实践

  1. 使用 isButtonPressed 进行菜单操作 - 避免持续触发
  2. 使用 isButtonDown 进行移动控制 - 实现流畅移动
  3. 支持多种输入方式 - 同时支持方向键和摇杆
  4. 添加输入缓冲 - 提升操作手感
  5. 为常用操作分配标准按键
    • A 键:确认、跳跃、主要动作
    • B 键:取消、返回、次要动作
    • Start 键:暂停菜单
    • Back 键:返回上一级/退出

下一步