mirror of
https://github.com/Nimac0/SDL_Minigame
synced 2026-01-12 13:43:41 +00:00
feat/ref: Interaction and Event management
This commit is contained in:
parent
adaed679af
commit
3c5d56de6b
@ -102,7 +102,7 @@ public:
|
|||||||
struct InputAction {
|
struct InputAction {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<Key> bindings;
|
std::vector<Key> bindings;
|
||||||
std::function<void()> callback;
|
std::function<void(bool)> callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
InputManager();
|
InputManager();
|
||||||
@ -110,28 +110,26 @@ public:
|
|||||||
|
|
||||||
void init(); // see if necessary
|
void init(); // see if necessary
|
||||||
void processEvents();
|
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);
|
void setActiveContext(const std::string& context);
|
||||||
std::string getActiveContext() const;
|
std::string getActiveContext() const;
|
||||||
|
|
||||||
void rebindAction(const std::string& actionName, const std::vector<Key>& newBindings, const std::string& context);
|
//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);
|
//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<Key> getBindings(const std::string& actionName, const std::string& context) const;
|
||||||
std::vector<InputAction*> getActionsByKey(const Key key) const;
|
std::vector<InputAction*> getActionsByKey(const Key key) const;
|
||||||
|
|
||||||
SDL_AppResult handleEvent(SDL_EventType type, SDL_Event* const event);
|
SDL_AppResult handleEvent(SDL_EventType type, SDL_Event* const event);
|
||||||
|
|
||||||
|
void initKeyMap();
|
||||||
private:
|
private:
|
||||||
// TODO: flesh this out to avoid loops in process actions
|
// TODO: flesh this out to avoid loops in process actions
|
||||||
// additionally to actionsByContext, not instead
|
// additionally to actionsByContext, not instead
|
||||||
std::map<std::string, std::vector<InputAction>> actionsByContext;
|
std::map<std::string, std::map<Key, std::vector<InputAction*>>> actionsByContextAndKey;
|
||||||
std::map<Key, std::vector<InputAction*>> actionsByKey;
|
|
||||||
|
|
||||||
std::map<Key, SDL_Scancode> keyMap;
|
std::map<Key, SDL_Scancode> keyMap;
|
||||||
std::string activeContext;
|
std::string activeContext;
|
||||||
|
|
||||||
void initKeyMap();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, InputManager::Key key);
|
std::ostream& operator<<(std::ostream& os, InputManager::Key key);
|
||||||
|
|||||||
28
include/InteractionComponent.h
Normal file
28
include/InteractionComponent.h
Normal 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;
|
||||||
|
};
|
||||||
@ -2,14 +2,27 @@
|
|||||||
|
|
||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
#include "InteractionListener.h"
|
#include "InteractionListener.h"
|
||||||
|
#include "InteractionManager.h"
|
||||||
#include "Vector2D.h"
|
#include "Vector2D.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#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 {
|
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;
|
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::weak_ptr<InteractionListener> target = std::weak_ptr<InteractionListener>();
|
||||||
std::shared_ptr<Vector2D> targetingReference; // required without explicit target
|
/// Coordinates from which to base targeting on. Is required if strategy is not set to 0 (none)
|
||||||
uint8_t strategy = 0; // required without explicit target, defaults to 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();
|
||||||
};
|
};
|
||||||
@ -12,11 +12,11 @@ Uint32 vego::VEGO_Event_Interaction;
|
|||||||
|
|
||||||
EventManager::EventManager()
|
EventManager::EventManager()
|
||||||
{
|
{
|
||||||
|
|
||||||
/// \TODO: from c++26 you (should be able to) can get the amount of name values in an enum
|
/// \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
|
vego::VEGO_Event_Interaction = SDL_RegisterEvents(1); // TODO: error handling
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EventManager::registerListener(EventListener listener, std::initializer_list<Uint32> eventTypes)
|
void EventManager::registerListener(EventListener listener, std::initializer_list<Uint32> eventTypes)
|
||||||
{
|
{
|
||||||
std::ranges::for_each(eventTypes.begin(), eventTypes.end(), [this, &listener](const Uint32& eventType) {
|
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;
|
SDL_EventType type = (SDL_EventType) event->type;
|
||||||
if (this->eventListeners.contains(type)) {
|
if (this->eventListeners.contains(type)) {
|
||||||
auto results = this->eventListeners.at(type) | std::views::transform(
|
std::vector<SDL_AppResult> results;
|
||||||
[&event](EventListener listener) {
|
for (auto& listener : this->eventListeners.at(type)) {
|
||||||
return listener((SDL_EventType) event->type, event);
|
results.emplace_back(listener((SDL_EventType) event->type, event));
|
||||||
});
|
}
|
||||||
if (std::ranges::contains(results, SDL_APP_FAILURE))
|
if (std::ranges::contains(results, SDL_APP_FAILURE))
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
if (std::ranges::contains(results, SDL_APP_SUCCESS))
|
if (std::ranges::contains(results, SDL_APP_SUCCESS))
|
||||||
|
|||||||
@ -50,6 +50,7 @@ SDL_AppResult GameInternal::init()
|
|||||||
GameInternal::soundManager = new SoundManager();
|
GameInternal::soundManager = new SoundManager();
|
||||||
GameInternal::collisionHandler = new CollisionHandler(manager); // why does this use a referrence, but AssetManager a pointer?
|
GameInternal::collisionHandler = new CollisionHandler(manager); // why does this use a referrence, but AssetManager a pointer?
|
||||||
GameInternal::inputManager = new InputManager();
|
GameInternal::inputManager = new InputManager();
|
||||||
|
GameInternal::inputManager->initKeyMap();
|
||||||
|
|
||||||
GameInternal::renderManager = new RenderManager();
|
GameInternal::renderManager = new RenderManager();
|
||||||
GameInternal::eventManager = new EventManager();
|
GameInternal::eventManager = new EventManager();
|
||||||
|
|||||||
@ -101,12 +101,8 @@ std::ostream& operator<<(std::ostream& os, InputManager::Key key) {
|
|||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const InputManager::InputAction& action) {
|
std::ostream& operator<<(std::ostream& os, const InputManager::InputAction& action) {
|
||||||
os << action.name << " with binding(s): ";
|
os << action.name << " with binding(s): ";
|
||||||
for (size_t i = 0; i < action.bindings.size(); ++i) {
|
for (auto& binding : action.bindings) {
|
||||||
os << action.bindings[i];
|
os << binding << ", ";
|
||||||
|
|
||||||
if (i < action.bindings.size() - 1) {
|
|
||||||
os << ", ";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
@ -168,13 +164,6 @@ InputManager::InputManager() : activeContext("Default") {}
|
|||||||
|
|
||||||
InputManager::~InputManager() {}
|
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() {
|
void InputManager::initKeyMap() {
|
||||||
keyMap = {
|
keyMap = {
|
||||||
{Key::UP, SDL_SCANCODE_UP},
|
{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) {
|
void InputManager::registerAction(const std::string& actionName, const std::vector<Key>& keys, std::function<void(bool)> callback, const std::string& context) {
|
||||||
actionsByContext[context].emplace_back(actionName, keys, callback);
|
InputAction* storedAction = new InputAction{actionName, keys, callback};
|
||||||
InputAction& storedAction = actionsByContext[context].back();
|
|
||||||
for (const auto& key : keys) {
|
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;
|
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 {
|
std::vector<InputManager::InputAction*> InputManager::getActionsByKey(const Key key) const {
|
||||||
auto it = actionsByKey.find(key);
|
std::vector<InputAction*> result;
|
||||||
if (it != actionsByKey.end()) {
|
|
||||||
std::cout << "DEBUG: Found " << it->second.size() << " actions for key " << key << std::endl;
|
for (const auto& [context, keyMap] : actionsByContextAndKey) {
|
||||||
return it->second;
|
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) {
|
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) {
|
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) {
|
if (event->key.repeat) {
|
||||||
return SDL_APP_CONTINUE;
|
return SDL_APP_CONTINUE;
|
||||||
}
|
}
|
||||||
|
auto keyIt = std::ranges::find_if(keyMap, [&](const auto& pair)
|
||||||
auto keyIt = std::ranges::find_if(keyMap,
|
{ return pair.second == event->key.scancode; });
|
||||||
[&](const auto& pair) { return pair.second == event->key.scancode; });
|
|
||||||
|
|
||||||
if (keyIt != keyMap.end()) {
|
if (keyIt != keyMap.end()) {
|
||||||
std::cout << "in != keymap.end" << std::endl;
|
std::cout << "in != keymap.end" << std::endl;
|
||||||
|
|
||||||
Key pressedKey = keyIt->first;
|
Key pressedKey = keyIt->first;
|
||||||
auto actionIt = actionsByKey.find(pressedKey);
|
auto keyActions = actionsByContextAndKey[activeContext];
|
||||||
if (actionIt != actionsByKey.end()) {
|
auto it = keyActions.find(pressedKey);
|
||||||
std::cout << "in != actionsByKey.end" << std::endl;
|
if (it != keyActions.end()) {
|
||||||
|
for (auto& action : it->second) {
|
||||||
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()) {
|
|
||||||
std::cout << "Action triggered: " << action->name << " in context: " << activeContext << std::endl;
|
std::cout << "Action triggered: " << action->name << " in context: " << activeContext << std::endl;
|
||||||
action->callback();
|
action->callback(type == SDL_EVENT_KEY_UP);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/InteractionComponent.cpp
Normal file
22
src/InteractionComponent.cpp
Normal 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;
|
||||||
|
}
|
||||||
16
src/InteractionEventdataStruct.cpp
Normal file
16
src/InteractionEventdataStruct.cpp
Normal 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);
|
||||||
|
}
|
||||||
@ -42,6 +42,9 @@ void TransformComponent::init()
|
|||||||
|
|
||||||
void TransformComponent::update(uint_fast16_t diffTime)
|
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°
|
float multiplier = direction.x != 0 && direction.y != 0 ? 0.707 : 1; // normalizes vector; only works if directions are in increments of 45°
|
||||||
Vector2D positionChange(
|
Vector2D positionChange(
|
||||||
direction.x * this->getSpeed() * multiplier * diffTime * (1.f/1000),
|
direction.x * this->getSpeed() * multiplier * diffTime * (1.f/1000),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user