diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ccd8e1..260029e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,8 +20,8 @@ set(SOURCES src/main.cpp src/game/game_object.cpp src/game/game_object.h - src/game/game.cpp src/game/game.h + src/game/game.cpp src/logging/easylogging++.cc src/logging/easylogging++.h src/coordinates/coordinate_transformer.cpp @@ -38,13 +38,20 @@ set(SOURCES src/config.h src/debug/grid_debug_layer.cpp src/debug/grid_debug_layer.h - src/game/input/input_mapper.cpp src/game/input/input_mapper.h + src/game/input/input_mapper.cpp src/game/input/direction.h src/game/input/direction.cpp src/game/player/player.cpp src/game/player/player.hpp - src/game/input/game_inputs.hpp src/game/world/world_view.cpp src/game/world/world_view.h src/utilities/smart_list.cpp src/utilities/smart_list.h src/utilities/vector_utils.hpp src/game/world/ITrackable.h src/game/input/input_identity.h) + src/game/input/game_inputs.hpp + src/game/world/world_view.cpp + src/game/world/world_view.h + src/utilities/smart_list.cpp + src/utilities/smart_list.h + src/utilities/vector_utils.hpp + src/game/world/ITrackable.h + src/game/input/input_identity.h) set(PHYSICS_00_SOURCES src/prototypes/physics_00.cpp) diff --git a/src/config.h b/src/config.h index 73dccad..b7c6da1 100644 --- a/src/config.h +++ b/src/config.h @@ -20,11 +20,13 @@ #define VIEW_RUBBER_FOLLOW_SPEED 0.5f +// Inputs +#define JOYSTICK_DEADZONE 0.1f + // Directions #define DIRECTION_HARD_ACTIVATION_THRESHOLD 0.1f // DEBUG - #define DB_ISOPLANE_CORNER_RADIUS 2 #endif //HOLESOME_CONFIG_H diff --git a/src/game/game.cpp b/src/game/game.cpp index f91d7ed..ecaf136 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -7,7 +7,6 @@ Game::Game(std::shared_ptr window) : window(std::move(window)), gameObjects() { - InputMapper::setGame(this); } Game::~Game() @@ -26,14 +25,14 @@ void Game::run() while (window->isOpen()) { - InputMapper::processEvents(); + InputMapper::getInstance()->processEvents(); TimeSinceLastUpdate += clock.restart(); while (TimeSinceLastUpdate >= FRAME_TIME) { TimeSinceLastUpdate -= FRAME_TIME; update(); - InputMapper::processEvents(); + InputMapper::getInstance()->processEvents(); } drawFrame(); } @@ -75,3 +74,23 @@ void Game::update() gameObject->lateUpdate(this); } } + +std::shared_ptr Game::getInstance() +{ + if (singletonInstance == nullptr) { + throw std::runtime_error("Game instance has to be initialized first."); + } + + return singletonInstance; +} + +std::shared_ptr Game::constructInstance(const std::shared_ptr& window) +{ + if (singletonInstance != nullptr) { + throw std::runtime_error("Game instance has already been initialized."); + } + + singletonInstance = std::make_shared(window); + + return singletonInstance; +} diff --git a/src/game/game.h b/src/game/game.h index df4aef4..1eb2f79 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -15,6 +15,8 @@ class ActionController; class Game { public: + static std::shared_ptr constructInstance(const std::shared_ptr& window); + static std::shared_ptr getInstance(); explicit Game(std::shared_ptr window); ~Game(); @@ -27,6 +29,7 @@ public: std::shared_ptr window; private: + static inline std::shared_ptr singletonInstance = nullptr; std::vector gameObjects; void drawFrame(); diff --git a/src/game/game_factory.cpp b/src/game/game_factory.cpp index 04f5f1d..84177e0 100644 --- a/src/game/game_factory.cpp +++ b/src/game/game_factory.cpp @@ -1,7 +1,3 @@ -// -// Created by max on 28.04.23. -// - #include #include "game_factory.hpp" #include "../config.h" @@ -12,7 +8,7 @@ std::shared_ptr GameFactory::createWindowed(const std::string &title, int sf::Style::Default, getAdditionalSettings()); applyAdditionalWindowConfig(window.get()); - return std::make_shared(window); + return Game::constructInstance(window); } std::shared_ptr GameFactory::createFullscreen(const std::string &title) { @@ -31,7 +27,7 @@ std::shared_ptr GameFactory::createFullscreen(const std::string &title) { getAdditionalSettings()); applyAdditionalWindowConfig(window.get()); - return std::make_shared(window); + return Game::constructInstance(window); } sf::ContextSettings GameFactory::getAdditionalSettings() { diff --git a/src/game/input/direction.cpp b/src/game/input/direction.cpp index e80d676..4e0fdc8 100644 --- a/src/game/input/direction.cpp +++ b/src/game/input/direction.cpp @@ -152,3 +152,15 @@ void Direction::set(sf::Vector2f direction) summedDirections = HardDirection::NONE; this->directionVector = direction; } + +void Direction::setX(float value) +{ + sf::Vector2f newDirection = {value, directionVector.y}; + set(newDirection); +} + +void Direction::setY(float value) +{ + sf::Vector2f newDirection = {directionVector.x, value}; + set(newDirection); +} diff --git a/src/game/input/direction.h b/src/game/input/direction.h index dd6e78f..f241253 100644 --- a/src/game/input/direction.h +++ b/src/game/input/direction.h @@ -40,6 +40,8 @@ public: void remove(HardDirection direction); void set(HardDirection direction); void set(sf::Vector2f direction); + void setX(float value); + void setY(float value); void clear(); private: diff --git a/src/game/input/input_identity.h b/src/game/input/input_identity.h index aa0f3f8..2914833 100644 --- a/src/game/input/input_identity.h +++ b/src/game/input/input_identity.h @@ -19,8 +19,9 @@ struct InputIdentity unsigned int inputOrder = 0; bool isActive = true; - explicit InputIdentity(InputDeviceType type) { + explicit InputIdentity(InputDeviceType type, unsigned int gamepad = 0) { deviceType = type; + gamepadId = gamepad; }; }; diff --git a/src/game/input/input_mapper.cpp b/src/game/input/input_mapper.cpp index 6a148af..eb70390 100644 --- a/src/game/input/input_mapper.cpp +++ b/src/game/input/input_mapper.cpp @@ -1,10 +1,8 @@ #include "input_mapper.h" -void InputMapper::setGame(Game *game) +InputMapper::InputMapper() { - InputMapper::game = game; - // Initialize identities allIdentity = std::make_shared(InputDeviceType::ALL); keyboardIdentity = std::make_shared(InputDeviceType::KEYBOARD); @@ -15,7 +13,7 @@ void InputMapper::setGame(Game *game) void InputMapper::processEvents() { sf::Event event{}; - while (game->window->pollEvent(event)) + while (Game::getInstance()->window->pollEvent(event)) { switch (event.type) { @@ -26,13 +24,19 @@ void InputMapper::processEvents() handleKeyRelease(event.key); break; case sf::Event::Closed: - game->exit(); + Game::getInstance()->exit(); break; case sf::Event::Resized: break; case sf::Event::JoystickMoved: handleJoystickMovement(event.joystickMove); break; + case sf::Event::JoystickConnected: + addGamepadIdentity(event.joystickConnect.joystickId); + break; + case sf::Event::JoystickDisconnected: + deactivateGamepadIdentity(event.joystickConnect.joystickId); + break; default: break; } @@ -44,7 +48,7 @@ void InputMapper::handleKeyPress(sf::Event::KeyEvent event) // Close game on Escape or Q in DEV Mode if (DEVELOPER_MODE && (event.code == sf::Keyboard::Escape || event.code == sf::Keyboard::Q)) { - game->exit(); + Game::getInstance()->exit(); return; } @@ -52,37 +56,44 @@ void InputMapper::handleKeyPress(sf::Event::KeyEvent event) auto direction = Direction::getKeyDirection(event.code); if (direction != HardDirection::NONE) { - inputDirectionBuffer.push_back(direction); + getInputIdentity(InputDeviceType::KEYBOARD)->direction.add(direction); } } -Direction InputMapper::getInputDirection() -{ - HardDirection direction = HardDirection::NONE; - - for (HardDirection directionPart: inputDirectionBuffer) - { - direction = static_cast(direction | directionPart); - } - - return Direction(direction); -} - void InputMapper::handleKeyRelease(sf::Event::KeyEvent event) { // Handle directionVector auto direction = Direction::getKeyDirection(event.code); if (direction != HardDirection::NONE) { - // Remove directionVector from buffer - inputDirectionBuffer.erase(std::remove(inputDirectionBuffer.begin(), inputDirectionBuffer.end(), direction), - inputDirectionBuffer.end()); + getInputIdentity(InputDeviceType::KEYBOARD)->direction.remove(direction); } } void InputMapper::handleJoystickMovement(sf::Event::JoystickMoveEvent event) { - event.joystickId; + auto gamepadIdentity = getInputIdentity(InputDeviceType::GAMEPAD, event.joystickId); + + auto value = event.position / 100.f; + auto axis = event.axis; + + // Handle deadzone and joystick drift + if (value > -JOYSTICK_DEADZONE && value < JOYSTICK_DEADZONE) + { + value = 0.f; + } + + if (axis == sf::Joystick::Axis::X || + axis == sf::Joystick::Axis::R || + axis == sf::Joystick::Axis::PovX) + { + gamepadIdentity->direction.setX(value); + } else if (axis == sf::Joystick::Axis::Y || + axis == sf::Joystick::Axis::U || + axis == sf::Joystick::Axis::PovY) + { + gamepadIdentity->direction.setY(value); + } } std::shared_ptr InputMapper::getInputIdentity(InputDeviceType deviceType, unsigned int gamepadId) @@ -92,12 +103,58 @@ std::shared_ptr InputMapper::getInputIdentity(InputDeviceType dev case InputDeviceType::KEYBOARD: return keyboardIdentity; case InputDeviceType::GAMEPAD: - if (gamepadIdentities.find(gamepadId) == gamepadIdentities.end()) + if (InputMapper::gamepadIdentities.contains(gamepadId)) { - throw std::invalid_argument("Gamepad with id " + std::to_string(gamepadId) + " not found."); + // Create if it does not exist yet + InputMapper::addGamepadIdentity(gamepadId); } - return gamepadIdentities[gamepadId]; + return InputMapper::gamepadIdentities[gamepadId]; default: - return allIdentity; + return InputMapper::allIdentity; } } + +void InputMapper::addGamepadIdentity(unsigned int gamepadId) +{ + // Exists already? + if (InputMapper::gamepadIdentities.contains(gamepadId)) + { + InputMapper::gamepadIdentities[gamepadId]->isActive = true; + return; + } + + // Does not exist yet, create new + auto newIdentity = std::make_shared(InputDeviceType::GAMEPAD, gamepadId); + InputMapper::gamepadIdentities[gamepadId] = newIdentity; +} + +void InputMapper::deactivateGamepadIdentity(unsigned int gamepadId) +{ + auto gamepadIdentity = getInputIdentity(InputDeviceType::GAMEPAD, gamepadId); + gamepadIdentity->isActive = false; + +} + +std::shared_ptr InputMapper::getInstance() +{ + if (singletonInstance == nullptr) { + singletonInstance = std::make_shared(); + } + + return singletonInstance; +} + +std::vector> InputMapper::getAllInputIdentities() +{ + std::vector> allIdentities; + + allIdentities.push_back(allIdentity); + allIdentities.push_back(keyboardIdentity); + + for (auto const& [key, val] : gamepadIdentities) + { + allIdentities.push_back(val); + } + + return allIdentities; +} diff --git a/src/game/input/input_mapper.h b/src/game/input/input_mapper.h index c0c392f..6c9855c 100644 --- a/src/game/input/input_mapper.h +++ b/src/game/input/input_mapper.h @@ -7,34 +7,37 @@ #include "direction.h" #include "input_identity.h" -class Game; - /** * Maps the inputs to actions. */ class InputMapper { + public: - static void setGame(Game *game); + InputMapper(); + static std::shared_ptr getInstance(); - static void processEvents(); + void processEvents(); - static std::shared_ptr getInputIdentity(InputDeviceType deviceType, unsigned int gamepadId = 0); + std::shared_ptr getInputIdentity(InputDeviceType deviceType, unsigned int gamepadId = 0); + std::vector> getAllInputIdentities(); private: - static inline Game *game = nullptr; + static inline std::shared_ptr singletonInstance = nullptr; - static std::shared_ptr allIdentity; - static std::shared_ptr keyboardIdentity; - static std::map> gamepadIdentities; + std::shared_ptr allIdentity; + std::shared_ptr keyboardIdentity; + std::map> gamepadIdentities; - static inline std::vector inputDirectionBuffer = std::vector(); + void handleKeyPress(sf::Event::KeyEvent event); - static void handleKeyPress(sf::Event::KeyEvent event); + void handleKeyRelease(sf::Event::KeyEvent event); - static void handleKeyRelease(sf::Event::KeyEvent event); + void handleJoystickMovement(sf::Event::JoystickMoveEvent event); - static void handleJoystickMovement(sf::Event::JoystickMoveEvent event); + void addGamepadIdentity(unsigned int gamepadId); + + void deactivateGamepadIdentity(unsigned int gamepadId); }; diff --git a/src/game/player/player.cpp b/src/game/player/player.cpp index 864a927..822f196 100644 --- a/src/game/player/player.cpp +++ b/src/game/player/player.cpp @@ -13,7 +13,23 @@ sf::Vector2f Player::getTrackableSize() const void Player::update(Game *game) { - auto moveDirection = InputMapper::getInputDirection().asVector(); + // Get gamepad input + std::shared_ptr gamepadIdentity = nullptr; + for (auto inputIdentity: InputMapper::getInstance()->getAllInputIdentities()) + { + if (inputIdentity->deviceType == InputDeviceType::GAMEPAD) + { + gamepadIdentity = inputIdentity; + break; + } + } + if (gamepadIdentity == nullptr) + { + return; + } + +// auto moveDirection = InputMapper::getInstance()->getInputIdentity(InputDeviceType::KEYBOARD)->direction.asVector(); + auto moveDirection = gamepadIdentity->direction.asScreenVector(); auto moveDelta = moveDirection * 30.0f * FRAME_TIME.asSeconds(); coordinates.move(moveDelta); circle->coordinates.set(coordinates); diff --git a/src/game/world/world_view.cpp b/src/game/world/world_view.cpp index b93a7ee..e54cb08 100644 --- a/src/game/world/world_view.cpp +++ b/src/game/world/world_view.cpp @@ -71,7 +71,7 @@ sf::Vector2f WorldView::getCenter() const void WorldView::followTarget() { - + // TODO auto closestPositionInDynamic = getClosestPositionInArea(dynamicFollowArea); marker->coordinates.set(IsometricCoordinates(closestPositionInDynamic)); return; diff --git a/src/logging/easylogging++.h b/src/logging/easylogging++.h index 6d76cb5..3361e23 100644 --- a/src/logging/easylogging++.h +++ b/src/logging/easylogging++.h @@ -3924,7 +3924,7 @@ class VersionInfo : base::StaticClass { /// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); /// @see el::base::PerformanceTracker /// @see el::base::PerformanceTracker::checkpoint -// Note: Do not surround this definition with null macro because of obj instance +// Note: Do not surround this definition with null macro because of obj singletonInstance #define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? \ new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) #define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true)