#include "input_manager.h" #include "../core/config.h" #include #include #include #include namespace engine::input { InputManager::InputManager(SDL_Renderer* sdl_renderer, const engine::core::Config* config): sdl_renderer_(sdl_renderer) { if (!sdl_renderer_) { spdlog::error("输入管理器: SDL_Renderer 为空指针"); throw std::runtime_error("输入管理器: SDL_Renderer 为空指针"); } initializeMappings(config); // 获取初始鼠标位置 float x, y; SDL_GetMouseState(&x, &y); mouse_position_ = {x, y}; spdlog::trace("初始鼠标位置: ({}, {})", mouse_position_.x, mouse_position_.y); } // --- 更新和事件处理 --- void InputManager::update() { // 1. 根据上一帧的值更新默认的动作状态 for (auto& [action_name, state] : action_states_) { if (state == ActionState::PRESSED_THIS_FRAME) { state = ActionState::HELD_DOWN; // 当某个键按下不动时,并不会生成SDL_Event。 } else if (state == ActionState::RELEASED_THIS_FRAME) { state = ActionState::INACTIVE; } } // 2. 处理所有待处理的 SDL 事件 (这将设定 action_states_ 的值) SDL_Event event; while (SDL_PollEvent(&event)) { processEvent(event); } } void InputManager::processEvent(const SDL_Event& event) { switch (event.type) { case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: { SDL_Scancode scancode = event.key.scancode; // 获取按键的scancode bool is_down = event.key.down; bool is_repeat = event.key.repeat; auto it = scancode_to_actions_map_.find(scancode); if (it != scancode_to_actions_map_.end()) { // 如果按键有对应的action const std::vector& associated_actions = it->second; for (const std::string& action_name : associated_actions) { updateActionState(action_name, is_down, is_repeat); // 更新action状态 } } break; } case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_UP: { Uint8 button = event.button.button; // 获取鼠标按钮 bool is_down = event.button.down; auto it = mouse_button_to_actions_map_.find(button); if (it != mouse_button_to_actions_map_.end()) { // 如果鼠标按钮有对应的action const std::vector& associated_actions = it->second; for (const std::string& action_name : associated_actions) { // 鼠标事件不考虑repeat, 所以第三个参数传false updateActionState(action_name, is_down, false); // 更新action状态 } } // 在点击时更新鼠标位置 mouse_position_ = {event.button.x, event.button.y}; break; } case SDL_EVENT_MOUSE_MOTION: // 处理鼠标运动 mouse_position_ = {event.motion.x, event.motion.y}; break; case SDL_EVENT_QUIT: should_quit_ = true; break; default: break; } } // --- 状态查询方法 --- bool InputManager::isActionDown(const std::string& action_name) const { // C++17 引入的 “带有初始化语句的 if 语句” if (auto it = action_states_.find(action_name); it != action_states_.end()) { return it->second == ActionState::PRESSED_THIS_FRAME || it->second == ActionState::HELD_DOWN; } return false; } bool InputManager::isActionPressed(const std::string& action_name) const { if (auto it = action_states_.find(action_name); it != action_states_.end()) { return it->second == ActionState::PRESSED_THIS_FRAME; } return false; } bool InputManager::isActionReleased(const std::string& action_name) const { if (auto it = action_states_.find(action_name); it != action_states_.end()) { return it->second == ActionState::RELEASED_THIS_FRAME; } return false; } bool InputManager::shouldQuit() const { return should_quit_; } void InputManager::setShouldQuit(bool should_quit) { should_quit_ = should_quit; } glm::vec2 InputManager::getMousePosition() const { return mouse_position_; } glm::vec2 InputManager::getLogicalMousePosition() const { glm::vec2 logical_pos; // 通过窗口坐标获取渲染坐标(逻辑坐标) SDL_RenderCoordinatesFromWindow(sdl_renderer_, mouse_position_.x, mouse_position_.y, &logical_pos.x, &logical_pos.y); return logical_pos; } // --- 初始化输入映射 --- void InputManager::initializeMappings(const engine::core::Config* config) { spdlog::trace("初始化输入映射..."); if (!config) { spdlog::error("输入管理器: Config 为空指针"); throw std::runtime_error("输入管理器: Config 为空指针"); } actions_to_keyname_map_ = config->input_mappings_; // 获取配置中的输入映射(动作 -> 按键名称) scancode_to_actions_map_.clear(); mouse_button_to_actions_map_.clear(); action_states_.clear(); // 如果配置中没有定义鼠标按钮动作(通常不需要配置),则添加默认映射, 用于 UI if (actions_to_keyname_map_.find("MouseLeftClick") == actions_to_keyname_map_.end()) { spdlog::debug("配置中没有定义 'MouseLeftClick' 动作,添加默认映射到 'MouseLeft'."); actions_to_keyname_map_["MouseLeftClick"] = {"MouseLeft"}; // 如果缺失则添加默认映射 } if (actions_to_keyname_map_.find("MouseRightClick") == actions_to_keyname_map_.end()) { spdlog::debug("配置中没有定义 'MouseRightClick' 动作,添加默认映射到 'MouseRight'."); actions_to_keyname_map_["MouseRightClick"] = {"MouseRight"}; // 如果缺失则添加默认映射 } // 遍历 动作 -> 按键名称 的映射 for (const auto& [action_name, key_names] : actions_to_keyname_map_) { // 每个动作对应一个动作状态,初始化为 INACTIVE action_states_[action_name] = ActionState::INACTIVE; spdlog::trace("映射动作: {}", action_name); // 设置 "按键 -> 动作" 的映射 for (const std::string& key_name : key_names) { SDL_Scancode scancode = scancodeFromString(key_name); // 尝试根据按键名称获取scancode Uint8 mouse_button = mouseButtonUint8FromString(key_name); // 尝试根据按键名称获取鼠标按钮 // 未来可添加其它输入类型 ... if (scancode != SDL_SCANCODE_UNKNOWN) { // 如果scancode有效,则将action添加到scancode_to_actions_map_中 scancode_to_actions_map_[scancode].push_back(action_name); spdlog::trace(" 映射按键: {} (Scancode: {}) 到动作: {}", key_name, static_cast(scancode), action_name); } else if (mouse_button != 0) { // 如果鼠标按钮有效,则将action添加到mouse_button_to_actions_map_中 mouse_button_to_actions_map_[mouse_button].push_back(action_name); spdlog::trace(" 映射鼠标按钮: {} (Button ID: {}) 到动作: {}", key_name, static_cast(mouse_button), action_name); // else if: 未来可添加其它输入类型 ... } else { spdlog::warn("输入映射警告: 未知键或按钮名称 '{}' 用于动作 '{}'.", key_name, action_name); } } } spdlog::trace("输入映射初始化完成."); } // --- 工具函数 --- // 将字符串名称转换为 SDL_Scancode SDL_Scancode InputManager::scancodeFromString(const std::string& key_name) { return SDL_GetScancodeFromName(key_name.c_str()); } // 将鼠标按钮名称字符串转换为 SDL 按钮 Uint8 值 Uint8 InputManager::mouseButtonUint8FromString(const std::string& button_name) { if (button_name == "MouseLeft") return SDL_BUTTON_LEFT; if (button_name == "MouseMiddle") return SDL_BUTTON_MIDDLE; if (button_name == "MouseRight") return SDL_BUTTON_RIGHT; // SDL 还定义了 SDL_BUTTON_X1 和 SDL_BUTTON_X2 if (button_name == "MouseX1") return SDL_BUTTON_X1; if (button_name == "MouseX2") return SDL_BUTTON_X2; return 0; // 0 不是有效的按钮值,表示无效 } void InputManager::updateActionState(const std::string& action_name, bool is_input_active, bool is_repeat_event) { auto it = action_states_.find(action_name); if (it == action_states_.end()) { spdlog::warn("尝试更新未注册的动作状态: {}", action_name); return; } if (is_input_active) { // 输入被激活 (按下) if (is_repeat_event) { it->second = ActionState::HELD_DOWN; } else { // 非重复的按下事件 it->second = ActionState::PRESSED_THIS_FRAME; } } else { // 输入被释放 (松开) it->second = ActionState::RELEASED_THIS_FRAME; } } } // namespace engine::input