Input Identities added and basic controller support

This commit is contained in:
Maximilian Giller 2023-05-17 14:13:39 +02:00
parent b7fca7d07a
commit ddea927ce0
13 changed files with 175 additions and 57 deletions

View file

@ -20,8 +20,8 @@ set(SOURCES
src/main.cpp src/main.cpp
src/game/game_object.cpp src/game/game_object.cpp
src/game/game_object.h src/game/game_object.h
src/game/game.cpp
src/game/game.h src/game/game.h
src/game/game.cpp
src/logging/easylogging++.cc src/logging/easylogging++.cc
src/logging/easylogging++.h src/logging/easylogging++.h
src/coordinates/coordinate_transformer.cpp src/coordinates/coordinate_transformer.cpp
@ -38,13 +38,20 @@ set(SOURCES
src/config.h src/config.h
src/debug/grid_debug_layer.cpp src/debug/grid_debug_layer.cpp
src/debug/grid_debug_layer.h src/debug/grid_debug_layer.h
src/game/input/input_mapper.cpp
src/game/input/input_mapper.h src/game/input/input_mapper.h
src/game/input/input_mapper.cpp
src/game/input/direction.h src/game/input/direction.h
src/game/input/direction.cpp src/game/input/direction.cpp
src/game/player/player.cpp src/game/player/player.cpp
src/game/player/player.hpp 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 set(PHYSICS_00_SOURCES
src/prototypes/physics_00.cpp) src/prototypes/physics_00.cpp)

View file

@ -20,11 +20,13 @@
#define VIEW_RUBBER_FOLLOW_SPEED 0.5f #define VIEW_RUBBER_FOLLOW_SPEED 0.5f
// Inputs
#define JOYSTICK_DEADZONE 0.1f
// Directions // Directions
#define DIRECTION_HARD_ACTIVATION_THRESHOLD 0.1f #define DIRECTION_HARD_ACTIVATION_THRESHOLD 0.1f
// DEBUG // DEBUG
#define DB_ISOPLANE_CORNER_RADIUS 2 #define DB_ISOPLANE_CORNER_RADIUS 2
#endif //HOLESOME_CONFIG_H #endif //HOLESOME_CONFIG_H

View file

@ -7,7 +7,6 @@
Game::Game(std::shared_ptr<sf::RenderWindow> window) : window(std::move(window)), Game::Game(std::shared_ptr<sf::RenderWindow> window) : window(std::move(window)),
gameObjects() gameObjects()
{ {
InputMapper::setGame(this);
} }
Game::~Game() Game::~Game()
@ -26,14 +25,14 @@ void Game::run()
while (window->isOpen()) while (window->isOpen())
{ {
InputMapper::processEvents(); InputMapper::getInstance()->processEvents();
TimeSinceLastUpdate += clock.restart(); TimeSinceLastUpdate += clock.restart();
while (TimeSinceLastUpdate >= FRAME_TIME) while (TimeSinceLastUpdate >= FRAME_TIME)
{ {
TimeSinceLastUpdate -= FRAME_TIME; TimeSinceLastUpdate -= FRAME_TIME;
update(); update();
InputMapper::processEvents(); InputMapper::getInstance()->processEvents();
} }
drawFrame(); drawFrame();
} }
@ -75,3 +74,23 @@ void Game::update()
gameObject->lateUpdate(this); gameObject->lateUpdate(this);
} }
} }
std::shared_ptr<Game> Game::getInstance()
{
if (singletonInstance == nullptr) {
throw std::runtime_error("Game instance has to be initialized first.");
}
return singletonInstance;
}
std::shared_ptr<Game> Game::constructInstance(const std::shared_ptr<sf::RenderWindow>& window)
{
if (singletonInstance != nullptr) {
throw std::runtime_error("Game instance has already been initialized.");
}
singletonInstance = std::make_shared<Game>(window);
return singletonInstance;
}

View file

@ -15,6 +15,8 @@ class ActionController;
class Game class Game
{ {
public: public:
static std::shared_ptr<Game> constructInstance(const std::shared_ptr<sf::RenderWindow>& window);
static std::shared_ptr<Game> getInstance();
explicit Game(std::shared_ptr<sf::RenderWindow> window); explicit Game(std::shared_ptr<sf::RenderWindow> window);
~Game(); ~Game();
@ -27,6 +29,7 @@ public:
std::shared_ptr<sf::RenderWindow> window; std::shared_ptr<sf::RenderWindow> window;
private: private:
static inline std::shared_ptr<Game> singletonInstance = nullptr;
std::vector<GameObject *> gameObjects; std::vector<GameObject *> gameObjects;
void drawFrame(); void drawFrame();

View file

@ -1,7 +1,3 @@
//
// Created by max on 28.04.23.
//
#include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics/RenderWindow.hpp>
#include "game_factory.hpp" #include "game_factory.hpp"
#include "../config.h" #include "../config.h"
@ -12,7 +8,7 @@ std::shared_ptr<Game> GameFactory::createWindowed(const std::string &title, int
sf::Style::Default, sf::Style::Default,
getAdditionalSettings()); getAdditionalSettings());
applyAdditionalWindowConfig(window.get()); applyAdditionalWindowConfig(window.get());
return std::make_shared<Game>(window); return Game::constructInstance(window);
} }
std::shared_ptr<Game> GameFactory::createFullscreen(const std::string &title) { std::shared_ptr<Game> GameFactory::createFullscreen(const std::string &title) {
@ -31,7 +27,7 @@ std::shared_ptr<Game> GameFactory::createFullscreen(const std::string &title) {
getAdditionalSettings()); getAdditionalSettings());
applyAdditionalWindowConfig(window.get()); applyAdditionalWindowConfig(window.get());
return std::make_shared<Game>(window); return Game::constructInstance(window);
} }
sf::ContextSettings GameFactory::getAdditionalSettings() { sf::ContextSettings GameFactory::getAdditionalSettings() {

View file

@ -152,3 +152,15 @@ void Direction::set(sf::Vector2f direction)
summedDirections = HardDirection::NONE; summedDirections = HardDirection::NONE;
this->directionVector = direction; 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);
}

View file

@ -40,6 +40,8 @@ public:
void remove(HardDirection direction); void remove(HardDirection direction);
void set(HardDirection direction); void set(HardDirection direction);
void set(sf::Vector2f direction); void set(sf::Vector2f direction);
void setX(float value);
void setY(float value);
void clear(); void clear();
private: private:

View file

@ -19,8 +19,9 @@ struct InputIdentity
unsigned int inputOrder = 0; unsigned int inputOrder = 0;
bool isActive = true; bool isActive = true;
explicit InputIdentity(InputDeviceType type) { explicit InputIdentity(InputDeviceType type, unsigned int gamepad = 0) {
deviceType = type; deviceType = type;
gamepadId = gamepad;
}; };
}; };

View file

@ -1,10 +1,8 @@
#include "input_mapper.h" #include "input_mapper.h"
void InputMapper::setGame(Game *game) InputMapper::InputMapper()
{ {
InputMapper::game = game;
// Initialize identities // Initialize identities
allIdentity = std::make_shared<InputIdentity>(InputDeviceType::ALL); allIdentity = std::make_shared<InputIdentity>(InputDeviceType::ALL);
keyboardIdentity = std::make_shared<InputIdentity>(InputDeviceType::KEYBOARD); keyboardIdentity = std::make_shared<InputIdentity>(InputDeviceType::KEYBOARD);
@ -15,7 +13,7 @@ void InputMapper::setGame(Game *game)
void InputMapper::processEvents() void InputMapper::processEvents()
{ {
sf::Event event{}; sf::Event event{};
while (game->window->pollEvent(event)) while (Game::getInstance()->window->pollEvent(event))
{ {
switch (event.type) switch (event.type)
{ {
@ -26,13 +24,19 @@ void InputMapper::processEvents()
handleKeyRelease(event.key); handleKeyRelease(event.key);
break; break;
case sf::Event::Closed: case sf::Event::Closed:
game->exit(); Game::getInstance()->exit();
break; break;
case sf::Event::Resized: case sf::Event::Resized:
break; break;
case sf::Event::JoystickMoved: case sf::Event::JoystickMoved:
handleJoystickMovement(event.joystickMove); handleJoystickMovement(event.joystickMove);
break; break;
case sf::Event::JoystickConnected:
addGamepadIdentity(event.joystickConnect.joystickId);
break;
case sf::Event::JoystickDisconnected:
deactivateGamepadIdentity(event.joystickConnect.joystickId);
break;
default: default:
break; break;
} }
@ -44,7 +48,7 @@ void InputMapper::handleKeyPress(sf::Event::KeyEvent event)
// Close game on Escape or Q in DEV Mode // Close game on Escape or Q in DEV Mode
if (DEVELOPER_MODE && (event.code == sf::Keyboard::Escape || event.code == sf::Keyboard::Q)) if (DEVELOPER_MODE && (event.code == sf::Keyboard::Escape || event.code == sf::Keyboard::Q))
{ {
game->exit(); Game::getInstance()->exit();
return; return;
} }
@ -52,37 +56,44 @@ void InputMapper::handleKeyPress(sf::Event::KeyEvent event)
auto direction = Direction::getKeyDirection(event.code); auto direction = Direction::getKeyDirection(event.code);
if (direction != HardDirection::NONE) 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<HardDirection>(direction | directionPart);
}
return Direction(direction);
}
void InputMapper::handleKeyRelease(sf::Event::KeyEvent event) void InputMapper::handleKeyRelease(sf::Event::KeyEvent event)
{ {
// Handle directionVector // Handle directionVector
auto direction = Direction::getKeyDirection(event.code); auto direction = Direction::getKeyDirection(event.code);
if (direction != HardDirection::NONE) if (direction != HardDirection::NONE)
{ {
// Remove directionVector from buffer getInputIdentity(InputDeviceType::KEYBOARD)->direction.remove(direction);
inputDirectionBuffer.erase(std::remove(inputDirectionBuffer.begin(), inputDirectionBuffer.end(), direction),
inputDirectionBuffer.end());
} }
} }
void InputMapper::handleJoystickMovement(sf::Event::JoystickMoveEvent event) 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<InputIdentity> InputMapper::getInputIdentity(InputDeviceType deviceType, unsigned int gamepadId) std::shared_ptr<InputIdentity> InputMapper::getInputIdentity(InputDeviceType deviceType, unsigned int gamepadId)
@ -92,12 +103,58 @@ std::shared_ptr<InputIdentity> InputMapper::getInputIdentity(InputDeviceType dev
case InputDeviceType::KEYBOARD: case InputDeviceType::KEYBOARD:
return keyboardIdentity; return keyboardIdentity;
case InputDeviceType::GAMEPAD: 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: 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<InputIdentity>(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> InputMapper::getInstance()
{
if (singletonInstance == nullptr) {
singletonInstance = std::make_shared<InputMapper>();
}
return singletonInstance;
}
std::vector<std::shared_ptr<InputIdentity>> InputMapper::getAllInputIdentities()
{
std::vector<std::shared_ptr<InputIdentity>> allIdentities;
allIdentities.push_back(allIdentity);
allIdentities.push_back(keyboardIdentity);
for (auto const& [key, val] : gamepadIdentities)
{
allIdentities.push_back(val);
}
return allIdentities;
}

View file

@ -7,34 +7,37 @@
#include "direction.h" #include "direction.h"
#include "input_identity.h" #include "input_identity.h"
class Game;
/** /**
* Maps the inputs to actions. * Maps the inputs to actions.
*/ */
class InputMapper class InputMapper
{ {
public: public:
static void setGame(Game *game); InputMapper();
static std::shared_ptr<InputMapper> getInstance();
static void processEvents(); void processEvents();
static std::shared_ptr<InputIdentity> getInputIdentity(InputDeviceType deviceType, unsigned int gamepadId = 0); std::shared_ptr<InputIdentity> getInputIdentity(InputDeviceType deviceType, unsigned int gamepadId = 0);
std::vector<std::shared_ptr<InputIdentity>> getAllInputIdentities();
private: private:
static inline Game *game = nullptr; static inline std::shared_ptr<InputMapper> singletonInstance = nullptr;
static std::shared_ptr<InputIdentity> allIdentity; std::shared_ptr<InputIdentity> allIdentity;
static std::shared_ptr<InputIdentity> keyboardIdentity; std::shared_ptr<InputIdentity> keyboardIdentity;
static std::map<unsigned int, std::shared_ptr<InputIdentity>> gamepadIdentities; std::map<unsigned int, std::shared_ptr<InputIdentity>> gamepadIdentities;
static inline std::vector<HardDirection> inputDirectionBuffer = std::vector<HardDirection>(); 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);
}; };

View file

@ -13,7 +13,23 @@ sf::Vector2f Player::getTrackableSize() const
void Player::update(Game *game) void Player::update(Game *game)
{ {
auto moveDirection = InputMapper::getInputDirection().asVector(); // Get gamepad input
std::shared_ptr<InputIdentity> 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(); auto moveDelta = moveDirection * 30.0f * FRAME_TIME.asSeconds();
coordinates.move(moveDelta); coordinates.move(moveDelta);
circle->coordinates.set(coordinates); circle->coordinates.set(coordinates);

View file

@ -71,7 +71,7 @@ sf::Vector2f WorldView::getCenter() const
void WorldView::followTarget() void WorldView::followTarget()
{ {
// TODO
auto closestPositionInDynamic = getClosestPositionInArea(dynamicFollowArea); auto closestPositionInDynamic = getClosestPositionInArea(dynamicFollowArea);
marker->coordinates.set(IsometricCoordinates(closestPositionInDynamic)); marker->coordinates.set(IsometricCoordinates(closestPositionInDynamic));
return; return;

View file

@ -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(); /// @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
/// @see el::base::PerformanceTracker::checkpoint /// @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 ? \ #define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? \
new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr )
#define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true) #define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true)