feat(事件系统): 添加SDL事件转换器并重构窗口事件处理

添加EventConverter类用于将SDL事件转换为引擎内部事件格式
重构Window类的事件处理逻辑,支持通过回调函数处理转换后的事件
修改Application类以使用新的事件系统
This commit is contained in:
ChestnutYueyue 2026-02-23 03:13:00 +08:00
parent 3cec8ff7ef
commit 137369c37c
6 changed files with 297 additions and 23 deletions

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <cstdint>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <variant> #include <variant>

View File

@ -0,0 +1,31 @@
#pragma once
#include <extra2d/event/event.h>
#include <SDL.h>
namespace extra2d {
/**
* @brief SDL事件转换器
*/
class EventConverter {
public:
static Event convert(const SDL_Event& e);
private:
static Event convertKey(const SDL_KeyboardEvent& e, EventType type);
static Event convertMouseBtn(const SDL_MouseButtonEvent& e, EventType type);
static Event convertMouseMove(const SDL_MouseMotionEvent& e);
static Event convertMouseWheel(const SDL_MouseWheelEvent& e);
static Event convertWindow(const SDL_WindowEvent& e);
static Event convertGamepadBtn(const SDL_ControllerButtonEvent& e, EventType type);
static Event convertGamepadAxis(const SDL_ControllerAxisEvent& e);
static Event convertTouch(const SDL_TouchFingerEvent& e, EventType type);
static i32 mapKey(SDL_Keycode key);
static i32 mapMouseBtn(u8 btn);
static i32 mapGamepadBtn(u8 btn);
static i32 mapGamepadAxis(u8 axis);
};
} // namespace extra2d

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <functional>
#include <string> #include <string>
#include <SDL.h> #include <SDL.h>
@ -18,25 +19,29 @@ struct WindowConfig {
class Window { class Window {
public: public:
using OnEvent = std::function<void(const SDL_Event&)>;
Window(); Window();
~Window(); ~Window();
bool create(const WindowConfig& config); bool create(const WindowConfig& config);
void destroy(); void destroy();
void pollEvents(); void poll();
bool shouldClose() const; bool shouldClose() const;
void setShouldClose(bool close); void close();
void setTitle(const std::string& title); void setTitle(const std::string& title);
void setSize(int width, int height); void setSize(int width, int height);
void setFullscreen(bool fullscreen); void setFullscreen(bool fullscreen);
int getWidth() const { return width_; } int width() const { return width_; }
int getHeight() const { return height_; } int height() const { return height_; }
bool isFullscreen() const { return fullscreen_; } bool fullscreen() const { return fullscreen_; }
SDL_Window* sdlWindow() const { return sdlWindow_; } void onEvent(OnEvent cb) { onEvent_ = cb; }
SDL_Window* native() const { return sdlWindow_; }
private: private:
SDL_Window* sdlWindow_ = nullptr; SDL_Window* sdlWindow_ = nullptr;
@ -44,6 +49,7 @@ private:
int height_ = 720; int height_ = 720;
bool fullscreen_ = true; bool fullscreen_ = true;
bool shouldClose_ = false; bool shouldClose_ = false;
OnEvent onEvent_;
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -1,6 +1,8 @@
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/render/render_device.h> #include <extra2d/render/render_device.h>
#include <extra2d/services/event_service.h>
#include <extra2d/services/logger_service.h> #include <extra2d/services/logger_service.h>
#include <extra2d/window/event_converter.h>
#include <extra2d/window/window.h> #include <extra2d/window/window.h>
#include <SDL.h> #include <SDL.h>
@ -30,8 +32,19 @@ bool Application::init() {
return false; return false;
} }
// 设置事件回调,将 SDL 事件转换为 Extra2D 事件
window_->onEvent([](const SDL_Event &e) {
Event event = EventConverter::convert(e);
if (event.type != EventType::None) {
auto eventService = ServiceLocator::instance().get<IEventService>();
if (eventService) {
eventService->push(event);
}
}
});
auto &device = RenderDevice::instance(); auto &device = RenderDevice::instance();
if (!device.init(window_->sdlWindow())) { if (!device.init(window_->native())) {
E2D_ERROR(CAT_APP, "渲染设备初始化失败"); E2D_ERROR(CAT_APP, "渲染设备初始化失败");
window_->destroy(); window_->destroy();
delete window_; delete window_;
@ -95,7 +108,7 @@ void Application::mainLoop() {
fpsTimer_ -= 1.0f; fpsTimer_ -= 1.0f;
} }
window_->pollEvents(); window_->poll();
ServiceLocator::instance().update(dt_); ServiceLocator::instance().update(dt_);
RenderDevice::instance().swapBuffers(); RenderDevice::instance().swapBuffers();
} }

View File

@ -0,0 +1,225 @@
#include <extra2d/window/event_converter.h>
namespace extra2d {
Event EventConverter::convert(const SDL_Event& e) {
switch (e.type) {
case SDL_KEYDOWN:
return convertKey(e.key, EventType::KeyPressed);
case SDL_KEYUP:
return convertKey(e.key, EventType::KeyReleased);
case SDL_MOUSEBUTTONDOWN:
return convertMouseBtn(e.button, EventType::MouseButtonPressed);
case SDL_MOUSEBUTTONUP:
return convertMouseBtn(e.button, EventType::MouseButtonReleased);
case SDL_MOUSEMOTION:
return convertMouseMove(e.motion);
case SDL_MOUSEWHEEL:
return convertMouseWheel(e.wheel);
case SDL_WINDOWEVENT:
return convertWindow(e.window);
case SDL_CONTROLLERBUTTONDOWN:
return convertGamepadBtn(e.cbutton, EventType::GamepadButtonPressed);
case SDL_CONTROLLERBUTTONUP:
return convertGamepadBtn(e.cbutton, EventType::GamepadButtonReleased);
case SDL_CONTROLLERAXISMOTION:
return convertGamepadAxis(e.caxis);
case SDL_FINGERDOWN:
return convertTouch(e.tfinger, EventType::TouchBegan);
case SDL_FINGERMOTION:
return convertTouch(e.tfinger, EventType::TouchMoved);
case SDL_FINGERUP:
return convertTouch(e.tfinger, EventType::TouchEnded);
default:
return Event{};
}
}
Event EventConverter::convertKey(const SDL_KeyboardEvent& e, EventType type) {
Event event;
event.type = type;
event.data = KeyEvent{mapKey(e.keysym.sym), static_cast<i32>(e.keysym.scancode), e.keysym.mod};
return event;
}
Event EventConverter::convertMouseBtn(const SDL_MouseButtonEvent& e, EventType type) {
Event event;
event.type = type;
event.data = MouseButtonEvent{mapMouseBtn(e.button), 0, Vec2{static_cast<f32>(e.x), static_cast<f32>(e.y)}};
return event;
}
Event EventConverter::convertMouseMove(const SDL_MouseMotionEvent& e) {
Event event;
event.type = EventType::MouseMoved;
event.data = MouseMoveEvent{Vec2{static_cast<f32>(e.x), static_cast<f32>(e.y)}, Vec2{static_cast<f32>(e.xrel), static_cast<f32>(e.yrel)}};
return event;
}
Event EventConverter::convertMouseWheel(const SDL_MouseWheelEvent& e) {
Event event;
event.type = EventType::MouseScrolled;
event.data = MouseScrollEvent{Vec2{static_cast<f32>(e.x), static_cast<f32>(e.y)}, Vec2{}};
return event;
}
Event EventConverter::convertWindow(const SDL_WindowEvent& e) {
switch (e.event) {
case SDL_WINDOWEVENT_CLOSE:
return Event::windowClose();
case SDL_WINDOWEVENT_RESIZED:
case SDL_WINDOWEVENT_SIZE_CHANGED:
return Event::windowResize(e.data1, e.data2);
default:
return Event{};
}
}
Event EventConverter::convertGamepadBtn(const SDL_ControllerButtonEvent& e, EventType type) {
Event event;
event.type = type;
event.data = GamepadButtonEvent{static_cast<i32>(e.which), mapGamepadBtn(e.button)};
return event;
}
Event EventConverter::convertGamepadAxis(const SDL_ControllerAxisEvent& e) {
Event event;
event.type = EventType::GamepadAxisMoved;
event.data = GamepadAxisEvent{static_cast<i32>(e.which), mapGamepadAxis(e.axis), static_cast<f32>(e.value) / 32767.0f};
return event;
}
Event EventConverter::convertTouch(const SDL_TouchFingerEvent& e, EventType type) {
Event event;
event.type = type;
// SDL 触摸坐标是归一化的 (0.0 - 1.0),转换为像素坐标
event.data = TouchEvent{
static_cast<i32>(e.fingerId),
Vec2{e.x, e.y}
};
return event;
}
i32 EventConverter::mapKey(SDL_Keycode key) {
switch (key) {
case SDLK_a: return 0;
case SDLK_b: return 1;
case SDLK_c: return 2;
case SDLK_d: return 3;
case SDLK_e: return 4;
case SDLK_f: return 5;
case SDLK_g: return 6;
case SDLK_h: return 7;
case SDLK_i: return 8;
case SDLK_j: return 9;
case SDLK_k: return 10;
case SDLK_l: return 11;
case SDLK_m: return 12;
case SDLK_n: return 13;
case SDLK_o: return 14;
case SDLK_p: return 15;
case SDLK_q: return 16;
case SDLK_r: return 17;
case SDLK_s: return 18;
case SDLK_t: return 19;
case SDLK_u: return 20;
case SDLK_v: return 21;
case SDLK_w: return 22;
case SDLK_x: return 23;
case SDLK_y: return 24;
case SDLK_z: return 25;
case SDLK_0: return 26;
case SDLK_1: return 27;
case SDLK_2: return 28;
case SDLK_3: return 29;
case SDLK_4: return 30;
case SDLK_5: return 31;
case SDLK_6: return 32;
case SDLK_7: return 33;
case SDLK_8: return 34;
case SDLK_9: return 35;
case SDLK_F1: return 36;
case SDLK_F2: return 37;
case SDLK_F3: return 38;
case SDLK_F4: return 39;
case SDLK_F5: return 40;
case SDLK_F6: return 41;
case SDLK_F7: return 42;
case SDLK_F8: return 43;
case SDLK_F9: return 44;
case SDLK_F10: return 45;
case SDLK_F11: return 46;
case SDLK_F12: return 47;
case SDLK_SPACE: return 48;
case SDLK_RETURN: return 49;
case SDLK_ESCAPE: return 50;
case SDLK_TAB: return 51;
case SDLK_BACKSPACE: return 52;
case SDLK_INSERT: return 53;
case SDLK_DELETE: return 54;
case SDLK_HOME: return 55;
case SDLK_END: return 56;
case SDLK_PAGEUP: return 57;
case SDLK_PAGEDOWN: return 58;
case SDLK_UP: return 59;
case SDLK_DOWN: return 60;
case SDLK_LEFT: return 61;
case SDLK_RIGHT: return 62;
case SDLK_LSHIFT: return 63;
case SDLK_RSHIFT: return 64;
case SDLK_LCTRL: return 65;
case SDLK_RCTRL: return 66;
case SDLK_LALT: return 67;
case SDLK_RALT: return 68;
case SDLK_CAPSLOCK: return 69;
case SDLK_NUMLOCKCLEAR: return 70;
case SDLK_SCROLLLOCK: return 71;
default: return -1;
}
}
i32 EventConverter::mapMouseBtn(u8 btn) {
switch (btn) {
case SDL_BUTTON_LEFT: return 0;
case SDL_BUTTON_RIGHT: return 1;
case SDL_BUTTON_MIDDLE: return 2;
case SDL_BUTTON_X1: return 3;
case SDL_BUTTON_X2: return 4;
default: return -1;
}
}
i32 EventConverter::mapGamepadBtn(u8 btn) {
switch (btn) {
case SDL_CONTROLLER_BUTTON_A: return 0;
case SDL_CONTROLLER_BUTTON_B: return 1;
case SDL_CONTROLLER_BUTTON_X: return 2;
case SDL_CONTROLLER_BUTTON_Y: return 3;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return 4;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return 5;
case SDL_CONTROLLER_BUTTON_LEFTSTICK: return 6;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return 7;
case SDL_CONTROLLER_BUTTON_BACK: return 8;
case SDL_CONTROLLER_BUTTON_START: return 9;
case SDL_CONTROLLER_BUTTON_GUIDE: return 10;
case SDL_CONTROLLER_BUTTON_DPAD_UP: return 11;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return 12;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return 13;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return 14;
default: return -1;
}
}
i32 EventConverter::mapGamepadAxis(u8 axis) {
switch (axis) {
case SDL_CONTROLLER_AXIS_LEFTX: return 0;
case SDL_CONTROLLER_AXIS_LEFTY: return 1;
case SDL_CONTROLLER_AXIS_RIGHTX: return 2;
case SDL_CONTROLLER_AXIS_RIGHTY: return 3;
case SDL_CONTROLLER_AXIS_TRIGGERLEFT: return 4;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: return 5;
default: return -1;
}
}
} // namespace extra2d

View File

@ -61,28 +61,28 @@ void Window::destroy() {
} }
} }
void Window::pollEvents() { void Window::poll() {
SDL_Event event; SDL_Event e;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&e)) {
switch (event.type) { if (e.type == SDL_QUIT) {
case SDL_QUIT:
shouldClose_ = true; shouldClose_ = true;
break; } else if (e.type == SDL_WINDOWEVENT) {
if (e.window.event == SDL_WINDOWEVENT_RESIZED ||
case SDL_WINDOWEVENT: e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
if (event.window.event == SDL_WINDOWEVENT_RESIZED || width_ = e.window.data1;
event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { height_ = e.window.data2;
width_ = event.window.data1;
height_ = event.window.data2;
} }
break; }
if (onEvent_) {
onEvent_(e);
} }
} }
} }
bool Window::shouldClose() const { return shouldClose_; } bool Window::shouldClose() const { return shouldClose_; }
void Window::setShouldClose(bool close) { shouldClose_ = close; } void Window::close() { shouldClose_ = true; }
void Window::setTitle(const std::string &title) { void Window::setTitle(const std::string &title) {
if (sdlWindow_) { if (sdlWindow_) {