0
0
mirror of https://github.com/Nimac0/SDL_Minigame synced 2026-01-12 07:53:43 +00:00

feat/ref: Interaction and Event management

This commit is contained in:
Nimac0 2025-04-09 21:06:34 +02:00
parent adaed679af
commit 3c5d56de6b
9 changed files with 120 additions and 121 deletions

View File

@ -102,7 +102,7 @@ public:
struct InputAction {
std::string name;
std::vector<Key> bindings;
std::function<void()> callback;
std::function<void(bool)> callback;
};
InputManager();
@ -110,28 +110,26 @@ public:
void init(); // see if necessary
void processEvents();
void registerAction(const std::string& actionName, const std::vector<Key>& keys, std::function<void()> callback, const std::string& context);
void registerAction(const std::string& actionName, const std::vector<Key>& keys, std::function<void(bool)> callback, const std::string& context);
void setActiveContext(const std::string& context);
std::string getActiveContext() const;
void rebindAction(const std::string& actionName, const std::vector<Key>& newBindings, const std::string& context);
void removeBindings(const std::string& actionName, const std::string& context);
std::vector<Key> getBindings(const std::string& actionName, const std::string& context) const;
//void rebindAction(const std::string& actionName, const std::vector<Key>& newBindings, const std::string& context);
//void removeBindings(const std::string& actionName, const std::string& context);
//std::vector<Key> getBindings(const std::string& actionName, const std::string& context) const;
std::vector<InputAction*> getActionsByKey(const Key key) const;
SDL_AppResult handleEvent(SDL_EventType type, SDL_Event* const event);
void initKeyMap();
private:
// TODO: flesh this out to avoid loops in process actions
// additionally to actionsByContext, not instead
std::map<std::string, std::vector<InputAction>> actionsByContext;
std::map<Key, std::vector<InputAction*>> actionsByKey;
std::map<std::string, std::map<Key, std::vector<InputAction*>>> actionsByContextAndKey;
std::map<Key, SDL_Scancode> keyMap;
std::string activeContext;
void initKeyMap();
};
std::ostream& operator<<(std::ostream& os, InputManager::Key key);

View File

@ -0,0 +1,28 @@
#pragma once
#include "Component.h"
#include "InteractionListener.h"
#include <functional>
class InteractionComponent : public Component, public InteractionListener
{
public:
/**
* @brief Constructor for the InteractionComponent.
* @param callback A function to be called when an interaction event is triggered. void* actor, void* data are passed to the callback function from InteractionEventdataStruct.
*/
InteractionComponent(std::function<void(void*,void*)> callback);
/**
* @brief Internal function to be called when an interaction event is triggered.
*/
void interact(void* actor, void* data) override;
/**
* @brief Internal function to use as reference for targeting.
*/
std::shared_ptr<Vector2D> getPosition() override;
private:
std::function<void(void*,void*)> interactionCallback;
};

View File

@ -2,14 +2,27 @@
#include "Entity.h"
#include "InteractionListener.h"
#include "InteractionManager.h"
#include "Vector2D.h"
#include <cstdint>
#include <memory>
/**
* @brief Struct to hold data for interaction events.
* This struct is used to pass data to the interaction manager when an interaction event is triggered.
*/
struct InteractionEventdataStruct {
void* actor; // suggestion, can also be used for other arbitrary data
/// Arbitray data to pass to the interaction listener. Can for example be an Entity ptr to represent the actor.
void* actor;
/// The data to pass to the interaction listener. Can be any type of pointer.
void* data;
/// The target of the interaction, e.g. InteractionComponent of an Entity. Is required if strategy is set to 0 (none)
std::weak_ptr<InteractionListener> target = std::weak_ptr<InteractionListener>();
std::shared_ptr<Vector2D> targetingReference; // required without explicit target
uint8_t strategy = 0; // required without explicit target, defaults to none
/// Coordinates from which to base targeting on. Is required if strategy is not set to 0 (none)
std::shared_ptr<Vector2D> targetingReference = nullptr;
/// required without explicit target, defaults to none
/// @sa InteractionManager::TargetingStrategy
uint8_t strategy = 0; // int since enum would be impossibling user defined targetingStrategies
void triggerEvent();
};

View File

@ -12,11 +12,11 @@ Uint32 vego::VEGO_Event_Interaction;
EventManager::EventManager()
{
/// \TODO: from c++26 you (should be able to) can get the amount of name values in an enum
vego::VEGO_Event_Interaction = SDL_RegisterEvents(1); // TODO: error handling
}
void EventManager::registerListener(EventListener listener, std::initializer_list<Uint32> eventTypes)
{
std::ranges::for_each(eventTypes.begin(), eventTypes.end(), [this, &listener](const Uint32& eventType) {
@ -31,10 +31,10 @@ SDL_AppResult EventManager::handleEvent(SDL_Event* event)
{
SDL_EventType type = (SDL_EventType) event->type;
if (this->eventListeners.contains(type)) {
auto results = this->eventListeners.at(type) | std::views::transform(
[&event](EventListener listener) {
return listener((SDL_EventType) event->type, event);
});
std::vector<SDL_AppResult> results;
for (auto& listener : this->eventListeners.at(type)) {
results.emplace_back(listener((SDL_EventType) event->type, event));
}
if (std::ranges::contains(results, SDL_APP_FAILURE))
return SDL_APP_FAILURE;
if (std::ranges::contains(results, SDL_APP_SUCCESS))

View File

@ -50,6 +50,7 @@ SDL_AppResult GameInternal::init()
GameInternal::soundManager = new SoundManager();
GameInternal::collisionHandler = new CollisionHandler(manager); // why does this use a referrence, but AssetManager a pointer?
GameInternal::inputManager = new InputManager();
GameInternal::inputManager->initKeyMap();
GameInternal::renderManager = new RenderManager();
GameInternal::eventManager = new EventManager();

View File

@ -101,12 +101,8 @@ std::ostream& operator<<(std::ostream& os, InputManager::Key key) {
std::ostream& operator<<(std::ostream& os, const InputManager::InputAction& action) {
os << action.name << " with binding(s): ";
for (size_t i = 0; i < action.bindings.size(); ++i) {
os << action.bindings[i];
if (i < action.bindings.size() - 1) {
os << ", ";
}
for (auto& binding : action.bindings) {
os << binding << ", ";
}
return os;
}
@ -168,13 +164,6 @@ InputManager::InputManager() : activeContext("Default") {}
InputManager::~InputManager() {}
void InputManager::init() {
if (SDL_Init(SDL_INIT_EVENTS) != 0) {
std::cerr << "Failed to initialize SDL: " << SDL_GetError() << std::endl;
return;
}
}
void InputManager::initKeyMap() {
keyMap = {
{Key::UP, SDL_SCANCODE_UP},
@ -259,81 +248,25 @@ void InputManager::initKeyMap() {
};
}
void InputManager::registerAction(const std::string& actionName, const std::vector<Key>& keys, std::function<void()> callback, const std::string& context) {
actionsByContext[context].emplace_back(actionName, keys, callback);
InputAction& storedAction = actionsByContext[context].back();
void InputManager::registerAction(const std::string& actionName, const std::vector<Key>& keys, std::function<void(bool)> callback, const std::string& context) {
InputAction* storedAction = new InputAction{actionName, keys, callback};
for (const auto& key : keys) {
actionsByKey[key].push_back(&storedAction);
actionsByContextAndKey[context][key].emplace_back(storedAction);
}
std::cout << "Registered action: " << storedAction << " in context: " << context << std::endl;
}
void InputManager::rebindAction(const std::string& actionName, const std::vector<Key>& newBindings, const std::string& context) {
auto it = actionsByContext.find(context);
if (it != actionsByContext.end()) {
for (auto& action : it->second) {
if (action.name == actionName) {
for (const auto& key : action.bindings) {
auto& keyActions = actionsByKey[key];
keyActions.erase(std::remove(keyActions.begin(), keyActions.end(), &action), keyActions.end());
if (keyActions.empty()) {
actionsByKey.erase(key);
}
}
action.bindings = newBindings;
for (const auto& key : newBindings) {
actionsByKey[key].push_back(&action);
}
std::cout << "Rebound action: " << actionName << " in context: " << context << " to new bindings: ";
for (const auto& key : newBindings) {
std::cout << key << " ";
}
std::cout << std::endl;
return;
}
}
}
std::cout << "Action not found: " << actionName << " in context: " << context << std::endl;
}
void InputManager::removeBindings(const std::string& actionName, const std::string& context) {
auto it = actionsByContext.find(context);
if (it != actionsByContext.end()) {
for (auto& action : it->second) {
if (action.name == actionName) {
action.bindings.clear();
std::cout << "Removed bindings for action: " << actionName << " in context: " << context << std::endl;
return;
}
}
}
std::cout << "Action not found: " << actionName << " in context: " << context << std::endl;
}
std::vector<InputManager::Key> InputManager::getBindings(const std::string& actionName, const std::string& context) const {
auto it = actionsByContext.find(context);
if (it != actionsByContext.end()) {
for (const auto& action : it->second) {
if (action.name == actionName) {
return action.bindings;
}
}
}
std::cout << "Action not found: " << actionName << " in context: " << context << "\n";
return {};
}
std::vector<InputManager::InputAction*> InputManager::getActionsByKey(const Key key) const {
auto it = actionsByKey.find(key);
if (it != actionsByKey.end()) {
std::cout << "DEBUG: Found " << it->second.size() << " actions for key " << key << std::endl;
return it->second;
std::vector<InputAction*> result;
for (const auto& [context, keyMap] : actionsByContextAndKey) {
auto it = keyMap.find(key);
if (it != keyMap.end()) {
result.insert(result.end(), it->second.begin(), it->second.end());
}
std::cout << "DEBUG: No actions found for key " << key << std::endl;
return {};
}
return result;
}
void InputManager::setActiveContext(const std::string& context) {
@ -346,37 +279,22 @@ std::string InputManager::getActiveContext() const {
}
SDL_AppResult InputManager::handleEvent(SDL_EventType type, SDL_Event* const event) {
if (type != SDL_EVENT_KEY_DOWN) {
return SDL_APP_CONTINUE;
}
if (event->key.repeat) {
return SDL_APP_CONTINUE;
}
auto keyIt = std::ranges::find_if(keyMap,
[&](const auto& pair) { return pair.second == event->key.scancode; });
auto keyIt = std::ranges::find_if(keyMap, [&](const auto& pair)
{ return pair.second == event->key.scancode; });
if (keyIt != keyMap.end()) {
std::cout << "in != keymap.end" << std::endl;
Key pressedKey = keyIt->first;
auto actionIt = actionsByKey.find(pressedKey);
if (actionIt != actionsByKey.end()) {
std::cout << "in != actionsByKey.end" << std::endl;
for (auto* action : actionIt->second) {
std::cout << "before if(action)" << std::endl;
if (action) {
std::cout << "after if(action)" << std::endl;
auto& activeActions = actionsByContext[activeContext];
if (std::ranges::find_if(activeActions,
[&](const InputAction& act) { return &act == action; }) != activeActions.end()) {
auto keyActions = actionsByContextAndKey[activeContext];
auto it = keyActions.find(pressedKey);
if (it != keyActions.end()) {
for (auto& action : it->second) {
std::cout << "Action triggered: " << action->name << " in context: " << activeContext << std::endl;
action->callback();
}
}
action->callback(type == SDL_EVENT_KEY_UP);
}
}
}

View File

@ -0,0 +1,22 @@
#include "InteractionComponent.h"
#include "VEGO.h"
InteractionComponent::InteractionComponent(std::function<void(void*,void*)> callback) : interactionCallback(callback)
{
VEGO_Game().interactionManager->registerListener(this->entity->getComponentAsPointer<InteractionComponent>());
}
void InteractionComponent::interact(void* actor, void* data)
{
if (interactionCallback) {
interactionCallback(actor, data);
}
}
std::shared_ptr<Vector2D> InteractionComponent::getPosition() // required for targeting strategy, return null to only allow explicit targeting
{
if (entity->hasComponent<TransformComponent>()) {
return std::make_shared<Vector2D>(entity->getComponent<TransformComponent>().position);
}
return nullptr;
}

View File

@ -0,0 +1,16 @@
#include "InteractionEventdataStruct.h"
#include "VEGO.h"
#include "VEGO_Event.h"
void InteractionEventdataStruct::triggerEvent()
{
// TODO: if target is null && strategy is 0, error
SDL_Event event;
SDL_zero(event);
event.type = vego::VEGO_Event_Interaction;
event.user.data1 = this;
SDL_PushEvent(&event);
}

View File

@ -42,6 +42,9 @@ void TransformComponent::init()
void TransformComponent::update(uint_fast16_t diffTime)
{
direction.x = direction.x > 0 ? 1 : direction.x < 0 ? -1 : 0;
direction.y = direction.y > 0 ? 1 : direction.y < 0 ? -1 : 0;
float multiplier = direction.x != 0 && direction.y != 0 ? 0.707 : 1; // normalizes vector; only works if directions are in increments of 45°
Vector2D positionChange(
direction.x * this->getSpeed() * multiplier * diffTime * (1.f/1000),