From 08570f3aabb590eebbbd08b515673cd796c73e37 Mon Sep 17 00:00:00 2001 From: Lenheart <947330670@qq.com> Date: Sat, 28 Mar 2026 03:57:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E4=BA=8B=E4=BB=B6=E7=B3=BB=E7=BB=9F):=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=AE=8C=E6=95=B4=E7=9A=84=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加事件系统核心组件,包括事件基类、各种输入事件类型和事件分发机制。主要功能: - 新增基础事件类及键盘、鼠标、触摸、手柄等输入事件 - 在Actor类中实现事件监听和分发功能 - 在Scene类中添加事件处理逻辑 - 在Application类中集成SDL事件转换和分发 - 添加测试用例验证手柄事件处理 事件系统支持冒泡和优先级排序,为游戏交互提供基础支持 --- Frostbite2D/include/frostbite2D/2d/actor.h | 58 ++++- .../include/frostbite2D/core/application.h | 5 + Frostbite2D/include/frostbite2D/event/event.h | 63 ++++++ .../frostbite2D/event/joystick_event.h | 112 ++++++++++ .../include/frostbite2D/event/key_event.h | 208 ++++++++++++++++++ .../include/frostbite2D/event/mouse_event.h | 108 +++++++++ .../include/frostbite2D/event/touch_event.h | 64 ++++++ .../include/frostbite2D/event/window_event.h | 77 +++++++ Frostbite2D/include/frostbite2D/scene/scene.h | 10 + Frostbite2D/src/frostbite2D/2d/actor.cpp | 170 ++++++++++++++ .../src/frostbite2D/core/application.cpp | 165 ++++++++++++-- Frostbite2D/src/frostbite2D/scene/scene.cpp | 53 +++++ Game/src/main.cpp | 45 +++- 13 files changed, 1117 insertions(+), 21 deletions(-) create mode 100644 Frostbite2D/include/frostbite2D/event/event.h create mode 100644 Frostbite2D/include/frostbite2D/event/joystick_event.h create mode 100644 Frostbite2D/include/frostbite2D/event/key_event.h create mode 100644 Frostbite2D/include/frostbite2D/event/mouse_event.h create mode 100644 Frostbite2D/include/frostbite2D/event/touch_event.h create mode 100644 Frostbite2D/include/frostbite2D/event/window_event.h diff --git a/Frostbite2D/include/frostbite2D/2d/actor.h b/Frostbite2D/include/frostbite2D/2d/actor.h index a2bda6b..d2dd705 100644 --- a/Frostbite2D/include/frostbite2D/2d/actor.h +++ b/Frostbite2D/include/frostbite2D/2d/actor.h @@ -4,8 +4,16 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include +#include +#include namespace frostbite2D { @@ -70,7 +78,43 @@ public: ActorList& GetChildren() { return children_; } const ActorList& GetChildren() const { return children_; } -protected: + bool IsEventReceiveEnabled() const { return eventReceiveEnabled_; } + void EnableEventReceive() { eventReceiveEnabled_ = true; } + void DisableEventReceive() { eventReceiveEnabled_ = false; } + + int32 GetEventPriority() const { return eventPriority_; } + void SetEventPriority(int32 priority) { eventPriority_ = priority; } + + bool IsEventInterceptEnabled() const { return eventInterceptEnabled_; } + void EnableEventIntercept() { eventInterceptEnabled_ = true; } + void DisableEventIntercept() { eventInterceptEnabled_ = false; } + + using EventCallback = std::function; + + uint32 AddEventListener(EventType type, EventCallback callback); + bool RemoveEventListener(uint32 listenerId); + void ClearEventListeners(); + + virtual bool OnEvent(const Event& event); + + virtual bool OnKeyDown(const KeyEvent& event); + virtual bool OnKeyUp(const KeyEvent& event); + virtual bool OnMouseDown(const MouseEvent& event); + virtual bool OnMouseUp(const MouseEvent& event); + virtual bool OnMouseMove(const MouseEvent& event); + virtual bool OnMouseWheel(const MouseEvent& event); + virtual bool OnTouchDown(const TouchEvent& event); + virtual bool OnTouchUp(const TouchEvent& event); + virtual bool OnTouchMove(const TouchEvent& event); + virtual bool OnJoystickAxis(const JoystickEvent& event); + virtual bool OnJoystickButtonDown(const JoystickEvent& event); + virtual bool OnJoystickButtonUp(const JoystickEvent& event); + virtual bool OnWindowResize(const WindowEvent& event); + + bool DispatchEvent(const Event& event); + bool BubbleEvent(const Event& event); + + protected: void SetParent(Actor* parent) { parent_ = parent; } void SetScene(Scene* scene) { scene_ = scene; } @@ -99,6 +143,18 @@ private: mutable Transform2D worldTransform_; mutable bool transformDirty_ = true; + bool eventReceiveEnabled_ = false; + int32 eventPriority_ = 0; + bool eventInterceptEnabled_ = false; + + struct EventListener { + uint32 id; + EventType type; + EventCallback callback; + }; + std::vector eventListeners_; + uint32 nextListenerId_ = 1; + void markTransformDirty(); void updateLocalTransform() const; void updateWorldTransform() const; diff --git a/Frostbite2D/include/frostbite2D/core/application.h b/Frostbite2D/include/frostbite2D/core/application.h index b46efbe..f18efbe 100644 --- a/Frostbite2D/include/frostbite2D/core/application.h +++ b/Frostbite2D/include/frostbite2D/core/application.h @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include namespace frostbite2D { @@ -175,6 +177,9 @@ private: */ void render(); + std::unique_ptr convertSDLEvent(const SDL_Event& sdlEvent); + void dispatchEvent(const Event& event); + Window* window_ = nullptr; class Renderer* renderer_ = nullptr; Camera* camera_ = nullptr; diff --git a/Frostbite2D/include/frostbite2D/event/event.h b/Frostbite2D/include/frostbite2D/event/event.h new file mode 100644 index 0000000..4a306fd --- /dev/null +++ b/Frostbite2D/include/frostbite2D/event/event.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +namespace frostbite2D { + +class Actor; + +enum class EventType : int32 { + None = 0, + WindowClose, + WindowResize, + WindowFocus, + WindowMinimize, + WindowMaximize, + WindowRestore, + KeyDown, + KeyUp, + KeyText, + MouseMove, + MouseButtonDown, + MouseButtonUp, + MouseWheel, + TouchDown, + TouchUp, + TouchMove, + JoystickAxis, + JoystickBall, + JoystickHat, + JoystickButtonDown, + JoystickButtonUp, + Custom +}; + +class Event { +public: + virtual ~Event() = default; + + virtual EventType getType() const = 0; + + bool isHandled() const { return handled_; } + void setHandled(bool handled) { handled_ = handled; } + + bool isPropagationStopped() const { return propagationStopped_; } + void stopPropagation() { propagationStopped_ = true; } + + Actor* getSource() const { return source_; } + void setSource(Actor* actor) { source_ = actor; } + + uint32 getTimestamp() const { return timestamp_; } + +protected: + Event() = default; + Event(uint32 timestamp) + : timestamp_(timestamp) {} + + bool handled_ = false; + bool propagationStopped_ = false; + Actor* source_ = nullptr; + uint32 timestamp_ = 0; +}; + +} diff --git a/Frostbite2D/include/frostbite2D/event/joystick_event.h b/Frostbite2D/include/frostbite2D/event/joystick_event.h new file mode 100644 index 0000000..1bc429a --- /dev/null +++ b/Frostbite2D/include/frostbite2D/event/joystick_event.h @@ -0,0 +1,112 @@ +#pragma once + +#include +#include + +namespace frostbite2D { + +class JoystickEvent : public Event { +public: + JoystickEvent(EventType type, uint32 timestamp, int32 deviceId) + : Event(timestamp) + , type_(type) + , deviceId_(deviceId) {} + + EventType getType() const override { + return type_; + } + + int32 getDeviceId() const { return deviceId_; } + +protected: + EventType type_; + int32 deviceId_; +}; + +class JoystickAxisEvent : public JoystickEvent { +public: + JoystickAxisEvent(uint32 timestamp, int32 deviceId, uint8 axis, int16 value) + : JoystickEvent(EventType::JoystickAxis, timestamp, deviceId) + , axis_(axis) + , value_(value) {} + + uint8 getAxis() const { return axis_; } + int16 getValue() const { return value_; } + + float getNormalizedValue() const { + return static_cast(value_) / 32767.0f; + } + +private: + uint8 axis_; + int16 value_; +}; + +class JoystickBallEvent : public JoystickEvent { +public: + JoystickBallEvent(uint32 timestamp, int32 deviceId, uint8 ball, int16 dx, int16 dy) + : JoystickEvent(EventType::JoystickBall, timestamp, deviceId) + , ball_(ball) + , dx_(dx) + , dy_(dy) {} + + uint8 getBall() const { return ball_; } + int16 getDX() const { return dx_; } + int16 getDY() const { return dy_; } + +private: + uint8 ball_; + int16 dx_; + int16 dy_; +}; + +class JoystickHatEvent : public JoystickEvent { +public: + JoystickHatEvent(uint32 timestamp, int32 deviceId, uint8 hat, uint8 value) + : JoystickEvent(EventType::JoystickHat, timestamp, deviceId) + , hat_(hat) + , value_(value) {} + + uint8 getHat() const { return hat_; } + uint8 getValue() const { return value_; } + + bool isCentered() const { return value_ == SDL_HAT_CENTERED; } + bool isUp() const { return (value_ & SDL_HAT_UP) != 0; } + bool isRight() const { return (value_ & SDL_HAT_RIGHT) != 0; } + bool isDown() const { return (value_ & SDL_HAT_DOWN) != 0; } + bool isLeft() const { return (value_ & SDL_HAT_LEFT) != 0; } + +private: + uint8 hat_; + uint8 value_; +}; + +class JoystickButtonEvent : public JoystickEvent { +public: + JoystickButtonEvent(EventType type, uint32 timestamp, int32 deviceId, uint8 button) + : JoystickEvent(type, timestamp, deviceId) + , button_(button) {} + + EventType getType() const override { + return type_; + } + + uint8 getButton() const { return button_; } + +private: + uint8 button_; +}; + +class JoystickButtonDownEvent : public JoystickButtonEvent { +public: + JoystickButtonDownEvent(uint32 timestamp, int32 deviceId, uint8 button) + : JoystickButtonEvent(EventType::JoystickButtonDown, timestamp, deviceId, button) {} +}; + +class JoystickButtonUpEvent : public JoystickButtonEvent { +public: + JoystickButtonUpEvent(uint32 timestamp, int32 deviceId, uint8 button) + : JoystickButtonEvent(EventType::JoystickButtonUp, timestamp, deviceId, button) {} +}; + +} diff --git a/Frostbite2D/include/frostbite2D/event/key_event.h b/Frostbite2D/include/frostbite2D/event/key_event.h new file mode 100644 index 0000000..e7dbfb2 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/event/key_event.h @@ -0,0 +1,208 @@ +#pragma once + +#include +#include +#include + +namespace frostbite2D { + +enum class KeyCode { + Unknown = SDLK_UNKNOWN, + Return = SDLK_RETURN, + Escape = SDLK_ESCAPE, + Backspace = SDLK_BACKSPACE, + Tab = SDLK_TAB, + Space = SDLK_SPACE, + Exclaim = SDLK_EXCLAIM, + Quotedbl = SDLK_QUOTEDBL, + Hash = SDLK_HASH, + Percent = SDLK_PERCENT, + Dollar = SDLK_DOLLAR, + Ampersand = SDLK_AMPERSAND, + Quote = SDLK_QUOTE, + LeftParen = SDLK_LEFTPAREN, + RightParen = SDLK_RIGHTPAREN, + Asterisk = SDLK_ASTERISK, + Plus = SDLK_PLUS, + Comma = SDLK_COMMA, + Minus = SDLK_MINUS, + Period = SDLK_PERIOD, + Slash = SDLK_SLASH, + Num0 = SDLK_0, + Num1 = SDLK_1, + Num2 = SDLK_2, + Num3 = SDLK_3, + Num4 = SDLK_4, + Num5 = SDLK_5, + Num6 = SDLK_6, + Num7 = SDLK_7, + Num8 = SDLK_8, + Num9 = SDLK_9, + Colon = SDLK_COLON, + Semicolon = SDLK_SEMICOLON, + Less = SDLK_LESS, + Equals = SDLK_EQUALS, + Greater = SDLK_GREATER, + Question = SDLK_QUESTION, + At = SDLK_AT, + LeftBracket = SDLK_LEFTBRACKET, + Backslash = SDLK_BACKSLASH, + RightBracket = SDLK_RIGHTBRACKET, + Caret = SDLK_CARET, + Underscore = SDLK_UNDERSCORE, + Backquote = SDLK_BACKQUOTE, + a = SDLK_a, + b = SDLK_b, + c = SDLK_c, + d = SDLK_d, + e = SDLK_e, + f = SDLK_f, + g = SDLK_g, + h = SDLK_h, + i = SDLK_i, + j = SDLK_j, + k = SDLK_k, + l = SDLK_l, + m = SDLK_m, + n = SDLK_n, + o = SDLK_o, + p = SDLK_p, + q = SDLK_q, + r = SDLK_r, + s = SDLK_s, + t = SDLK_t, + u = SDLK_u, + v = SDLK_v, + w = SDLK_w, + x = SDLK_x, + y = SDLK_y, + z = SDLK_z, + CapsLock = SDLK_CAPSLOCK, + F1 = SDLK_F1, + F2 = SDLK_F2, + F3 = SDLK_F3, + F4 = SDLK_F4, + F5 = SDLK_F5, + F6 = SDLK_F6, + F7 = SDLK_F7, + F8 = SDLK_F8, + F9 = SDLK_F9, + F10 = SDLK_F10, + F11 = SDLK_F11, + F12 = SDLK_F12, + PrintScreen = SDLK_PRINTSCREEN, + ScrollLock = SDLK_SCROLLLOCK, + Pause = SDLK_PAUSE, + Insert = SDLK_INSERT, + Home = SDLK_HOME, + PageUp = SDLK_PAGEUP, + Delete = SDLK_DELETE, + End = SDLK_END, + PageDown = SDLK_PAGEDOWN, + Right = SDLK_RIGHT, + Left = SDLK_LEFT, + Down = SDLK_DOWN, + Up = SDLK_UP, + Numpad0 = SDLK_KP_0, + Numpad1 = SDLK_KP_1, + Numpad2 = SDLK_KP_2, + Numpad3 = SDLK_KP_3, + Numpad4 = SDLK_KP_4, + Numpad5 = SDLK_KP_5, + Numpad6 = SDLK_KP_6, + Numpad7 = SDLK_KP_7, + Numpad8 = SDLK_KP_8, + Numpad9 = SDLK_KP_9, + NumpadDecimal = SDLK_KP_DECIMAL, + NumpadDivide = SDLK_KP_DIVIDE, + NumpadMultiply = SDLK_KP_MULTIPLY, + NumpadMinus = SDLK_KP_MINUS, + NumpadPlus = SDLK_KP_PLUS, + NumpadEnter = SDLK_KP_ENTER, + NumpadEquals = SDLK_KP_EQUALS, + LShift = SDLK_LSHIFT, + RShift = SDLK_RSHIFT, + LCtrl = SDLK_LCTRL, + RCtrl = SDLK_RCTRL, + LAlt = SDLK_LALT, + RAlt = SDLK_RALT, + LGui = SDLK_LGUI, + RGui = SDLK_RGUI +}; + +enum class KeyMod { + None = KMOD_NONE, + LShift = KMOD_LSHIFT, + RShift = KMOD_RSHIFT, + Shift = KMOD_SHIFT, + LCtrl = KMOD_LCTRL, + RCtrl = KMOD_RCTRL, + Ctrl = KMOD_CTRL, + LAlt = KMOD_LALT, + RAlt = KMOD_RALT, + Alt = KMOD_ALT, + LGui = KMOD_LGUI, + RGui = KMOD_RGUI, + Gui = KMOD_GUI, + Num = KMOD_NUM, + Caps = KMOD_CAPS +}; + +class KeyEvent : public Event { +public: + KeyEvent(uint32 timestamp, KeyCode keyCode, KeyMod modifiers, bool repeat) + : Event(timestamp) + , keyCode_(keyCode) + , modifiers_(modifiers) + , repeat_(repeat) {} + + EventType getType() const override { + return EventType::KeyDown; + } + + KeyCode getKeyCode() const { return keyCode_; } + KeyMod getModifiers() const { return modifiers_; } + bool isRepeat() const { return repeat_; } + +private: + KeyCode keyCode_; + KeyMod modifiers_; + bool repeat_; +}; + +class KeyUpEvent : public Event { +public: + KeyUpEvent(uint32 timestamp, KeyCode keyCode, KeyMod modifiers) + : Event(timestamp) + , keyCode_(keyCode) + , modifiers_(modifiers) {} + + EventType getType() const override { + return EventType::KeyUp; + } + + KeyCode getKeyCode() const { return keyCode_; } + KeyMod getModifiers() const { return modifiers_; } + +private: + KeyCode keyCode_; + KeyMod modifiers_; +}; + +class KeyTextEvent : public Event { +public: + KeyTextEvent(uint32 timestamp, const std::string& text) + : Event(timestamp) + , text_(text) {} + + EventType getType() const override { + return EventType::KeyText; + } + + const std::string& getText() const { return text_; } + +private: + std::string text_; +}; + +} diff --git a/Frostbite2D/include/frostbite2D/event/mouse_event.h b/Frostbite2D/include/frostbite2D/event/mouse_event.h new file mode 100644 index 0000000..4e76841 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/event/mouse_event.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include + +namespace frostbite2D { + +enum class MouseButton { + Left = SDL_BUTTON_LEFT, + Right = SDL_BUTTON_RIGHT, + Middle = SDL_BUTTON_MIDDLE, + X1 = SDL_BUTTON_X1, + X2 = SDL_BUTTON_X2 +}; + +class MouseEvent : public Event { +public: + MouseEvent(EventType type, uint32 timestamp, const Vec2& position) + : Event(timestamp) + , type_(type) + , position_(position) {} + + virtual ~MouseEvent() = default; + + EventType getType() const override { return type_; } + const Vec2& getPosition() const { return position_; } + int32 getX() const { return static_cast(position_.x); } + int32 getY() const { return static_cast(position_.y); } + +protected: + EventType type_; + Vec2 position_; +}; + +class MouseMoveEvent : public Event { +public: + MouseMoveEvent(uint32 timestamp, const Vec2& position, const Vec2& relative) + : Event(timestamp) + , position_(position) + , relative_(relative) {} + + EventType getType() const override { + return EventType::MouseMove; + } + + const Vec2& getPosition() const { return position_; } + const Vec2& getRelative() const { return relative_; } + + int32 getX() const { return static_cast(position_.x); } + int32 getY() const { return static_cast(position_.y); } + +private: + Vec2 position_; + Vec2 relative_; +}; + +class MouseButtonEvent : public Event { +public: + MouseButtonEvent(uint32 timestamp, MouseButton button, const Vec2& position, bool down) + : Event(timestamp) + , button_(button) + , position_(position) + , down_(down) {} + + EventType getType() const override { + return down_ ? EventType::MouseButtonDown : EventType::MouseButtonUp; + } + + MouseButton getButton() const { return button_; } + const Vec2& getPosition() const { return position_; } + bool isDown() const { return down_; } + + int32 getX() const { return static_cast(position_.x); } + int32 getY() const { return static_cast(position_.y); } + +private: + MouseButton button_; + Vec2 position_; + bool down_; +}; + +class MouseWheelEvent : public Event { +public: + MouseWheelEvent(uint32 timestamp, const Vec2& position, const Vec2& direction, bool flipped) + : Event(timestamp) + , position_(position) + , direction_(direction) + , flipped_(flipped) {} + + EventType getType() const override { + return EventType::MouseWheel; + } + + const Vec2& getPosition() const { return position_; } + const Vec2& getDirection() const { return direction_; } + bool isFlipped() const { return flipped_; } + + int32 getX() const { return static_cast(direction_.x); } + int32 getY() const { return static_cast(direction_.y); } + +private: + Vec2 position_; + Vec2 direction_; + bool flipped_; +}; + +} diff --git a/Frostbite2D/include/frostbite2D/event/touch_event.h b/Frostbite2D/include/frostbite2D/event/touch_event.h new file mode 100644 index 0000000..b3a1ba1 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/event/touch_event.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include + +namespace frostbite2D { + +class TouchEvent : public Event { +public: + TouchEvent(EventType type, uint32 timestamp, int64 touchId, int64 fingerId, + const Vec2& position, const Vec2& delta, float pressure) + : Event(timestamp) + , type_(type) + , touchId_(touchId) + , fingerId_(fingerId) + , position_(position) + , delta_(delta) + , pressure_(pressure) {} + + EventType getType() const override { + return type_; + } + + int64 getTouchId() const { return touchId_; } + int64 getFingerId() const { return fingerId_; } + const Vec2& getPosition() const { return position_; } + const Vec2& getDelta() const { return delta_; } + float getPressure() const { return pressure_; } + +private: + EventType type_; + int64 touchId_; + int64 fingerId_; + Vec2 position_; + Vec2 delta_; + float pressure_; +}; + +class TouchDownEvent : public TouchEvent { +public: + TouchDownEvent(uint32 timestamp, int64 touchId, int64 fingerId, + const Vec2& position, float pressure) + : TouchEvent(EventType::TouchDown, timestamp, touchId, fingerId, + position, Vec2(0, 0), pressure) {} +}; + +class TouchUpEvent : public TouchEvent { +public: + TouchUpEvent(uint32 timestamp, int64 touchId, int64 fingerId, + const Vec2& position, float pressure) + : TouchEvent(EventType::TouchUp, timestamp, touchId, fingerId, + position, Vec2(0, 0), pressure) {} +}; + +class TouchMoveEvent : public TouchEvent { +public: + TouchMoveEvent(uint32 timestamp, int64 touchId, int64 fingerId, + const Vec2& position, const Vec2& delta, float pressure) + : TouchEvent(EventType::TouchMove, timestamp, touchId, fingerId, + position, delta, pressure) {} +}; + +} diff --git a/Frostbite2D/include/frostbite2D/event/window_event.h b/Frostbite2D/include/frostbite2D/event/window_event.h new file mode 100644 index 0000000..c67d139 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/event/window_event.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +namespace frostbite2D { + +class WindowEvent : public Event { +public: + WindowEvent(EventType type, uint32 timestamp, int32 windowId) + : Event(timestamp) + , type_(type) + , windowId_(windowId) {} + + EventType getType() const override { + return type_; + } + + int32 getWindowId() const { return windowId_; } + +protected: + EventType type_; + int32 windowId_; +}; + +class WindowCloseEvent : public WindowEvent { +public: + WindowCloseEvent(uint32 timestamp, int32 windowId) + : WindowEvent(EventType::WindowClose, timestamp, windowId) {} +}; + +class WindowResizeEvent : public WindowEvent { +public: + WindowResizeEvent(uint32 timestamp, int32 windowId, int32 width, int32 height) + : WindowEvent(EventType::WindowResize, timestamp, windowId) + , width_(width) + , height_(height) {} + + int32 getWidth() const { return width_; } + int32 getHeight() const { return height_; } + +private: + int32 width_; + int32 height_; +}; + +class WindowFocusEvent : public WindowEvent { +public: + WindowFocusEvent(uint32 timestamp, int32 windowId, bool focused) + : WindowEvent(EventType::WindowFocus, timestamp, windowId) + , focused_(focused) {} + + bool isFocused() const { return focused_; } + +private: + bool focused_; +}; + +class WindowMinimizeEvent : public WindowEvent { +public: + WindowMinimizeEvent(uint32 timestamp, int32 windowId) + : WindowEvent(EventType::WindowMinimize, timestamp, windowId) {} +}; + +class WindowMaximizeEvent : public WindowEvent { +public: + WindowMaximizeEvent(uint32 timestamp, int32 windowId) + : WindowEvent(EventType::WindowMaximize, timestamp, windowId) {} +}; + +class WindowRestoreEvent : public WindowEvent { +public: + WindowRestoreEvent(uint32 timestamp, int32 windowId) + : WindowEvent(EventType::WindowRestore, timestamp, windowId) {} +}; + +} diff --git a/Frostbite2D/include/frostbite2D/scene/scene.h b/Frostbite2D/include/frostbite2D/scene/scene.h index 6031dbb..d49e94c 100644 --- a/Frostbite2D/include/frostbite2D/scene/scene.h +++ b/Frostbite2D/include/frostbite2D/scene/scene.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace frostbite2D { @@ -15,7 +17,15 @@ public: void Update(float deltaTime) override; void Render() override; + bool OnEvent(const Event& event) override; + + static Scene* GetCurrent(); + private: + bool dispatchToChildren(const Event& event); + + static Scene* current_; + friend class SceneManager; }; diff --git a/Frostbite2D/src/frostbite2D/2d/actor.cpp b/Frostbite2D/src/frostbite2D/2d/actor.cpp index 79298f5..6ca7e37 100644 --- a/Frostbite2D/src/frostbite2D/2d/actor.cpp +++ b/Frostbite2D/src/frostbite2D/2d/actor.cpp @@ -1,4 +1,9 @@ #include +#include +#include +#include +#include +#include #include namespace frostbite2D { @@ -210,5 +215,170 @@ void Actor::RenderChildren() { } } +uint32 Actor::AddEventListener(EventType type, EventCallback callback) { + EventListener listener; + listener.id = nextListenerId_++; + listener.type = type; + listener.callback = std::move(callback); + eventListeners_.push_back(listener); + return listener.id; +} + +bool Actor::RemoveEventListener(uint32 listenerId) { + for (auto it = eventListeners_.begin(); it != eventListeners_.end(); ++it) { + if (it->id == listenerId) { + eventListeners_.erase(it); + return true; + } + } + return false; +} + +void Actor::ClearEventListeners() { + eventListeners_.clear(); +} + +bool Actor::OnEvent(const Event& event) { + if (!eventReceiveEnabled_) { + return false; + } + + for (const auto& listener : eventListeners_) { + if (listener.type == event.getType()) { + if (listener.callback(event)) { + return true; + } + } + } + + switch (event.getType()) { + case EventType::KeyDown: + return OnKeyDown(static_cast(event)); + case EventType::KeyUp: + return OnKeyUp(static_cast(event)); + case EventType::MouseButtonDown: + return OnMouseDown(static_cast(event)); + case EventType::MouseButtonUp: + return OnMouseUp(static_cast(event)); + case EventType::MouseMove: + return OnMouseMove(static_cast(event)); + case EventType::MouseWheel: + return OnMouseWheel(static_cast(event)); + case EventType::TouchDown: + return OnTouchDown(static_cast(event)); + case EventType::TouchUp: + return OnTouchUp(static_cast(event)); + case EventType::TouchMove: + return OnTouchMove(static_cast(event)); + case EventType::JoystickAxis: + return OnJoystickAxis(static_cast(event)); + case EventType::JoystickButtonDown: + return OnJoystickButtonDown(static_cast(event)); + case EventType::JoystickButtonUp: + return OnJoystickButtonUp(static_cast(event)); + case EventType::WindowResize: + return OnWindowResize(static_cast(event)); + default: + return false; + } +} + +bool Actor::OnKeyDown(const KeyEvent& event) { + (void)event; + return false; +} + +bool Actor::OnKeyUp(const KeyEvent& event) { + (void)event; + return false; +} + +bool Actor::OnMouseDown(const MouseEvent& event) { + (void)event; + return false; +} + +bool Actor::OnMouseUp(const MouseEvent& event) { + (void)event; + return false; +} + +bool Actor::OnMouseMove(const MouseEvent& event) { + (void)event; + return false; +} + +bool Actor::OnMouseWheel(const MouseEvent& event) { + (void)event; + return false; +} + +bool Actor::OnTouchDown(const TouchEvent& event) { + (void)event; + return false; +} + +bool Actor::OnTouchUp(const TouchEvent& event) { + (void)event; + return false; +} + +bool Actor::OnTouchMove(const TouchEvent& event) { + (void)event; + return false; +} + +bool Actor::OnJoystickAxis(const JoystickEvent& event) { + (void)event; + return false; +} + +bool Actor::OnJoystickButtonDown(const JoystickEvent& event) { + (void)event; + return false; +} + +bool Actor::OnJoystickButtonUp(const JoystickEvent& event) { + (void)event; + return false; +} + +bool Actor::OnWindowResize(const WindowEvent& event) { + (void)event; + return false; +} + +bool Actor::DispatchEvent(const Event& event) { + if (event.isPropagationStopped()) { + return false; + } + + if (OnEvent(event)) { + return true; + } + + for (auto it = children_.begin(); it != children_.end(); ++it) { + if (*it) { + if ((*it)->DispatchEvent(event)) { + return true; + } + } + } + + return false; +} + +bool Actor::BubbleEvent(const Event& event) { + if (!parent_) { + return false; + } + + if (parent_->OnEvent(event)) { + return true; + } + + return parent_->BubbleEvent(event); +} + } diff --git a/Frostbite2D/src/frostbite2D/core/application.cpp b/Frostbite2D/src/frostbite2D/core/application.cpp index 09442cf..cdbce4b 100644 --- a/Frostbite2D/src/frostbite2D/core/application.cpp +++ b/Frostbite2D/src/frostbite2D/core/application.cpp @@ -1,8 +1,16 @@ #include +#include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -10,6 +18,7 @@ #include #include #include +#include #include #include @@ -116,11 +125,18 @@ bool Application::initCoreModules() { asset.setWorkingDirectory("/switch/Frostbite2D/" + config_.appName); SDL_Log("Asset working directory: %s", asset.getWorkingDirectory().c_str()); #endif - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER) != 0) { SDL_Log("Failed to initialize SDL: %s", SDL_GetError()); return false; } + // 打开第一个手柄(如果存在) + if (SDL_GameController *controller = SDL_GameControllerOpen(0)) { + SDL_Log("GameController opened: %s", SDL_GameControllerName(controller)); + } else { + SDL_Log("No GameController found"); + } + // 使用SDL2创建窗口 this->window_ = new Window(); @@ -201,29 +217,19 @@ void Application::resume() { } void Application::mainLoop() { - // 记录程序启动时的初始时间(毫秒) - Uint32 start_time = SDL_GetTicks(); - // 定时退出时间:1秒 = 1000毫秒 - const Uint32 EXIT_DELAY = 1000; - while (!shouldQuit_) { - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { + SDL_Event sdlEvent; + while (SDL_PollEvent(&sdlEvent)) { + if (sdlEvent.type == SDL_QUIT) { shouldQuit_ = true; break; } - } - // ===================== 新增:1秒定时退出 ===================== - Uint32 current_time = SDL_GetTicks(); - // 判断是否达到1秒 - if (current_time - start_time >= EXIT_DELAY) { - shouldQuit_ = true; - // 跳出事件循环 - break; + auto event = convertSDLEvent(sdlEvent); + if (event) { + dispatchEvent(*event); + } } - // ============================================================ if (!paused_) { update(); @@ -233,6 +239,129 @@ void Application::mainLoop() { } } +std::unique_ptr Application::convertSDLEvent(const SDL_Event& sdlEvent) { + switch (sdlEvent.type) { + case SDL_WINDOWEVENT: { + switch (sdlEvent.window.event) { + case SDL_WINDOWEVENT_CLOSE: + return std::make_unique(sdlEvent.window.timestamp, sdlEvent.window.windowID); + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + return std::make_unique(sdlEvent.window.timestamp, sdlEvent.window.windowID, + sdlEvent.window.data1, sdlEvent.window.data2); + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_FOCUS_LOST: + return std::make_unique(sdlEvent.window.timestamp, sdlEvent.window.windowID, + sdlEvent.window.event == SDL_WINDOWEVENT_FOCUS_GAINED); + case SDL_WINDOWEVENT_MINIMIZED: + return std::make_unique(sdlEvent.window.timestamp, sdlEvent.window.windowID); + case SDL_WINDOWEVENT_MAXIMIZED: + return std::make_unique(sdlEvent.window.timestamp, sdlEvent.window.windowID); + case SDL_WINDOWEVENT_RESTORED: + return std::make_unique(sdlEvent.window.timestamp, sdlEvent.window.windowID); + } + break; + } + + case SDL_KEYDOWN: { + return std::make_unique(sdlEvent.key.timestamp, + static_cast(sdlEvent.key.keysym.sym), + static_cast(sdlEvent.key.keysym.mod), + sdlEvent.key.repeat != 0); + } + + case SDL_KEYUP: { + return std::make_unique(sdlEvent.key.timestamp, + static_cast(sdlEvent.key.keysym.sym), + static_cast(sdlEvent.key.keysym.mod)); + } + + case SDL_TEXTINPUT: { + return std::make_unique(sdlEvent.text.timestamp, sdlEvent.text.text); + } + + case SDL_MOUSEMOTION: { + return std::make_unique(sdlEvent.motion.timestamp, + Vec2(static_cast(sdlEvent.motion.x), static_cast(sdlEvent.motion.y)), + Vec2(static_cast(sdlEvent.motion.xrel), static_cast(sdlEvent.motion.yrel))); + } + + case SDL_MOUSEBUTTONDOWN: { + return std::make_unique(sdlEvent.button.timestamp, + static_cast(sdlEvent.button.button), + Vec2(static_cast(sdlEvent.button.x), static_cast(sdlEvent.button.y)), + true); + } + + case SDL_MOUSEBUTTONUP: { + return std::make_unique(sdlEvent.button.timestamp, + static_cast(sdlEvent.button.button), + Vec2(static_cast(sdlEvent.button.x), static_cast(sdlEvent.button.y)), + false); + } + + case SDL_MOUSEWHEEL: { + return std::make_unique(sdlEvent.wheel.timestamp, + Vec2(static_cast(sdlEvent.wheel.mouseX), static_cast(sdlEvent.wheel.mouseY)), + Vec2(static_cast(sdlEvent.wheel.x), static_cast(sdlEvent.wheel.y)), + sdlEvent.wheel.direction == SDL_MOUSEWHEEL_FLIPPED); + } + + case SDL_FINGERDOWN: { + return std::make_unique(sdlEvent.tfinger.timestamp, + sdlEvent.tfinger.touchId, + sdlEvent.tfinger.fingerId, + Vec2(static_cast(sdlEvent.tfinger.x), static_cast(sdlEvent.tfinger.y)), + sdlEvent.tfinger.pressure); + } + + case SDL_FINGERUP: { + return std::make_unique(sdlEvent.tfinger.timestamp, + sdlEvent.tfinger.touchId, + sdlEvent.tfinger.fingerId, + Vec2(static_cast(sdlEvent.tfinger.x), static_cast(sdlEvent.tfinger.y)), + sdlEvent.tfinger.pressure); + } + + case SDL_FINGERMOTION: { + return std::make_unique(sdlEvent.tfinger.timestamp, + sdlEvent.tfinger.touchId, + sdlEvent.tfinger.fingerId, + Vec2(static_cast(sdlEvent.tfinger.x), static_cast(sdlEvent.tfinger.y)), + Vec2(static_cast(sdlEvent.tfinger.dx), static_cast(sdlEvent.tfinger.dy)), + sdlEvent.tfinger.pressure); + } + + case SDL_CONTROLLERBUTTONDOWN: { + return std::make_unique(sdlEvent.cbutton.timestamp, + sdlEvent.cbutton.which, + sdlEvent.cbutton.button); + } + + case SDL_CONTROLLERBUTTONUP: { + return std::make_unique(sdlEvent.cbutton.timestamp, + sdlEvent.cbutton.which, + sdlEvent.cbutton.button); + } + + case SDL_CONTROLLERAXISMOTION: { + return std::make_unique(sdlEvent.caxis.timestamp, + sdlEvent.caxis.which, + sdlEvent.caxis.axis, + sdlEvent.caxis.value); + } + } + + return nullptr; +} + +void Application::dispatchEvent(const Event& event) { + Scene* currentScene = SceneManager::get().GetCurrentScene(); + if (currentScene) { + currentScene->OnEvent(event); + } +} + void Application::update() { double currentTime = SDL_GetPerformanceCounter() / static_cast(SDL_GetPerformanceFrequency()); deltaTime_ = static_cast(currentTime - lastFrameTime_); diff --git a/Frostbite2D/src/frostbite2D/scene/scene.cpp b/Frostbite2D/src/frostbite2D/scene/scene.cpp index 898af48..66eea21 100644 --- a/Frostbite2D/src/frostbite2D/scene/scene.cpp +++ b/Frostbite2D/src/frostbite2D/scene/scene.cpp @@ -1,9 +1,13 @@ #include +#include #include namespace frostbite2D { +Scene* Scene::current_ = nullptr; + Scene::Scene() { + current_ = this; } Scene::~Scene() { @@ -25,5 +29,54 @@ void Scene::Render() { RenderChildren(); } +Scene* Scene::GetCurrent() { + return current_; +} + +bool Scene::OnEvent(const Event& event) { + if (Actor::OnEvent(event)) { + return true; + } + + if (dispatchToChildren(event)) { + return true; + } + + return false; +} + +bool Scene::dispatchToChildren(const Event& event) { + if (event.isPropagationStopped()) { + return false; + } + + std::vector sortedChildren; + for (auto it = children_.begin(); it != children_.end(); ++it) { + Actor* child = it->Get(); + if (child && child->IsEventReceiveEnabled()) { + sortedChildren.push_back(child); + } + } + + std::sort(sortedChildren.begin(), sortedChildren.end(), + [](Actor* a, Actor* b) { + return a->GetEventPriority() < b->GetEventPriority(); + }); + + for (Actor* child : sortedChildren) { + if (child->IsEventInterceptEnabled()) { + if (child->OnEvent(event)) { + return true; + } + } else { + if (child->DispatchEvent(event)) { + return true; + } + } + } + + return false; +} + } diff --git a/Game/src/main.cpp b/Game/src/main.cpp index 592befa..6fde6f9 100644 --- a/Game/src/main.cpp +++ b/Game/src/main.cpp @@ -3,10 +3,12 @@ #include #include #include +#include #include #include #include + #include #include #include @@ -41,8 +43,47 @@ int main(int argc, char **argv) { // SDL_Log("Starting main loop..."); - // auto menuScene = MakePtr(); - // SceneManager::get().PushScene(menuScene); + auto menuScene = MakePtr(); + SceneManager::get().PushScene(menuScene); + + auto TestActor = MakePtr(); + + menuScene->AddChild(TestActor); + + TestActor->EnableEventReceive(); + + // 监听手柄按键按下 + TestActor->AddEventListener( + EventType::JoystickButtonDown, [](const Event &event) { + const auto &ke = static_cast(event); + SDL_Log("Joystick Button Down: device=%d, button=%d", ke.getDeviceId(), + ke.getButton()); + + if(ke.getButton() == 6) { + Application::get().quit(); + } + return true; + }); + + // 监听手柄按键抬起 + TestActor->AddEventListener( + EventType::JoystickButtonUp, [](const Event &event) { + const auto &ke = static_cast(event); + SDL_Log("Joystick Button Up: device=%d, button=%d", ke.getDeviceId(), + ke.getButton()); + + return true; + }); + + // 监听手柄轴 + TestActor->AddEventListener(EventType::JoystickAxis, [](const Event &event) { + const auto &ae = static_cast(event); + if (std::abs(ae.getNormalizedValue()) > 0.1f) { + SDL_Log("Joystick Axis: device=%d, axis=%d, value=%.2f", ae.getDeviceId(), + ae.getAxis(), ae.getNormalizedValue()); + } + return true; + }); // // 尝试加载精灵 // auto sprite = Sprite::createFromFile("assets/player.png");