diff options
26 files changed, 1224 insertions, 2 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index af4ae4e..936b057 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,67 @@ { "cmake.sourceDirectory": "${workspaceFolder}/src", "files.associations": { - "*.tcc": "cpp" + "functional": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "list": "cpp", + "map": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "format": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "semaphore": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp" } } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..05054c5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: g++ build active file", + "command": "/usr/bin/g++", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +}
\ No newline at end of file diff --git a/mwe/.vscode/settings.json b/mwe/.vscode/settings.json new file mode 100644 index 0000000..c7913ab --- /dev/null +++ b/mwe/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.associations": { + "variant": "cpp", + "*.tcc": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "string_view": "cpp", + "ostream": "cpp", + "iostream": "cpp" + } +} diff --git a/mwe/events/.vscode/c_cpp_properties.json b/mwe/events/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..64d3f90 --- /dev/null +++ b/mwe/events/.vscode/c_cpp_properties.json @@ -0,0 +1,15 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "linux-clang-x64" + } + ], + "version": 4 +}
\ No newline at end of file diff --git a/mwe/events/.vscode/settings.json b/mwe/events/.vscode/settings.json new file mode 100644 index 0000000..816d790 --- /dev/null +++ b/mwe/events/.vscode/settings.json @@ -0,0 +1,68 @@ +{ + "files.associations": { + "iostream": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "list": "cpp", + "map": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp", + "condition_variable": "cpp", + "ctime": "cpp", + "forward_list": "cpp", + "executor": "cpp", + "io_context": "cpp", + "netfwd": "cpp", + "ratio": "cpp", + "timer": "cpp", + "future": "cpp", + "mutex": "cpp", + "semaphore": "cpp", + "stop_token": "cpp", + "thread": "cpp" + } +} diff --git a/mwe/events/CMakeLists.txt b/mwe/events/CMakeLists.txt new file mode 100644 index 0000000..585d869 --- /dev/null +++ b/mwe/events/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.5) +project(gameloop) + +# Set the C++ standard (optional, but good practice) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) +set(CMAKE_BUILD_TYPE Debug) + +# Find the SDL2 package +find_package(SDL2 REQUIRED) + +add_executable(gameloop + src/window.cpp + src/main.cpp + #src/eventHandler.cpp + src/eventManager.cpp + src/event.cpp + src/loopManager.cpp + src/timer.cpp + src/keyCodes.cpp + src/eventHandler.cpp +) + +# Link the SDL2 library to your project +target_link_libraries(gameloop ${SDL2_LIBRARIES}) + +# Include SDL2 header files and project headers +target_include_directories(gameloop PRIVATE ${SDL2_INCLUDE_DIRS}) +target_include_directories(gameloop PRIVATE ${CMAKE_SOURCE_DIR}/include) + diff --git a/mwe/events/imgs/demo.bmp b/mwe/events/imgs/demo.bmp Binary files differnew file mode 100644 index 0000000..65354fe --- /dev/null +++ b/mwe/events/imgs/demo.bmp diff --git a/mwe/events/imgs/demo.jpg b/mwe/events/imgs/demo.jpg Binary files differnew file mode 100644 index 0000000..f534e1b --- /dev/null +++ b/mwe/events/imgs/demo.jpg diff --git a/mwe/events/include/event.h b/mwe/events/include/event.h new file mode 100644 index 0000000..802140c --- /dev/null +++ b/mwe/events/include/event.h @@ -0,0 +1,87 @@ +#pragma once +#include <cstdint> +#include <iostream> +#include <string> +#include <unordered_map> +#include <variant> +#include "keyCodes.h" + +class UUIDGenerator { +public: + static std::uint32_t getUniqueID() { + static std::uint32_t id = 0; + return ++id; + } +}; +#define REGISTER_EVENT_TYPE(ClassName) \ +public: \ + static std::uint32_t getStaticEventType() { \ + static std::uint32_t typeID = UUIDGenerator::getUniqueID(); \ + return typeID; \ + } \ + virtual std::uint32_t getEventType() const override { \ + return getStaticEventType(); \ + } +class Event { +public: + Event(std::string eventType); + virtual ~Event() = default; + virtual std::uint32_t getEventType() const = 0; + virtual std::string toString() const; + void addArgument(const std::string& key, const std::variant<int, std::string, float>& value); + + std::variant<int, std::string, float> getArgument(const std::string& key) const; + + std::string getType() const; + bool getHandled() const; + void markHandled(); + +private: + std::unordered_map<std::string, std::variant<int, std::string, float>> eventData; + bool isHandled = false; +}; + +// KeyPressedEvent class +class KeyPressedEvent : public Event { +public: + KeyPressedEvent(int keyCode); + + REGISTER_EVENT_TYPE("KeyPressedEvent"); + + Keycode getKeyCode() const; + int getRepeatCount() const; + +private: + Keycode keycode; + +public: + Keycode key = 0; + int repeatCount = 0; +}; + +// KeyReleasedEvent class +class KeyReleasedEvent : public Event { +public: + KeyReleasedEvent(int keyCode); + + REGISTER_EVENT_TYPE(KeyReleasedEvent); + + Keycode getKeyCode() const; + +private: + Keycode key = 0; +}; + +// MousePressedEvent class +class MousePressedEvent : public Event { +public: + MousePressedEvent(int mouseX, int mouseY); + + REGISTER_EVENT_TYPE(MousePressedEvent) + + std::pair<int, int> getMousePosition() const; + +private: + int mouseX = 0; + int mouseY = 0; +}; diff --git a/mwe/events/include/eventHandler.h b/mwe/events/include/eventHandler.h new file mode 100644 index 0000000..e5b99d6 --- /dev/null +++ b/mwe/events/include/eventHandler.h @@ -0,0 +1,48 @@ +#pragma once + +#include "event.h" + +#include <functional> +#include <iostream> +template<typename EventType> +using EventHandler = std::function<void(const EventType& e)>; + +class IEventHandlerWrapper { +public: + virtual ~IEventHandlerWrapper() = default; + + void exec(const Event& e); + + virtual std::string getType() const = 0; + virtual bool isDestroyOnSuccess() const = 0; + +private: + virtual void call(const Event& e) = 0; +}; + +template<typename EventType> +class EventHandlerWrapper : public IEventHandlerWrapper { +public: + explicit EventHandlerWrapper(const EventHandler<EventType>& handler, const bool destroyOnSuccess = false) + : m_handler(handler) + , m_handlerType(m_handler.target_type().name()) + , m_destroyOnSuccess(destroyOnSuccess) + { + // std::cout << m_handlerType << std::endl; + } + +private: + void call(const Event& e) override + { + if (e.getEventType() == EventType::getStaticEventType()) { + m_handler(static_cast<const EventType&>(e)); + } + } + + std::string getType() const override { return m_handlerType; } + bool isDestroyOnSuccess() const { return m_destroyOnSuccess; } + + EventHandler<EventType> m_handler; + const std::string m_handlerType; + bool m_destroyOnSuccess { false }; +}; diff --git a/mwe/events/include/eventManager.h b/mwe/events/include/eventManager.h new file mode 100644 index 0000000..709796a --- /dev/null +++ b/mwe/events/include/eventManager.h @@ -0,0 +1,57 @@ +#pragma once +#include <unordered_map> +#include <memory> +#include "event.h" +#include "keyCodes.h" +#include "eventHandler.h" +#include <vector> +// using EventType = std::uint32_t; +// using EventId = std::uint64_t; + +class EventManager { +public: + EventManager(const EventManager&) = delete; + const EventManager& operator=(const EventManager&) = delete; + static EventManager& getInstance() { + static EventManager instance; + return instance; + } + + void shutdown(); + void subscribe(int eventType, std::unique_ptr<IEventHandlerWrapper>&& handler, int eventId); + void unsubscribe(int eventType, const std::string& handlerName, int eventId); + void triggerEvent(const Event& event_, int eventId); + void queueEvent(std::unique_ptr<Event>&& event_, int eventId); + void dispatchEvents(); + +private: + EventManager() = default; + std::vector<std::pair<std::unique_ptr<Event>, int>> m_eventsQueue; + std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>> m_subscribers; + std::unordered_map<int, std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>> m_subscribersByEventId; +}; + + +template<typename EventType> +inline void subscribe(const EventHandler<EventType>& callback, int eventId = 0, const bool unsubscribeOnSuccess = false) +{ + std::unique_ptr<IEventHandlerWrapper> handler = std::make_unique<EventHandlerWrapper<EventType>>(callback, unsubscribeOnSuccess); + EventManager::getInstance().subscribe(EventType::getStaticEventType(), std::move(handler), eventId); +} + +template<typename EventType> +inline void unsubscribe(const EventHandler<EventType>& callback, int eventId = 0) +{ + const std::string handlerName = callback.target_type().name(); + EventManager::getInstance().unsubscribe(EventType::getStaticEventType(), handlerName, eventId); +} + +inline void triggerEvent(const Event& triggeredEvent, int eventId = 0) +{ + EventManager::getInstance().triggerEvent(triggeredEvent, eventId); +} + +inline void queueEvent(std::unique_ptr<Event>&& queuedEvent, int eventId = 0) +{ + EventManager::getInstance().queueEvent(std::forward<std::unique_ptr<Event>>(queuedEvent), eventId); +} diff --git a/mwe/events/include/keyCodes.h b/mwe/events/include/keyCodes.h new file mode 100644 index 0000000..c3a7826 --- /dev/null +++ b/mwe/events/include/keyCodes.h @@ -0,0 +1,143 @@ +#pragma once +#include <cstdint> +#include <unordered_map> +#include <SDL2/SDL.h> +using Keycode = uint16_t; +enum : Keycode { + // From glfw3.h + Space = 32, + Apostrophe = 39, /* ' */ + Comma = 44, /* , */ + Minus = 45, /* - */ + Period = 46, /* . */ + Slash = 47, /* / */ + + D0 = 48, /* 0 */ + D1 = 49, /* 1 */ + D2 = 50, /* 2 */ + D3 = 51, /* 3 */ + D4 = 52, /* 4 */ + D5 = 53, /* 5 */ + D6 = 54, /* 6 */ + D7 = 55, /* 7 */ + D8 = 56, /* 8 */ + D9 = 57, /* 9 */ + + Semicolon = 59, /* ; */ + Equal = 61, /* = */ + + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + + LeftBracket = 91, /* [ */ + Backslash = 92, /* \ */ + RightBracket = 93, /* ] */ + GraveAccent = 96, /* ` */ + + World1 = 161, /* non-US #1 */ + World2 = 162, /* non-US #2 */ + + /* Function keys */ + Escape = 256, + Enter = 257, + Tab = 258, + Backspace = 259, + Insert = 260, + Delete = 261, + Right = 262, + Left = 263, + Down = 264, + Up = 265, + PageUp = 266, + PageDown = 267, + Home = 268, + End = 269, + CapsLock = 280, + ScrollLock = 281, + NumLock = 282, + PrintScreen = 283, + Pause = 284, + F1 = 290, + F2 = 291, + F3 = 292, + F4 = 293, + F5 = 294, + F6 = 295, + F7 = 296, + F8 = 297, + F9 = 298, + F10 = 299, + F11 = 300, + F12 = 301, + F13 = 302, + F14 = 303, + F15 = 304, + F16 = 305, + F17 = 306, + F18 = 307, + F19 = 308, + F20 = 309, + F21 = 310, + F22 = 311, + F23 = 312, + F24 = 313, + F25 = 314, + + /* Keypad */ + KP0 = 320, + KP1 = 321, + KP2 = 322, + KP3 = 323, + KP4 = 324, + KP5 = 325, + KP6 = 326, + KP7 = 327, + KP8 = 328, + KP9 = 329, + KPDecimal = 330, + KPDivide = 331, + KPMultiply = 332, + KPSubtract = 333, + KPAdd = 334, + KPEnter = 335, + KPEqual = 336, + + LeftShift = 340, + LeftControl = 341, + LeftAlt = 342, + LeftSuper = 343, + RightShift = 344, + RightControl = 345, + RightAlt = 346, + RightSuper = 347, + Menu = 348 +}; +// Define the mapping +extern const std::unordered_map<SDL_Keycode, Keycode> sdlToCustom; + +// Function to map SDL_Keycode to custom Keycode +Keycode getCustomKey(SDL_Keycode sdlKey); diff --git a/mwe/events/include/loopManager.h b/mwe/events/include/loopManager.h new file mode 100644 index 0000000..5c519d9 --- /dev/null +++ b/mwe/events/include/loopManager.h @@ -0,0 +1,28 @@ +#pragma once +#include "window.h" +#include <SDL2/SDL.h> +#include "timer.h" +//#include "combinedEvent.h" +#include "eventManager.h" +#include "loopManager.h" +#include "eventHandler.h" +class LoopManager { +public: + LoopManager(); + void setup(); + void loop(); + void setRunning(bool running); +private: + void processInput(); + void update(); + void lateUpdate(); + void fixedUpdate(); + void render(); + bool gameRunning = false; + WindowManager window; + int timeScale = 1; + float accumulator = 0.0; + double currentTime; + double t = 0.0; + double dt = 0.01; +}; diff --git a/mwe/events/include/timer.h b/mwe/events/include/timer.h new file mode 100644 index 0000000..22383b2 --- /dev/null +++ b/mwe/events/include/timer.h @@ -0,0 +1,31 @@ +#pragma once + +#include <SDL2/SDL.h> + +class LoopTimer { +public: + static LoopTimer & getInstance(); + void start(); + void update(); + double getDeltaTime() const; + int getCurrentTime() const; + void advanceFixedUpdate(); + double getFixedDeltaTime() const; + void setFPS(int FPS); + int getFPS() const; + void enforceFrameRate(); + double getLag() const; + +private: + LoopTimer(); + int FPS = 50; + double gameScale = 1; + double maximumDeltaTime = 0.25; + double deltaTime; + double frameTargetTime = FPS / 1000; + double fixedDeltaTime = 0.01; + double elapsedTime; + double elapsedFixedTime; + double time; + uint64_t lastFrameTime; +}; diff --git a/mwe/events/include/window.h b/mwe/events/include/window.h new file mode 100644 index 0000000..9020b1a --- /dev/null +++ b/mwe/events/include/window.h @@ -0,0 +1,19 @@ +#pragma once +#include <SDL2/SDL.h> +#include <iostream> +#include <vector> +class WindowManager { +public: + WindowManager(); + virtual ~WindowManager(); + bool initWindow(); + void destroyWindow(); + + SDL_Renderer * getRenderer(); + +private: + const int SCREEN_WIDTH = 800; + const int SCREEN_HEIGHT = 600; + SDL_Window * window = NULL; + SDL_Renderer * renderer = NULL; +}; diff --git a/mwe/events/src/event.cpp b/mwe/events/src/event.cpp new file mode 100644 index 0000000..fecae76 --- /dev/null +++ b/mwe/events/src/event.cpp @@ -0,0 +1,56 @@ +#include "event.h" +#include "keyCodes.h" +// Event class methods +Event::Event(std::string eventType) { + eventData["eventType"] = eventType; +} + +void Event::addArgument(const std::string& key, const std::variant<int, std::string, float>& value) { + eventData[key] = value; +} + +std::variant<int, std::string, float> Event::getArgument(const std::string& key) const { + return eventData.at(key); +} + +std::string Event::getType() const { + return std::get<std::string>(eventData.at("eventType")); +} +std::string Event::toString() const { + return std::to_string(getEventType()); +} +bool Event::getHandled() const { + return isHandled; +} + +void Event::markHandled() { + isHandled = true; +} + +// KeyPressedEvent class methods +KeyPressedEvent::KeyPressedEvent(int keycode) + : Event("KeyPressedEvent"), key(keycode), repeatCount(0) {} + +Keycode KeyPressedEvent::getKeyCode() const { + return key; +} + +int KeyPressedEvent::getRepeatCount() const { + return repeatCount; +} + +// KeyReleasedEvent class methods +KeyReleasedEvent::KeyReleasedEvent(int keycode) + : Event("KeyReleasedEvent"), key(keycode) {} + +Keycode KeyReleasedEvent::getKeyCode() const { + return key; +} + +// MousePressedEvent class methods +MousePressedEvent::MousePressedEvent(int mouseX, int mouseY) + : Event("MousePressedEvent"), mouseX(mouseX), mouseY(mouseY) {} + +std::pair<int, int> MousePressedEvent::getMousePosition() const { + return {mouseX, mouseY}; +} diff --git a/mwe/events/src/eventHandler.cpp b/mwe/events/src/eventHandler.cpp new file mode 100644 index 0000000..7040d8d --- /dev/null +++ b/mwe/events/src/eventHandler.cpp @@ -0,0 +1,5 @@ +#include "eventHandler.h" +void IEventHandlerWrapper::exec(const Event& e) + { + call(e); + } diff --git a/mwe/events/src/eventManager.cpp b/mwe/events/src/eventManager.cpp new file mode 100644 index 0000000..ce8e940 --- /dev/null +++ b/mwe/events/src/eventManager.cpp @@ -0,0 +1,109 @@ +#include "eventManager.h" + +void EventManager::shutdown() +{ + m_subscribers.clear(); +} + +void EventManager::subscribe(int eventType, std::unique_ptr<IEventHandlerWrapper>&& handler, int eventId) +{ + if (eventId) { + std::unordered_map<int, std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>>::iterator subscribers = m_subscribersByEventId.find(eventType); + + if (subscribers != m_subscribersByEventId.end()) { + std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>& handlersMap = subscribers->second; + std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator handlers = handlersMap.find(eventId); + if (handlers != handlersMap.end()) { + handlers->second.emplace_back(std::move(handler)); + return; + } + } + m_subscribersByEventId[eventType][eventId].emplace_back(std::move(handler)); + + } else { + std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator subscribers = m_subscribers.find(eventType); + if (subscribers != m_subscribers.end()) { + std::vector<std::unique_ptr<IEventHandlerWrapper>>& handlers = subscribers->second; + for (std::unique_ptr<IEventHandlerWrapper>& it : handlers) { + if (it->getType() == handler->getType()) { + // log for double register + return; + } + } + handlers.emplace_back(std::move(handler)); + } else { + m_subscribers[eventType].emplace_back(std::move(handler)); + } + } +} + +void EventManager::unsubscribe(int eventType, const std::string& handlerName, int eventId) +{ + if (eventId) { + std::unordered_map<int, std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>>::iterator subscribers = m_subscribersByEventId.find(eventType); + if (subscribers != m_subscribersByEventId.end()) { + std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>& handlersMap = subscribers->second; + std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator handlers = handlersMap.find(eventId); + if (handlers != handlersMap.end()) { + std::vector<std::unique_ptr<IEventHandlerWrapper>>& callbacks = handlers->second; + for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::iterator it = callbacks.begin(); it != callbacks.end(); ++it) { + if (it->get()->getType() == handlerName) { + it = callbacks.erase(it); + return; + } + } + } + } + } else { + std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator handlersIt = m_subscribers.find(eventType); + if (handlersIt != m_subscribers.end()) { + std::vector<std::unique_ptr<IEventHandlerWrapper>>& handlers = handlersIt->second; + for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::iterator it = handlers.begin(); it != handlers.end(); ++it) { + if (it->get()->getType() == handlerName) { + it = handlers.erase(it); + return; + } + } + } + } +} + +void EventManager::triggerEvent(const Event& event_, int eventId) +{ + std::vector<std::unique_ptr<IEventHandlerWrapper>>& handlers = m_subscribers[event_.getEventType()]; + for (std::unique_ptr<IEventHandlerWrapper>& handler : handlers) { + handler->exec(event_); + } + + std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>& handlersMap = m_subscribersByEventId[event_.getEventType()]; + std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator handlersIt = handlersMap.find(eventId); + if (handlersIt != handlersMap.end()) { + std::vector<std::unique_ptr<IEventHandlerWrapper>>& callbacks = handlersIt->second; + for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::iterator it = callbacks.begin(); it != callbacks.end();) { + std::unique_ptr<IEventHandlerWrapper>& handler = *it; + handler->exec(event_); + if (handler->isDestroyOnSuccess()) { + it = callbacks.erase(it); + } else { + ++it; + } + } + } +} + +void EventManager::queueEvent(std::unique_ptr<Event>&& event_, int eventId) +{ + m_eventsQueue.emplace_back(std::move(event_), eventId); +} + +void EventManager::dispatchEvents() +{ + for (std::vector<std::pair<std::unique_ptr<Event>, int>>::iterator eventIt = m_eventsQueue.begin(); eventIt != m_eventsQueue.end();) { + if (!eventIt->first.get()->getHandled()) { + triggerEvent(*eventIt->first.get(), eventIt->second); + eventIt = m_eventsQueue.erase(eventIt); + } else { + ++eventIt; + } + } +} diff --git a/mwe/events/src/keyCodes.cpp b/mwe/events/src/keyCodes.cpp new file mode 100644 index 0000000..6f35d11 --- /dev/null +++ b/mwe/events/src/keyCodes.cpp @@ -0,0 +1,140 @@ +#include "keyCodes.h" + +const std::unordered_map<SDL_Keycode, Keycode> sdlToCustom = { + { SDLK_SPACE, Space }, + { SDLK_QUOTE, Apostrophe }, + { SDLK_COMMA, Comma }, + { SDLK_MINUS, Minus }, + { SDLK_PERIOD, Period }, + { SDLK_SLASH, Slash }, + + { SDLK_0, D0 }, + { SDLK_1, D1 }, + { SDLK_2, D2 }, + { SDLK_3, D3 }, + { SDLK_4, D4 }, + { SDLK_5, D5 }, + { SDLK_6, D6 }, + { SDLK_7, D7 }, + { SDLK_8, D8 }, + { SDLK_9, D9 }, + + { SDLK_SEMICOLON, Semicolon }, + { SDLK_EQUALS, Equal }, + + { SDLK_a, A }, + { SDLK_b, B }, + { SDLK_c, C }, + { SDLK_d, D }, + { SDLK_e, E }, + { SDLK_f, F }, + { SDLK_g, G }, + { SDLK_h, H }, + { SDLK_i, I }, + { SDLK_j, J }, + { SDLK_k, K }, + { SDLK_l, L }, + { SDLK_m, M }, + { SDLK_n, N }, + { SDLK_o, O }, + { SDLK_p, P }, + { SDLK_q, Q }, + { SDLK_r, R }, + { SDLK_s, S }, + { SDLK_t, T }, + { SDLK_u, U }, + { SDLK_v, V }, + { SDLK_w, W }, + { SDLK_x, X }, + { SDLK_y, Y }, + { SDLK_z, Z }, + + { SDLK_LEFTBRACKET, LeftBracket }, + { SDLK_BACKSLASH, Backslash }, + { SDLK_RIGHTBRACKET, RightBracket }, + { SDLK_BACKQUOTE, GraveAccent }, + + { SDLK_ESCAPE, Escape }, + { SDLK_RETURN, Enter }, + { SDLK_TAB, Tab }, + { SDLK_BACKSPACE, Backspace }, + { SDLK_INSERT, Insert }, + { SDLK_DELETE, Delete }, + { SDLK_RIGHT, Right }, + { SDLK_LEFT, Left }, + { SDLK_DOWN, Down }, + { SDLK_UP, Up }, + { SDLK_PAGEUP, PageUp }, + { SDLK_PAGEDOWN, PageDown }, + { SDLK_HOME, Home }, + { SDLK_END, End }, + + { SDLK_CAPSLOCK, CapsLock }, + { SDLK_SCROLLLOCK, ScrollLock }, + { SDLK_NUMLOCKCLEAR, NumLock }, + { SDLK_PRINTSCREEN, PrintScreen }, + { SDLK_PAUSE, Pause }, + + { SDLK_F1, F1 }, + { SDLK_F2, F2 }, + { SDLK_F3, F3 }, + { SDLK_F4, F4 }, + { SDLK_F5, F5 }, + { SDLK_F6, F6 }, + { SDLK_F7, F7 }, + { SDLK_F8, F8 }, + { SDLK_F9, F9 }, + { SDLK_F10, F10 }, + { SDLK_F11, F11 }, + { SDLK_F12, F12 }, + { SDLK_F13, F13 }, + { SDLK_F14, F14 }, + { SDLK_F15, F15 }, + { SDLK_F16, F16 }, + { SDLK_F17, F17 }, + { SDLK_F18, F18 }, + { SDLK_F19, F19 }, + { SDLK_F20, F20 }, + { SDLK_F21, F21 }, + { SDLK_F22, F22 }, + { SDLK_F23, F23 }, + { SDLK_F24, F24 }, + + { SDLK_KP_0, KP0 }, + { SDLK_KP_1, KP1 }, + { SDLK_KP_2, KP2 }, + { SDLK_KP_3, KP3 }, + { SDLK_KP_4, KP4 }, + { SDLK_KP_5, KP5 }, + { SDLK_KP_6, KP6 }, + { SDLK_KP_7, KP7 }, + { SDLK_KP_8, KP8 }, + { SDLK_KP_9, KP9 }, + + { SDLK_KP_DECIMAL, KPDecimal }, + { SDLK_KP_DIVIDE, KPDivide }, + { SDLK_KP_MULTIPLY, KPMultiply }, + { SDLK_KP_MINUS, KPSubtract }, + { SDLK_KP_PLUS, KPAdd }, + { SDLK_KP_ENTER, KPEnter }, + { SDLK_KP_EQUALS, KPEqual }, + + { SDLK_LSHIFT, LeftShift }, + { SDLK_LCTRL, LeftControl }, + { SDLK_LALT, LeftAlt }, + { SDLK_LGUI, LeftSuper }, + + { SDLK_RSHIFT, RightShift }, + { SDLK_RCTRL, RightControl }, + { SDLK_RALT, RightAlt }, + { SDLK_RGUI, RightSuper }, + + { SDLK_MENU, Menu } +}; +Keycode getCustomKey(SDL_Keycode sdlKey) { + auto it = sdlToCustom.find(sdlKey); + if (it != sdlToCustom.end()) { + return it->second; + } + return 0; +} diff --git a/mwe/events/src/loopManager.cpp b/mwe/events/src/loopManager.cpp new file mode 100644 index 0000000..6defda0 --- /dev/null +++ b/mwe/events/src/loopManager.cpp @@ -0,0 +1,90 @@ +#include "loopManager.h" + +LoopManager::LoopManager() {} +void LoopManager::processInput() { + SDL_Event event; + SDL_PollEvent(&event); + switch (event.type) { + case SDL_QUIT: + gameRunning = false; + break; + case SDL_KEYDOWN: + triggerEvent(KeyPressedEvent(getCustomKey(event.key.keysym.sym))); + break; + case SDL_MOUSEBUTTONDOWN: + int x, y; + SDL_GetMouseState(&x, &y); + triggerEvent(MousePressedEvent(x, y)); + break; + } +} +void LoopManager::setRunning(bool running){ + this->gameRunning = running; +} +void LoopManager::fixedUpdate() { + //fprintf(stderr, "fixed update\n"); +} +void LoopManager::loop() { + LoopTimer & timer = LoopTimer::getInstance(); + timer.start(); + + while (gameRunning) { + timer.update(); + + while (timer.getLag() >= timer.getFixedDeltaTime()) { + processInput(); + fixedUpdate(); + timer.advanceFixedUpdate(); + } + + update(); + render(); + + timer.enforceFrameRate(); + } + + window.destroyWindow(); +} +void onKey(const KeyPressedEvent& e) +{ + int keyCode = e.getKeyCode(); + std::cout << "keycode pressed: " << keyCode << std::endl; + +} +void onMouse(const MousePressedEvent& e){ + fprintf(stderr, "mouse Position X: %d Y: %d\n", e.getMousePosition().first, e.getMousePosition().second); +} +void LoopManager::setup() { + gameRunning = window.initWindow(); + LoopTimer::getInstance().start(); + LoopTimer::getInstance().setFPS(50); + EventHandler<KeyPressedEvent> callback = onKey; + subscribe<KeyPressedEvent>(callback,false); + EventHandler<MousePressedEvent> mouseCallback = onMouse; + subscribe<MousePressedEvent>(mouseCallback ,false); + EventHandler<KeyPressedEvent> closeWindowCallback = [this](const KeyPressedEvent& e) { + if(e.getKeyCode() == Escape){ + this->setRunning(false); + } + }; + subscribe<KeyPressedEvent>(closeWindowCallback,false); + +} +void LoopManager::render() { + //fprintf(stderr, "**********render********** \n"); + if (gameRunning) { + //window.render(objectList); + } +} + +void LoopManager::update() { + //fprintf(stderr, "**********normal update********** \n"); + LoopTimer & timer = LoopTimer::getInstance(); + + float delta = timer.getDeltaTime(); + + // for (int i = 0; i < objectList.size(); i++) { + // objectList[i]->setX(objectList[i]->getX() + 50 * delta); + // objectList[i]->setY(objectList[i]->getY() + 50 * delta); + // } +} diff --git a/mwe/events/src/main.cpp b/mwe/events/src/main.cpp new file mode 100644 index 0000000..3940c60 --- /dev/null +++ b/mwe/events/src/main.cpp @@ -0,0 +1,50 @@ +#include <SDL2/SDL.h> +#include <iostream> +#include <memory> +#include "loopManager.h" +#include "event.h" + +class PlayerDamagedEvent : public Event { +public: + PlayerDamagedEvent(int damage, int playerID) + : Event("PlayerDamaged"), damage(damage), playerID(playerID) {} + + REGISTER_EVENT_TYPE(PlayerDamagedEvent); + + int getDamage() const { return damage; } + int getPlayerID() const { return playerID; } + +private: + int damage; + int playerID; +}; +void onPlayerDamaged(const PlayerDamagedEvent& e) { + std::cout << "Player " << e.getPlayerID() << " took " << e.getDamage() << " damage." << std::endl; +} + +void onKeyPressed(const KeyPressedEvent& e) +{ + const int keyCode = e.getKeyCode(); + fprintf(stderr,"KeyCode %d\n",keyCode); +} +int main(int argc, char* args[]) { + LoopManager gameLoop; + // Create an event handler for KeyPressedEvent + // EventHandler<KeyPressedEvent> callback = [](const KeyPressedEvent& e) { + // onKeyPressed(e); + // }; + // custom event class poc + EventHandler<PlayerDamagedEvent> playerDamagedHandler = onPlayerDamaged; + subscribe<PlayerDamagedEvent>(playerDamagedHandler); + + triggerEvent(PlayerDamagedEvent(50, 1)); + //EventHandler<KeyPressedEvent> callback = onKeyPressed; + //subscribe<KeyPressedEvent>(callback,false); + std::unique_ptr<Event> anotherKeyPressEvent = std::make_unique<KeyPressedEvent>(65); + queueEvent(std::move(anotherKeyPressEvent)); + triggerEvent(KeyPressedEvent(42)); + EventManager::getInstance().dispatchEvents(); + gameLoop.setup(); + gameLoop.loop(); + return 0; +} diff --git a/mwe/events/src/timer.cpp b/mwe/events/src/timer.cpp new file mode 100644 index 0000000..f4fee23 --- /dev/null +++ b/mwe/events/src/timer.cpp @@ -0,0 +1,54 @@ +#include "timer.h" + +LoopTimer::LoopTimer() {} +LoopTimer & LoopTimer::getInstance() { + static LoopTimer instance; + return instance; +} + +void LoopTimer::start() { + lastFrameTime = SDL_GetTicks64(); + elapsedTime = 0; + elapsedFixedTime = 0; + deltaTime = 0; +} + +// Update the timer, calculate deltaTime +void LoopTimer::update() { + uint64_t currentFrameTime = SDL_GetTicks64(); + deltaTime + = (currentFrameTime - lastFrameTime) / 1000.0; + + if (deltaTime > maximumDeltaTime) { + deltaTime = maximumDeltaTime; + } + + elapsedTime += deltaTime; + lastFrameTime = currentFrameTime; +} + +double LoopTimer::getDeltaTime() const { return deltaTime; } +int LoopTimer::getCurrentTime() const { return SDL_GetTicks(); } + +void LoopTimer::advanceFixedUpdate() { elapsedFixedTime += fixedDeltaTime; } + +double LoopTimer::getFixedDeltaTime() const { return fixedDeltaTime; } + +void LoopTimer::setFPS(int FPS) { + this->FPS = FPS; + frameTargetTime = 1.0 / FPS; +} + +int LoopTimer::getFPS() const { return FPS; } + +void LoopTimer::enforceFrameRate() { + uint64_t currentFrameTime = SDL_GetTicks64(); + double frameDuration = (currentFrameTime - lastFrameTime) / 1000.0; + + if (frameDuration < frameTargetTime) { + uint32_t delayTime + = (uint32_t) ((frameTargetTime - frameDuration) * 1000.0); + SDL_Delay(delayTime); + } +} +double LoopTimer::getLag() const { return elapsedTime - elapsedFixedTime; } diff --git a/mwe/events/src/window.cpp b/mwe/events/src/window.cpp new file mode 100644 index 0000000..9cafd57 --- /dev/null +++ b/mwe/events/src/window.cpp @@ -0,0 +1,30 @@ +#include "window.h" +WindowManager::WindowManager() { +} +WindowManager::~WindowManager() { destroyWindow(); } +SDL_Renderer * WindowManager::getRenderer() { return renderer; } + +bool WindowManager::initWindow() { + if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { + fprintf(stderr, "Error inititalising SDL.\n"); + return false; + } + window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, + SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + if (!window) { + fprintf(stderr, "Error creating SDL Window. \n"); + return false; + } + renderer = SDL_CreateRenderer(window, -1, 0); + if (!renderer) { + fprintf(stderr, "Error creating SDL renderer. \n"); + return false; + } + return true; +} +void WindowManager::destroyWindow() { + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} diff --git a/mwe/events/versions/delayBased.cpp b/mwe/events/versions/delayBased.cpp new file mode 100644 index 0000000..253a03a --- /dev/null +++ b/mwe/events/versions/delayBased.cpp @@ -0,0 +1,56 @@ +#include "loopManager.h" +#include "timer.h" +LoopManager::LoopManager() {} +void LoopManager::processInput() { + SDL_Event event; + SDL_PollEvent(&event); + switch (event.type) { + case SDL_QUIT: + gameRunning = false; + break; + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_ESCAPE) { + gameRunning = false; + } + break; + } +} +void LoopManager::loop() { + fprintf(stderr, "loop. \n"); + while (gameRunning) { + //Timer::getInstance().update(); + //deltaTime = Timer::getInstance().getDeltaTime(); + processInput(); + update(); + render(); + } + window.destroyWindow(); +} +void LoopManager::setup() { + gameRunning = window.initWindow(); + LoopTimer::getInstance().start(); + LoopTimer::getInstance().setFPS(210); + + for (int i = 0; i < 2; i++) { + GameObject * square2 + = new GameObject("square2", i * 40, i * 40, 20, 20, 0, 0); + objectList.push_back(square2); + } +} +void LoopManager::render() { + if (gameRunning) { + window.render(objectList); + } +} + +void LoopManager::update() { + LoopTimer & timer = LoopTimer::getInstance(); + timer.enforceFrameRate(); + timer.update(); + float delta = timer.getDeltaTime(); + + for (int i = 0; i < objectList.size(); i++) { + objectList[i]->setX(objectList[i]->getX() + 50 * delta); + objectList[i]->setY(objectList[i]->getY() + 50 * delta); + } +} diff --git a/mwe/gameloop/include/eventManager.h b/mwe/gameloop/include/eventManager.h index 69c6801..ba1ca32 100644 --- a/mwe/gameloop/include/eventManager.h +++ b/mwe/gameloop/include/eventManager.h @@ -1 +1,5 @@ -class EventManager {}; +#pragma once +class EventManager { + public: + EventManager(); +}; diff --git a/mwe/gameloop/include/loopManager.h b/mwe/gameloop/include/loopManager.h index e202423..dcbfedc 100644 --- a/mwe/gameloop/include/loopManager.h +++ b/mwe/gameloop/include/loopManager.h @@ -2,6 +2,7 @@ #include "gameObject.h" #include "window.h" #include <SDL2/SDL.h> +#include "event.h" class LoopManager { public: LoopManager(); |