From 90c3b6149e57fd90e84c2567da684e84f3cdbddd Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sat, 20 Sep 2025 17:57:56 -0500 Subject: [PATCH] Input --- src/CMakeLists.txt | 1 + src/assert/Assert.cpp | 2 +- src/console/Console.cpp | 29 ++++- src/console/Console.hpp | 22 +++- src/display/Display.cpp | 9 +- src/display/Display.hpp | 7 +- src/engine/Engine.cpp | 15 +-- src/engine/Engine.hpp | 11 +- src/input/CMakeLists.txt | 1 + src/input/Input.cpp | 218 ++++++++++++++++++++++++++++++++++---- src/input/Input.hpp | 32 +++--- src/input/InputBind.cpp | 26 +++++ src/input/InputBind.hpp | 35 ++++++ src/input/InputButton.cpp | 74 ++++++++++++- src/input/InputButton.hpp | 55 +++++++++- src/main.cpp | 6 +- 16 files changed, 469 insertions(+), 74 deletions(-) create mode 100644 src/input/InputBind.cpp create mode 100644 src/input/InputBind.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 025d5c5e..20e9f30c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory(assert) add_subdirectory(console) add_subdirectory(display) add_subdirectory(engine) +add_subdirectory(input) add_subdirectory(time) # Platform-specific settings diff --git a/src/assert/Assert.cpp b/src/assert/Assert.cpp index cc05dbc4..3114cd4b 100644 --- a/src/assert/Assert.cpp +++ b/src/assert/Assert.cpp @@ -52,7 +52,7 @@ assertTrueImpl( file, line, - pointer != NULL, + pointer != NULL && pointer != nullptr, message ); diff --git a/src/console/Console.cpp b/src/console/Console.cpp index c7730662..f0e9c3fb 100644 --- a/src/console/Console.cpp +++ b/src/console/Console.cpp @@ -13,16 +13,23 @@ Console::Console(void) : variables(), buffer() { + this->registerCommand("echo", [](auto console, auto args) { + if(args.size() == 0) { + console.print("Usage: echo "); + return; + } + console.print("%s", args[0].c_str()); + }); } void Console::registerCommand( const std::string &cmd, - const std::function&)> &callback + const std::function&)> &cb ) { assertTrue(cmd.length() > 0, "cmd length must be > 0"); - assertTrue(callback != nullptr, "callback must not be null"); + assertTrue(cb != nullptr, "callback must not be null"); - this->commands[cmd] = callback; + this->commands[cmd] = cb; } template @@ -121,7 +128,7 @@ void Console::update() { printf("Console: Variable '%s' ", cmdName.c_str()); continue; } - itCmd->second(args); + itCmd->second(*this, args); } buffer.clear(); } @@ -141,6 +148,20 @@ void Console::exec(const std::string &str) { } } +void Console::print(const char_t *fmt, ...) { + va_list args; + va_start(args, fmt); + + vprintf(fmt, args); + printf("\n"); + + va_end(args); +} + +void Console::print(std::string str) { + this->print("%s", str.c_str()); +} + Console::~Console(void) { } \ No newline at end of file diff --git a/src/console/Console.hpp b/src/console/Console.hpp index 4ff0cdbf..b99623e7 100644 --- a/src/console/Console.hpp +++ b/src/console/Console.hpp @@ -10,7 +10,8 @@ namespace Dawn { struct Console { private: std::map< - std::string, std::function&)> + std::string, + std::function&)> > commands; std::map&)> &callback + const std::function&)> &cb ); /** @@ -64,6 +65,21 @@ namespace Dawn { */ void exec(const std::string &str); + /** + * Prints a formatted string to the console output. + * + * @param fmt The format string. + * @param ... The format arguments. + */ + void print(const char_t *fmt, ...); + + /** + * Prints a string to the console output. + * + * @param str The string to print. + */ + void print(std::string str); + /** * Destructs the console. */ diff --git a/src/display/Display.cpp b/src/display/Display.cpp index ba97c46b..978b1aa7 100644 --- a/src/display/Display.cpp +++ b/src/display/Display.cpp @@ -8,11 +8,12 @@ using namespace Dawn; -Display::Display(void) : +Display::Display(Engine &engine) : #if DAWN_SDL2 glContext(nullptr), - window(nullptr) + window(nullptr), #endif + engine(engine) { #if DAWN_SDL2 uint32_t flags = SDL_INIT_VIDEO; @@ -72,14 +73,14 @@ void Display::update(void) { while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: { - Engine::getInstance()->console.exec("exit"); + engine.console.exec("exit"); break; } case SDL_WINDOWEVENT: { switch(event.window.event) { case SDL_WINDOWEVENT_CLOSE: { - Engine::getInstance()->console.exec("exit"); + engine.console.exec("exit"); break; } diff --git a/src/display/Display.hpp b/src/display/Display.hpp index 95def75b..acf78b2c 100644 --- a/src/display/Display.hpp +++ b/src/display/Display.hpp @@ -7,8 +7,11 @@ #include "dawn.hpp" namespace Dawn { + struct Engine; + struct Display { private: + Engine &engine; #if DAWN_SDL2 SDL_Window* window; SDL_GLContext glContext; @@ -20,8 +23,10 @@ namespace Dawn { /** * Display constructor + * + * @param engine The engine instance. */ - Display(void); + Display(Engine &engine); /** * Update the display (swap buffers, etc.) diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 077077c6..1b2b1144 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -7,21 +7,13 @@ using namespace Dawn; -std::shared_ptr Engine::instance = nullptr; - -std::shared_ptr Engine::getInstance() { - if(!Engine::instance) { - Engine::instance = std::make_shared(); - } - return Engine::instance; -} - Engine::Engine() : time(), console(), - display() + display(*this), + input(*this) { - console.registerCommand("exit", [this](std::vector &args) { + console.registerCommand("exit", [this](auto console, auto args) { this->exitRequested = true; }); } @@ -32,6 +24,7 @@ bool_t Engine::isExitRequested() const { void Engine::update(void) { time.update(); + input.update(); console.update(); display.update(); } diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index b2218041..c07a9a88 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -7,11 +7,12 @@ #include "time/Time.hpp" #include "console/Console.hpp" #include "display/Display.hpp" +#include "input/Input.hpp" namespace Dawn { struct Engine { private: - static std::shared_ptr instance; + static Engine* instance; bool_t exitRequested = false; @@ -19,13 +20,7 @@ namespace Dawn { Time time; Console console; Display display; - - /** - * Get the singleton instance of the engine. - * - * @return A shared pointer to the engine instance. - */ - static std::shared_ptr getInstance(); + Input input; /** * Constructor for the Dawn engine. diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 81d68f18..1da07738 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -8,4 +8,5 @@ target_sources(${DAWN_TARGET_NAME} PRIVATE Input.cpp InputButton.cpp + InputBind.cpp ) \ No newline at end of file diff --git a/src/input/Input.cpp b/src/input/Input.cpp index b62e414d..45010eb7 100644 --- a/src/input/Input.cpp +++ b/src/input/Input.cpp @@ -4,57 +4,239 @@ // https://opensource.org/licenses/MIT #include "Input.hpp" +#include "assert/Assert.hpp" +#include "engine/Engine.hpp" using namespace Dawn; -Input::Input(void) : +Input::Input(Engine &engine) : + engine(engine), states({}), buttons({}) { + engine.console.registerCommand("bind", [this](auto console, auto args) { + if(args.size() == 0) { + this->engine.console.print("Usage: bind [command]"); + return; + } + + // Find input by name. + auto itButton = this->buttons.begin(); + while(itButton != this->buttons.end()) { + if(itButton->getName() == args[0]) break; + ++itButton; + } + + if(itButton == this->buttons.end()) { + this->engine.console.print("No such input: %s", args[0].c_str()); + return; + } + + // Is there a second arg? + if(args.size() == 1) { + auto bind = itButton->getBind(); + if(bind) { + console.print(bind->getName()); + } else { + console.print(itButton->getCommand()); + } + return; + } + + // Is there a bind for the arg? + auto itBind = InputBind::ALL_BINDS.begin(); + while(itBind != InputBind::ALL_BINDS.end()) { + if(strcasecmp((*itBind)->getName().c_str(),args[1].c_str()) == 0) break; + ++itBind; + } + + // This is a bind? + if(itBind != InputBind::ALL_BINDS.end()) { + itButton->setBind(const_cast(*itBind)); + return; + } + + // This is a command then. + itButton->setCommand(args[1]); + }); + + #if DAWN_SDL2 + buttons.push_back(InputButton("esc", InputButtonType::KEY, { .scancode = SDL_SCANCODE_ESCAPE })); + buttons.push_back(InputButton("1", InputButtonType::KEY, { .scancode = SDL_SCANCODE_1 })); + buttons.push_back(InputButton("2", InputButtonType::KEY, { .scancode = SDL_SCANCODE_2 })); + buttons.push_back(InputButton("3", InputButtonType::KEY, { .scancode = SDL_SCANCODE_3 })); + buttons.push_back(InputButton("4", InputButtonType::KEY, { .scancode = SDL_SCANCODE_4 })); + buttons.push_back(InputButton("5", InputButtonType::KEY, { .scancode = SDL_SCANCODE_5 })); + buttons.push_back(InputButton("6", InputButtonType::KEY, { .scancode = SDL_SCANCODE_6 })); + buttons.push_back(InputButton("7", InputButtonType::KEY, { .scancode = SDL_SCANCODE_7 })); + buttons.push_back(InputButton("8", InputButtonType::KEY, { .scancode = SDL_SCANCODE_8 })); + buttons.push_back(InputButton("9", InputButtonType::KEY, { .scancode = SDL_SCANCODE_9 })); + buttons.push_back(InputButton("0", InputButtonType::KEY, { .scancode = SDL_SCANCODE_0 })); + buttons.push_back(InputButton("-", InputButtonType::KEY, { .scancode = SDL_SCANCODE_MINUS })); + buttons.push_back(InputButton("backspace", InputButtonType::KEY, { .scancode = SDL_SCANCODE_BACKSPACE })); + buttons.push_back(InputButton("tab", InputButtonType::KEY, { .scancode = SDL_SCANCODE_TAB })); + buttons.push_back(InputButton("q", InputButtonType::KEY, { .scancode = SDL_SCANCODE_Q })); + buttons.push_back(InputButton("w", InputButtonType::KEY, { .scancode = SDL_SCANCODE_W })); + buttons.push_back(InputButton("e", InputButtonType::KEY, { .scancode = SDL_SCANCODE_E })); + buttons.push_back(InputButton("r", InputButtonType::KEY, { .scancode = SDL_SCANCODE_R })); + buttons.push_back(InputButton("t", InputButtonType::KEY, { .scancode = SDL_SCANCODE_T })); + buttons.push_back(InputButton("y", InputButtonType::KEY, { .scancode = SDL_SCANCODE_Y })); + buttons.push_back(InputButton("u", InputButtonType::KEY, { .scancode = SDL_SCANCODE_U })); + buttons.push_back(InputButton("i", InputButtonType::KEY, { .scancode = SDL_SCANCODE_I })); + buttons.push_back(InputButton("o", InputButtonType::KEY, { .scancode = SDL_SCANCODE_O })); + buttons.push_back(InputButton("p", InputButtonType::KEY, { .scancode = SDL_SCANCODE_P })); + buttons.push_back(InputButton("[", InputButtonType::KEY, { .scancode = SDL_SCANCODE_LEFTBRACKET })); + buttons.push_back(InputButton("]", InputButtonType::KEY, { .scancode = SDL_SCANCODE_RIGHTBRACKET })); + buttons.push_back(InputButton("enter", InputButtonType::KEY, { .scancode = SDL_SCANCODE_RETURN })); + buttons.push_back(InputButton("a", InputButtonType::KEY, { .scancode = SDL_SCANCODE_A })); + buttons.push_back(InputButton("s", InputButtonType::KEY, { .scancode = SDL_SCANCODE_S })); + buttons.push_back(InputButton("d", InputButtonType::KEY, { .scancode = SDL_SCANCODE_D })); + buttons.push_back(InputButton("f", InputButtonType:: KEY, { .scancode = SDL_SCANCODE_F })); + buttons.push_back(InputButton("g", InputButtonType::KEY, { .scancode = SDL_SCANCODE_G })); + buttons.push_back(InputButton("h", InputButtonType::KEY, { .scancode = SDL_SCANCODE_H })); + buttons.push_back(InputButton("j", InputButtonType::KEY, { .scancode = SDL_SCANCODE_J })); + buttons.push_back(InputButton("k", InputButtonType::KEY, { .scancode = SDL_SCANCODE_K })); + buttons.push_back(InputButton("l", InputButtonType::KEY, { .scancode = SDL_SCANCODE_L })); + buttons.push_back(InputButton(";", InputButtonType::KEY, { .scancode = SDL_SCANCODE_SEMICOLON })); + buttons.push_back(InputButton("'", InputButtonType::KEY, { .scancode = SDL_SCANCODE_APOSTROPHE })); + buttons.push_back(InputButton("`", InputButtonType::KEY, { .scancode = SDL_SCANCODE_GRAVE })); + buttons.push_back(InputButton("\\", InputButtonType::KEY, { .scancode = SDL_SCANCODE_BACKSLASH })); + buttons.push_back(InputButton("z", InputButtonType::KEY, { .scancode = SDL_SCANCODE_Z })); + buttons.push_back(InputButton("x", InputButtonType::KEY, { .scancode = SDL_SCANCODE_X })); + buttons.push_back(InputButton("c", InputButtonType::KEY, { .scancode = SDL_SCANCODE_C })); + buttons.push_back(InputButton("v", InputButtonType::KEY, { .scancode = SDL_SCANCODE_V })); + buttons.push_back(InputButton("b", InputButtonType::KEY, { .scancode = SDL_SCANCODE_B })); + buttons.push_back(InputButton("n", InputButtonType::KEY, { .scancode = SDL_SCANCODE_N })); + buttons.push_back(InputButton("m", InputButtonType::KEY, { .scancode = SDL_SCANCODE_M })); + buttons.push_back(InputButton(",", InputButtonType::KEY, { .scancode = SDL_SCANCODE_COMMA })); + buttons.push_back(InputButton(".", InputButtonType::KEY, { .scancode = SDL_SCANCODE_PERIOD })); + buttons.push_back(InputButton("/", InputButtonType::KEY, { .scancode = SDL_SCANCODE_SLASH })); + buttons.push_back(InputButton("space", InputButtonType::KEY, { .scancode = SDL_SCANCODE_SPACE })); + buttons.push_back(InputButton("capslock", InputButtonType::KEY, { .scancode = SDL_SCANCODE_CAPSLOCK })); + buttons.push_back(InputButton("f1", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F1 })); + buttons.push_back(InputButton("f2", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F2 })); + buttons.push_back(InputButton("f3", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F3 })); + buttons.push_back(InputButton("f4", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F4 })); + buttons.push_back(InputButton("f5", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F5 })); + buttons.push_back(InputButton("f6", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F6 })); + buttons.push_back(InputButton("f7", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F7 })); + buttons.push_back(InputButton("f8", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F8 })); + buttons.push_back(InputButton("f9", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F9 })); + buttons.push_back(InputButton("f10", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F10 })); + buttons.push_back(InputButton("f11", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F11 })); + buttons.push_back(InputButton("f12", InputButtonType::KEY, { .scancode = SDL_SCANCODE_F12 })); + buttons.push_back(InputButton("printscreen", InputButtonType::KEY, { .scancode = SDL_SCANCODE_PRINTSCREEN })); + buttons.push_back(InputButton("scrolllock", InputButtonType::KEY, { .scancode = SDL_SCANCODE_SCROLLLOCK })); + buttons.push_back(InputButton("pause", InputButtonType::KEY, { .scancode = SDL_SCANCODE_PAUSE })); + buttons.push_back(InputButton("insert", InputButtonType::KEY, { .scancode = SDL_SCANCODE_INSERT })); + buttons.push_back(InputButton("home", InputButtonType::KEY, { .scancode = SDL_SCANCODE_HOME })); + buttons.push_back(InputButton("end", InputButtonType::KEY, { .scancode = SDL_SCANCODE_END })); + buttons.push_back(InputButton("pageup", InputButtonType::KEY, { .scancode = SDL_SCANCODE_PAGEUP })); + buttons.push_back(InputButton("pagedown", InputButtonType::KEY, { .scancode = SDL_SCANCODE_PAGEDOWN })); + buttons.push_back(InputButton("delete", InputButtonType::KEY, { .scancode = SDL_SCANCODE_DELETE })); + buttons.push_back(InputButton("right", InputButtonType::KEY, { .scancode = SDL_SCANCODE_RIGHT })); + buttons.push_back(InputButton("left", InputButtonType::KEY, { .scancode = SDL_SCANCODE_LEFT })); + buttons.push_back(InputButton("down", InputButtonType::KEY, { .scancode = SDL_SCANCODE_DOWN })); + buttons.push_back(InputButton("up", InputButtonType::KEY, { .scancode = SDL_SCANCODE_UP })); + buttons.push_back(InputButton("numlock", InputButtonType::KEY, { .scancode = SDL_SCANCODE_NUMLOCKCLEAR })); + buttons.push_back(InputButton("kp_divide", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_DIVIDE })); + buttons.push_back(InputButton("kp_multiply", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_MULTIPLY })); + buttons.push_back(InputButton("kp_minus", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_MINUS })); + buttons.push_back(InputButton("kp_plus", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_PLUS })); + buttons.push_back(InputButton("kp_enter", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_ENTER })); + buttons.push_back(InputButton("kp_1", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_1 })); + buttons.push_back(InputButton("kp_2", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_2 })); + buttons.push_back(InputButton("kp_3", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_3 })); + buttons.push_back(InputButton("kp_4", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_4 })); + buttons.push_back(InputButton("kp_5", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_5 })); + buttons.push_back(InputButton("kp_6", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_6 })); + buttons.push_back(InputButton("kp_7", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_7 })); + buttons.push_back(InputButton("kp_8", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_8 })); + buttons.push_back(InputButton("kp_9", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_9 })); + buttons.push_back(InputButton("kp_0", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_0 })); + buttons.push_back(InputButton("kp_period", InputButtonType::KEY, { .scancode = SDL_SCANCODE_KP_PERIOD })); + buttons.push_back(InputButton("ctrl", InputButtonType::KEY, { .scancode = SDL_SCANCODE_LCTRL })); + buttons.push_back(InputButton("shift", InputButtonType::KEY, { .scancode = SDL_SCANCODE_LSHIFT })); + buttons.push_back(InputButton("alt", InputButtonType::KEY, { .scancode = SDL_SCANCODE_LALT })); + buttons.push_back(InputButton("rctrl", InputButtonType::KEY, { .scancode = SDL_SCANCODE_RCTRL })); + buttons.push_back(InputButton("rshift", InputButtonType::KEY, { .scancode = SDL_SCANCODE_RSHIFT })); + buttons.push_back(InputButton("ralt", InputButtonType::KEY, { .scancode = SDL_SCANCODE_RALT })); + #endif } -float_t Input::getCurrent(const InputBind &bind) const { +float_t Input::getCurrent(const InputBind *bind) const { + assertNotNull(bind, "Input bind cannot be NULL"); + auto it = states.find(bind); - if(it != states.end()) { - return it->second.current; - } + if(it != states.end()) return it->second.current; return 0.0f; } -float_t Input::getPrevious(const InputBind &bind) const { +float_t Input::getPrevious(const InputBind* bind) const { + assertNotNull(bind, "Input bind cannot be NULL"); + auto it = states.find(bind); - if(it != states.end()) { - return it->second.previous; - } + if(it != states.end()) return it->second.previous; return 0.0f; } -float_t Input::getWhen(const InputBind &bind) const { +float_t Input::getWhen(const InputBind *bind) const { auto it = states.find(bind); - if(it != states.end()) { - return it->second.when; - } + if(it != states.end()) return it->second.when; return 0.0f; } -bool_t Input::isDown(const InputBind &bind) const { +bool_t Input::isDown(const InputBind* bind) const { + assertNotNull(bind, "Input bind cannot be NULL"); return this->getCurrent(bind) != 0.0f; } -bool_t Input::isUp(const InputBind &bind) const { +bool_t Input::isUp(const InputBind* bind) const { + assertNotNull(bind, "Input bind cannot be NULL"); return this->getCurrent(bind) == 0.0f; } -bool_t Input::wasPressed(const InputBind &bind) const { +bool_t Input::wasPressed(const InputBind* bind) const { + assertNotNull(bind, "Input bind cannot be NULL"); return this->getPrevious(bind) == 0.0f && this->getCurrent(bind) != 0.0f; } -bool_t Input::wasReleased(const InputBind &bind) const { +bool_t Input::wasReleased(const InputBind* bind) const { + assertNotNull(bind, "Input bind cannot be NULL"); return this->getPrevious(bind) != 0.0f && this->getCurrent(bind) == 0.0f; } void Input::update(void) { - + // Reset all input bind states + for(auto &state : this->states) { + state.second.previous = state.second.current; + state.second.current = 0.0f; + } + + // Update button states. + auto itButton = this->buttons.begin(); + while(itButton != this->buttons.end()) { + itButton->update(); + + if(itButton->getCurrent() != 0.0f) { + // Exec command if actuated this tick. + auto cmd = itButton->getCommand(); + if(itButton->getPrevious() == 0.0f && cmd.length() > 0) { + engine.console.exec(cmd); + } else { + // If bound to an input, update the bind state. + auto bind = itButton->getBind(); + if(bind != nullptr) { + states[bind].when = engine.time.time; + states[bind].current = fmaxf( + states[bind].current, itButton->getCurrent() + ); + } + } + } + + ++itButton; + } } Input::~Input(void) { diff --git a/src/input/Input.hpp b/src/input/Input.hpp index ca43c6e7..fd7ca45d 100644 --- a/src/input/Input.hpp +++ b/src/input/Input.hpp @@ -7,31 +7,27 @@ #include "InputButton.hpp" namespace Dawn { - enum class InputBind { - UP = "UP", - DOWN = "DOWN", - LEFT = "LEFT", - RIGHT = "RIGHT", - ACCEPT = "ACCEPT", - CANCEL = "CANCEL" - }; - struct InputBindState { float_t current; float_t previous; float_t when; }; + struct Engine; + struct Input { private: - std::map states; + std::map states; std::vector buttons; + Engine &engine; public: /** * Initializes the input manager. + * + * @param engine The engine instance. */ - Input(void); + Input(Engine &engine); /** * Returns the current state of the input bind. @@ -39,7 +35,7 @@ namespace Dawn { * @param bind The input bind to check. * @return The current state of the input bind. */ - float_t getCurrent(const InputBind &bind) const; + float_t getCurrent(const InputBind* bind) const; /** * Returns the previous state of the input bind. @@ -47,7 +43,7 @@ namespace Dawn { * @param bind The input bind to check. * @return The previous state of the input bind. */ - float_t getPrevious(const InputBind &bind) const; + float_t getPrevious(const InputBind* bind) const; /** * Returns the time when the input last changed states. @@ -55,7 +51,7 @@ namespace Dawn { * @param bind The input bind to check. * @return The time when the input last changed states. */ - float_t getWhen(const InputBind &bind) const; + float_t getWhen(const InputBind* bind) const; /** * Returns true if the input bind is currently down. @@ -63,7 +59,7 @@ namespace Dawn { * @param bind The input bind to check. * @return True if the input bind is currently down. */ - bool_t isDown(const InputBind &bind) const; + bool_t isDown(const InputBind* bind) const; /** * Returns true if the input bind is currently up. @@ -71,7 +67,7 @@ namespace Dawn { * @param bind The input bind to check. * @return True if the input bind is currently up. */ - bool_t isUp(const InputBind &bind) const; + bool_t isUp(const InputBind* bind) const; /** * Returns true if the input bind was pressed this frame. @@ -79,7 +75,7 @@ namespace Dawn { * @param bind The input bind to check. * @return True if the input bind was pressed this frame. */ - bool_t wasPressed(const InputBind &bind) const; + bool_t wasPressed(const InputBind* bind) const; /** * Returns true if the input bind was released this frame. @@ -87,7 +83,7 @@ namespace Dawn { * @param bind The input bind to check. * @return True if the input bind was released this frame. */ - bool_t wasReleased(const InputBind &bind) const; + bool_t wasReleased(const InputBind* bind) const; /** * Updates the input manager. diff --git a/src/input/InputBind.cpp b/src/input/InputBind.cpp new file mode 100644 index 00000000..b29cea93 --- /dev/null +++ b/src/input/InputBind.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2025 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "InputBind.hpp" + +using namespace Dawn; + +std::vector InputBind::ALL_BINDS; + +const InputBind InputBind::UP("UP"); +const InputBind InputBind::DOWN("DOWN"); +const InputBind InputBind::LEFT("LEFT"); +const InputBind InputBind::RIGHT("RIGHT"); +const InputBind InputBind::ACCEPT("ACCEPT"); +const InputBind InputBind::CANCEL("CANCEL"); + + +InputBind::InputBind(const std::string &name) : name(name) { + ALL_BINDS.push_back(this); +} + +std::string InputBind::getName() const { + return this->name; +} \ No newline at end of file diff --git a/src/input/InputBind.hpp b/src/input/InputBind.hpp new file mode 100644 index 00000000..3e0ed2c8 --- /dev/null +++ b/src/input/InputBind.hpp @@ -0,0 +1,35 @@ +// Copyright (c) 2025 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "dawn.hpp" + +namespace Dawn { + struct InputBind { + public: + const std::string name; + + static std::vector ALL_BINDS; + static const InputBind UP; + static const InputBind DOWN; + static const InputBind LEFT; + static const InputBind RIGHT; + static const InputBind ACCEPT; + static const InputBind CANCEL; + + /** + * Returns the name of the input bind. + * + * @return The name of the input bind. + */ + std::string getName() const; + + private: + /** + * Private only constructor for an InputBind. + */ + InputBind(const std::string &name); + }; +} \ No newline at end of file diff --git a/src/input/InputButton.cpp b/src/input/InputButton.cpp index 53996e7f..4c1655e3 100644 --- a/src/input/InputButton.cpp +++ b/src/input/InputButton.cpp @@ -4,17 +4,87 @@ // https://opensource.org/licenses/MIT #include "InputButton.hpp" +#include "engine/Engine.hpp" +#include "assert/Assert.hpp" using namespace Dawn; InputButton::InputButton( const std::string &name, - const InputButtonType buttonType + const InputButtonType buttonType, + const InputButtonData data ) : name(name), buttonType(buttonType), data(data), - command("") + command(""), + bind(nullptr), + current(0.0f), + previous(0.0f) { +} + +void InputButton::update() { + previous = current; + + switch(this->buttonType) { + case InputButtonType::KEY: { + #if DAWN_SDL2 + const uint8_t *state = SDL_GetKeyboardState(NULL); + current = state[this->data.scancode] ? 1.0f : 0.0f; + #else + #error "No input platform defined" + #endif + break; + } + + case InputButtonType::BUTTON: { + current = 0.0f; + break; + } + + case InputButtonType::AXIS: { + current = 0.0f; + break; + } + + default: { + assertUnreachable("Unknown input button type"); + } + } +} + +float_t InputButton::getCurrent(void) const { + return this->current; +} + +float_t InputButton::getPrevious(void) const { + return this->previous; +} + +std::string InputButton::getName(void) const { + return this->name; +} + +InputButtonType InputButton::getType(void) const { + return this->buttonType; +} + +std::string InputButton::getCommand(void) const { + return this->command; +} + +InputBind * InputButton::getBind(void) const { + return this->bind; +} + +void InputButton::setBind(const InputBind *bind) { + this->bind = const_cast(bind); + this->command.clear(); +} + +void InputButton::setCommand(const std::string &command) { + this->command = command; + this->bind = nullptr; } \ No newline at end of file diff --git a/src/input/InputButton.hpp b/src/input/InputButton.hpp index fc357e19..3e75bb4f 100644 --- a/src/input/InputButton.hpp +++ b/src/input/InputButton.hpp @@ -4,15 +4,19 @@ // https://opensource.org/licenses/MIT #pragma once -#include "dusk.hpp" +#include "InputBind.hpp" namespace Dawn { enum class InputButtonType { BUTTON, - AXIS + AXIS, + KEY }; - struct InputButtonData { + union InputButtonData { + #if DAWN_SDL2 + SDL_Scancode scancode; + #endif }; struct InputButton { @@ -20,7 +24,12 @@ namespace Dawn { const std::string name; const InputButtonType buttonType; const InputButtonData data; + std::string command; + InputBind *bind; + + float_t previous; + float_t current; public: /** @@ -38,6 +47,25 @@ namespace Dawn { const InputButtonData data ); + /** + * Updates the state of the input button. + */ + void update(); + + /** + * Returns the current state of the input button. + * + * @return The current state of the input button. + */ + float_t getCurrent() const; + + /** + * Returns the previous state of the input button. + * + * @return The previous state of the input button. + */ + float_t getPrevious() const; + /** * Returns the human name of the input button. * @@ -58,5 +86,26 @@ namespace Dawn { * @return The command that is executed when the input button is pressed. */ std::string getCommand(void) const; + + /** + * Returns the input bind associated with this button, or NULL if none. + * + * @return The input bind associated with this button, or NULL if none. + */ + InputBind * getBind(void) const; + + /** + * Sets the input bind for a button. + * + * @param bind The input bind to associate with this button. + */ + void setBind(const InputBind *bind); + + /** + * Sets the command to be executed when the button is pressed. + * + * @param command The command to execute when the button is pressed. + */ + void setCommand(const std::string &command); }; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index a187806e..7cf2a7cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,11 +9,15 @@ using namespace Dawn; int main(int argc, char **argv) { - auto engine = Engine::getInstance(); + std::shared_ptr engine = std::make_shared(); + + engine->console.exec("bind w up"); while(!engine->isExitRequested()) { engine->update(); } + engine = nullptr; + return 0; } \ No newline at end of file