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/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)

View file

@ -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

View file

@ -7,7 +7,6 @@
Game::Game(std::shared_ptr<sf::RenderWindow> 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> 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
{
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);
~Game();
@ -27,6 +29,7 @@ public:
std::shared_ptr<sf::RenderWindow> window;
private:
static inline std::shared_ptr<Game> singletonInstance = nullptr;
std::vector<GameObject *> gameObjects;
void drawFrame();

View file

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

View file

@ -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);
}

View file

@ -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:

View file

@ -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;
};
};

View file

@ -1,10 +1,8 @@
#include "input_mapper.h"
void InputMapper::setGame(Game *game)
InputMapper::InputMapper()
{
InputMapper::game = game;
// Initialize identities
allIdentity = std::make_shared<InputIdentity>(InputDeviceType::ALL);
keyboardIdentity = std::make_shared<InputIdentity>(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<HardDirection>(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<InputIdentity> InputMapper::getInputIdentity(InputDeviceType deviceType, unsigned int gamepadId)
@ -92,12 +103,58 @@ std::shared_ptr<InputIdentity> 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<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 "input_identity.h"
class Game;
/**
* Maps the inputs to actions.
*/
class InputMapper
{
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:
static inline Game *game = nullptr;
static inline std::shared_ptr<InputMapper> singletonInstance = nullptr;
static std::shared_ptr<InputIdentity> allIdentity;
static std::shared_ptr<InputIdentity> keyboardIdentity;
static std::map<unsigned int, std::shared_ptr<InputIdentity>> gamepadIdentities;
std::shared_ptr<InputIdentity> allIdentity;
std::shared_ptr<InputIdentity> keyboardIdentity;
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)
{
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();
coordinates.move(moveDelta);
circle->coordinates.set(coordinates);

View file

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