0
0
mirror of https://github.com/Nimac0/SDL_Minigame synced 2026-01-12 09:03:42 +00:00

InteractionManager + proof of concept

This commit is contained in:
Benedikt Galbavy 2025-03-22 14:38:26 +01:00
parent a9e754dd4f
commit adaed679af
23 changed files with 339 additions and 125 deletions

View File

@ -92,8 +92,8 @@ public:
{ {
T* c(new T(std::forward<TArgs>(mArgs)...)); T* c(new T(std::forward<TArgs>(mArgs)...));
c->entity = this; c->entity = this;
std::unique_ptr<Component> uPtr{ c }; std::shared_ptr<Component> uPtr{ c };
this->components.emplace_back(std::move(uPtr)); this->components.at(getComponentTypeID<T>()) = std::move(uPtr);
componentArray[getComponentTypeID<T>()] = c; componentArray[getComponentTypeID<T>()] = c;
componentBitSet[getComponentTypeID<T>()] = true; componentBitSet[getComponentTypeID<T>()] = true;
@ -108,10 +108,15 @@ public:
return *static_cast<T*>(ptr); return *static_cast<T*>(ptr);
} }
template <typename T> std::shared_ptr<T> getComponentAsPointer() const
{
return std::static_pointer_cast<T>(components.at(getComponentTypeID<T>()));
}
private: private:
Manager& manager; Manager& manager;
bool active = true; bool active = true;
std::vector<std::unique_ptr<Component>> components; std::array<std::shared_ptr<Component>, MAX_COMPONENTS> components;
ComponentArray componentArray = {}; ComponentArray componentArray = {};
ComponentBitSet componentBitSet; ComponentBitSet componentBitSet;

View File

@ -1,11 +0,0 @@
#pragma once
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_events.h>
class EventListener {
public:
virtual SDL_AppResult handleEvent(SDL_EventType type, SDL_Event* const event) = 0;
EventListener() {};
virtual ~EventListener() {};
};

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "EventListener.h" #include <functional>
#include <initializer_list> #include <initializer_list>
#include <map> #include <map>
#include <vector> #include <vector>
@ -9,10 +8,14 @@
#include "SDL3/SDL_events.h" #include "SDL3/SDL_events.h"
#include "SDL3/SDL_init.h" #include "SDL3/SDL_init.h"
typedef std::function<SDL_AppResult(SDL_EventType, SDL_Event* const)> EventListener;
class EventManager { class EventManager {
public: public:
void registerListener(EventListener* listener, std::initializer_list<SDL_EventType> eventTypes); EventManager();
void registerListener(EventListener listener, std::initializer_list<Uint32> eventTypes);
SDL_AppResult handleEvent(SDL_Event* const event); SDL_AppResult handleEvent(SDL_Event* const event);
private: private:
std::map<SDL_EventType, std::vector<EventListener*>> eventListeners = std::map<SDL_EventType, std::vector<EventListener*>>(); std::map<Uint32, std::vector<EventListener>> eventListeners = std::map<Uint32, std::vector<EventListener>>();
}; };

View File

@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include "EventManager.h" #include "EventManager.h"
#include "InteractionManager.h"
#include "Manager.h" #include "Manager.h"
#include "SDL3/SDL_events.h" #include "SDL3/SDL_events.h"
#include "SDL3/SDL_init.h" #include "SDL3/SDL_init.h"
@ -49,10 +50,11 @@ public:
/* static */ TextureManager* textureManager; /* static */ TextureManager* textureManager;
/* static */ SoundManager* soundManager; /* static */ SoundManager* soundManager;
/* static */ InputManager* inputManager; /* static */ InputManager* inputManager;
RenderManager* renderManager;
EventManager* eventManager;
InteractionManager* interactionManager;
Manager manager; Manager manager;
RenderManager renderManager;
EventManager eventManager;
Map* map; // game specific, might not be needed for all types of games Map* map; // game specific, might not be needed for all types of games
ConfigLoader* config; ConfigLoader* config;

View File

@ -1,8 +1,7 @@
#pragma once #pragma once
#include "EventListener.h" #include <SDL3/SDL_events.h>
#include "SDL3/SDL_events.h" #include <SDL3/SDL_init.h>
#include "SDL3/SDL_init.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <map> #include <map>
#include <string> #include <string>
@ -10,7 +9,7 @@
#include <vector> #include <vector>
#include <iostream> #include <iostream>
class InputManager : public EventListener { class InputManager {
public: public:
enum class EventType { enum class EventType {
KeyDown, KeyDown,
@ -121,6 +120,8 @@ public:
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);
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
@ -131,7 +132,6 @@ private:
std::string activeContext; std::string activeContext;
void initKeyMap(); void initKeyMap();
SDL_AppResult handleEvent(SDL_EventType type, SDL_Event* const event) override;
}; };
std::ostream& operator<<(std::ostream& os, InputManager::Key key); std::ostream& operator<<(std::ostream& os, InputManager::Key key);

View File

@ -0,0 +1,15 @@
#pragma once
#include "Entity.h"
#include "InteractionListener.h"
#include "Vector2D.h"
#include <cstdint>
#include <memory>
struct InteractionEventdataStruct {
void* actor; // suggestion, can also be used for other arbitrary data
void* data;
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
};

View File

@ -0,0 +1,17 @@
#pragma once
#include "Vector2D.h"
#include <memory>
class InteractionListener {
public:
InteractionListener() { };
virtual ~InteractionListener() { };
virtual void interact(void* actor, void* data) = 0;
virtual std::shared_ptr<Vector2D> getPosition() // required for targeting strategy, return null to only allow explicit targeting
{
return nullptr;
}
};

View File

@ -0,0 +1,35 @@
#pragma once
#include "InteractionListener.h"
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_init.h"
#include <cstdint>
#include <functional>
#include <memory>
#include <vector>
#include <ranges>
// TODO: ranges concept to avoid to<vector> in cpp
typedef std::function<std::shared_ptr<InteractionListener>(Vector2D*, std::vector<std::shared_ptr<InteractionListener>>)> TargetingFunc;
class InteractionManager {
public:
InteractionManager();
InteractionManager (const InteractionManager&) = delete;
InteractionManager& operator= (const InteractionManager&) = delete;
enum class TargetingStrategy : uint8_t {
none = 0,
closest,
manhattenDistance
};
SDL_AppResult handleInteract(SDL_EventType type, SDL_Event* const event);
void registerListener(std::weak_ptr<InteractionListener> listener);
uint8_t registerTargetingFunc(TargetingFunc func);
private:
std::vector<std::weak_ptr<InteractionListener>> listeners;
std::array<TargetingFunc, 256> targetingFuncs;
};

View File

@ -2,8 +2,9 @@
#include <functional> #include <functional>
#include "Component.h" #include "Component.h"
#include "InteractionListener.h"
class PowerupComponent : public Component class PowerupComponent : public Component, public InteractionListener
{ {
public: public:
/** /**
@ -15,6 +16,10 @@ public:
void update(uint_fast16_t diffTime) override; void update(uint_fast16_t diffTime) override;
void interact(void* actor, void* data) override;
std::shared_ptr<Vector2D> getPosition() override;
private: private:
std::function<void (Entity*)> pickupFunc; std::function<void (Entity*)> pickupFunc;
}; };

View File

@ -7,7 +7,7 @@ class RenderObject
public: public:
virtual void draw() = 0; virtual void draw() = 0;
RenderObject(int zIndex, RenderManager& renderManager); RenderObject(int zIndex, RenderManager* renderManager);
~RenderObject(); ~RenderObject();
int getZIndex() { return this->zIndex; }; int getZIndex() { return this->zIndex; };
@ -23,5 +23,5 @@ private:
int zIndex = 0; int zIndex = 0;
protected: protected:
RenderManager& renderManager; RenderManager* renderManager;
}; };

View File

@ -40,6 +40,11 @@ private:
const char* path; //!< empty string if texture has a texture enum value, otherwise the path of the texture const char* path; //!< empty string if texture has a texture enum value, otherwise the path of the texture
public: public:
//debug
Textures getTexture() { return this->textureEnum; }
SpriteComponent(Textures texture, int zIndex); SpriteComponent(Textures texture, int zIndex);
SpriteComponent(Textures texture, int xOffset, int yOffset, int zIndex); SpriteComponent(Textures texture, int xOffset, int yOffset, int zIndex);
SpriteComponent(const char* path, int xOffset, int yOffset, int zIndex); SpriteComponent(const char* path, int xOffset, int yOffset, int zIndex);

View File

@ -83,9 +83,15 @@ class TextureManager
*/ */
SDL_Texture* loadMapTileTexture(const char* path); SDL_Texture* loadMapTileTexture(const char* path);
std::string getTexturePath(Textures texture) {
return this->texture_references.at(texture);
}
private: private:
SDL_ScaleMode scaleMode = SDL_SCALEMODE_NEAREST; SDL_ScaleMode scaleMode = SDL_SCALEMODE_NEAREST;
Manager* manager; Manager* manager;
std::map<Textures, SDL_Texture*> texture_cache; std::map<Textures, SDL_Texture*> texture_cache;
std::map<std::string, SDL_Texture*> mapTile_texture_cache; std::map<std::string, SDL_Texture*> mapTile_texture_cache;
std::map<Textures, std::string> texture_references;
}; };

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
enum VEGO_Event { #include <SDL3/SDL_stdinc.h>
VEGO_Event_Interaction = SDL_EVENT_USER
}; namespace vego {
extern Uint32 VEGO_Event_Interaction;
}

View File

@ -37,6 +37,8 @@ void AssetManager::createPowerup(Vector2D pos, std::function<void (Entity*)> pic
powerups.addComponent<ColliderComponent>("powerup", 0.6f); powerups.addComponent<ColliderComponent>("powerup", 0.6f);
powerups.addComponent<PowerupComponent>(pickupFunc); powerups.addComponent<PowerupComponent>(pickupFunc);
powerups.addGroup((size_t)Entity::GroupLabel::POWERUPS); powerups.addGroup((size_t)Entity::GroupLabel::POWERUPS);
VEGO_Game().interactionManager->registerListener(powerups.getComponentAsPointer<PowerupComponent>());
} }
Vector2D AssetManager::calculateSpawnPosition() Vector2D AssetManager::calculateSpawnPosition()

View File

@ -6,7 +6,9 @@
void Entity::update(uint_fast16_t diffTime) const void Entity::update(uint_fast16_t diffTime) const
{ {
for (auto const& c : components) c->update(diffTime); for (auto const& c : components)
if (c)
c->update(diffTime);
} }
bool Entity::hasGroup(Group mGroup) bool Entity::hasGroup(Group mGroup)

View File

@ -1,16 +1,27 @@
#include "EventManager.h" #include "EventManager.h"
#include "EventListener.h"
#include "SDL3/SDL_events.h" #include "SDL3/SDL_events.h"
#include "SDL3/SDL_init.h" #include "SDL3/SDL_init.h"
#include "SDL3/SDL_stdinc.h"
#include "VEGO_Event.h"
#include <algorithm> #include <algorithm>
#include <functional>
#include <ranges> #include <ranges>
#include <vector> #include <vector>
void EventManager::registerListener(EventListener* listener, std::initializer_list<SDL_EventType> eventTypes) Uint32 vego::VEGO_Event_Interaction;
EventManager::EventManager()
{ {
std::ranges::for_each(eventTypes.begin(), eventTypes.end(), [this, &listener](const SDL_EventType& eventType) {
/// \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) {
if (!this->eventListeners.contains(eventType)) { if (!this->eventListeners.contains(eventType)) {
this->eventListeners.insert({eventType, std::vector<EventListener*>()}); this->eventListeners.insert({eventType, std::vector<EventListener>()});
} }
this->eventListeners.at(eventType).emplace_back(listener); this->eventListeners.at(eventType).emplace_back(listener);
}); });
@ -21,8 +32,8 @@ 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( auto results = this->eventListeners.at(type) | std::views::transform(
[&event](EventListener* listener) { [&event](EventListener listener) {
return listener->handleEvent((SDL_EventType) event->type, event); return 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;

View File

@ -2,6 +2,9 @@
#include "CollisionHandler.h" #include "CollisionHandler.h"
#include "AssetManager.h" #include "AssetManager.h"
#include "EventManager.h"
#include "InputManager.h"
#include "InteractionManager.h"
#include "RenderManager.h" #include "RenderManager.h"
#include <SDL3_mixer/SDL_mixer.h> #include <SDL3_mixer/SDL_mixer.h>
#include "SDL3/SDL_events.h" #include "SDL3/SDL_events.h"
@ -17,12 +20,12 @@
#include <VEGO.h> #include <VEGO.h>
#include <VEGO_Event.h> #include <VEGO_Event.h>
#include <functional>
#include "ConfigLoader.h" #include "ConfigLoader.h"
GameInternal::GameInternal() : GameInternal::GameInternal() :
manager(this), manager(this),
renderManager(),
tiles(manager.getGroup((size_t)Entity::GroupLabel::MAPTILES)), tiles(manager.getGroup((size_t)Entity::GroupLabel::MAPTILES)),
players(manager.getGroup((size_t)Entity::GroupLabel::PLAYERS)), players(manager.getGroup((size_t)Entity::GroupLabel::PLAYERS)),
projectiles(manager.getGroup((size_t)Entity::GroupLabel::PROJECTILE)), projectiles(manager.getGroup((size_t)Entity::GroupLabel::PROJECTILE)),
@ -48,10 +51,12 @@ SDL_AppResult GameInternal::init()
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();
this->eventManager.registerListener(inputManager, { SDL_EVENT_KEY_DOWN, SDL_EVENT_KEY_UP }); GameInternal::renderManager = new RenderManager();
GameInternal::eventManager = new EventManager();
GameInternal::interactionManager = new InteractionManager();
/// \TODO: from c++26 you (should be able to) can loop through all values of an enum this->eventManager->registerListener(std::bind_front(&InputManager::handleEvent, this->inputManager), { SDL_EVENT_KEY_DOWN, SDL_EVENT_KEY_UP });
SDL_RegisterEvents(VEGO_Event_Interaction); this->eventManager->registerListener(std::bind_front(&InteractionManager::handleInteract, VEGO_Game().interactionManager), { vego::VEGO_Event_Interaction });
int flags = 0; int flags = 0;
if (finalConfig.at("fullscreen")) if (finalConfig.at("fullscreen"))
@ -122,7 +127,7 @@ SDL_AppResult GameInternal::init()
} }
SDL_AppResult GameInternal::handleEvent(SDL_Event* event) { SDL_AppResult GameInternal::handleEvent(SDL_Event* event) {
SDL_AppResult result = this->eventManager.handleEvent(event); SDL_AppResult result = this->eventManager->handleEvent(event);
if (event->type == SDL_EVENT_QUIT) { if (event->type == SDL_EVENT_QUIT) {
this->clean(); this->clean();
@ -147,7 +152,7 @@ void GameInternal::update(Uint64 frameTime)
void GameInternal::render() void GameInternal::render()
{ {
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
this->renderManager.renderAll(); this->renderManager->renderAll();
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
} }

View File

@ -1,7 +1,11 @@
#include "InputManager.h" #include "InputManager.h"
#include "InteractionEventdataStruct.h"
#include "SDL3/SDL_events.h" #include "SDL3/SDL_events.h"
#include "SDL3/SDL_init.h" #include "SDL3/SDL_init.h"
#include <iostream> #include <iostream>
#include "SDL3/SDL_stdinc.h"
#include "VEGO.h"
#include "VEGO_Event.h"
std::ostream& operator<<(std::ostream& os, InputManager::Key key) { std::ostream& operator<<(std::ostream& os, InputManager::Key key) {
static const std::unordered_map<InputManager::Key, std::string> keyToString { static const std::unordered_map<InputManager::Key, std::string> keyToString {
@ -342,14 +346,11 @@ 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) {
std::cout << "in handleEvent" << std::endl;
if (type != SDL_EVENT_KEY_DOWN) { if (type != SDL_EVENT_KEY_DOWN) {
std::cout << "ignore key up" << std::endl;
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }
if (event->key.repeat) { if (event->key.repeat) {
std::cout << "ignore key repeat" << std::endl;
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }

View File

@ -0,0 +1,90 @@
#include "InteractionManager.h"
#include "InteractionEventdataStruct.h"
#include "InteractionListener.h"
#include "SDL3/SDL_init.h"
#include "VEGO_Event.h"
#include "Vector2D.h"
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <iterator>
#include <memory>
#include <ranges>
#include <type_traits>
#include <vector>
InteractionManager::InteractionManager()
{
this->targetingFuncs.fill(nullptr);
this->targetingFuncs.at(static_cast<std::underlying_type<InteractionManager::TargetingStrategy>::type>(InteractionManager::TargetingStrategy::closest)) = [](Vector2D* reference, std::vector<std::shared_ptr<InteractionListener>> input) {
return std::shared_ptr<InteractionListener>();
};
this->targetingFuncs.at(static_cast<std::underlying_type<InteractionManager::TargetingStrategy>::type>(InteractionManager::TargetingStrategy::closest)) = [](Vector2D* reference, std::vector<std::shared_ptr<InteractionListener>> input) {
auto min = std::ranges::min_element(input, [&reference](std::shared_ptr<InteractionListener>& a, std::shared_ptr<InteractionListener>& b) {
std::shared_ptr<Vector2D> coordA = a->getPosition();
std::shared_ptr<Vector2D> coordB = b->getPosition();
if (coordB == nullptr) return true;
if (coordA == nullptr) return false;
return std::sqrt(std::pow(coordA->x - reference->x, 2) + std::pow(coordA->y - reference->y, 2)) < std::sqrt(std::pow(coordB->x - reference->x, 2) + std::pow(coordB->y - reference->y, 2));
});
return min == std::ranges::end(input) ? nullptr : *min;
};
this->targetingFuncs.at(static_cast<std::underlying_type<InteractionManager::TargetingStrategy>::type>(InteractionManager::TargetingStrategy::manhattenDistance)) = [](Vector2D* reference, std::vector<std::shared_ptr<InteractionListener>> input) {
auto min = std::ranges::min_element(input, [&reference](std::shared_ptr<InteractionListener>& a, std::shared_ptr<InteractionListener>& b) {
std::shared_ptr<Vector2D> coordA = a->getPosition();
std::shared_ptr<Vector2D> coordB = b->getPosition();
if (coordB == nullptr) return true;
if (coordA == nullptr) return false;
return (std::abs(coordA->x - reference->x) + std::abs(coordA->y - reference->y)) < (std::abs(coordB->x - reference->x) + std::abs(coordB->y - reference->y));
});
return min == std::ranges::end(input) ? nullptr : *min;
};
}
SDL_AppResult InteractionManager::handleInteract(SDL_EventType type, SDL_Event* const event)
{
if (type != vego::VEGO_Event_Interaction) { // error handling
return SDL_APP_CONTINUE;
}
InteractionEventdataStruct* data = static_cast<InteractionEventdataStruct*>(event->user.data1);
std::shared_ptr<InteractionListener> listener = data->target.lock();
if (data->strategy != static_cast<std::underlying_type<InteractionManager::TargetingStrategy>::type>(InteractionManager::TargetingStrategy::none)) {
listener = this->targetingFuncs.at(data->strategy)(
data->targetingReference.get(),
this->listeners
| std::views::transform(&std::weak_ptr<InteractionListener>::lock)
| std::views::filter(&std::shared_ptr<InteractionListener>::operator bool)
| std::ranges::to<std::vector>()
);
}
if (listener) {
listener->interact(data->actor, data->data);
}
return SDL_APP_CONTINUE;
}
void InteractionManager::registerListener(std::weak_ptr<InteractionListener> listener)
{
this->listeners.emplace_back(listener);
}
uint8_t InteractionManager::registerTargetingFunc(TargetingFunc func)
{
auto it = std::ranges::find_if(this->targetingFuncs, [](const auto& func) {
return !func;
});
(*it) = func;
return std::distance(this->targetingFuncs.begin(), it);
}

View File

@ -3,9 +3,13 @@
#include "CollisionHandler.h" #include "CollisionHandler.h"
#include "Entity.h" #include "Entity.h"
#include "HealthComponent.h" #include "HealthComponent.h"
#include "SpriteComponent.h"
#include "StatEffectsComponent.h" #include "StatEffectsComponent.h"
#include "Constants.h" #include "Constants.h"
#include "TextureManager.h"
#include "TransformComponent.h"
#include <cstdint> #include <cstdint>
#include "VEGO.h"
PowerupComponent::PowerupComponent(std::function<void (Entity*)> func) PowerupComponent::PowerupComponent(std::function<void (Entity*)> func)
{ {
@ -24,3 +28,15 @@ void PowerupComponent::update(uint_fast16_t diffTime)
this->entity->destroy(); this->entity->destroy();
} }
} }
void PowerupComponent::interact(void* actor, void* data)
{
std::cout << VEGO_Game().textureManager->getTexturePath(this->entity->getComponent<SpriteComponent>().getTexture()) << std::endl;
}
std::shared_ptr<Vector2D> PowerupComponent::getPosition()
{
return std::make_shared<Vector2D>(this->entity->getComponent<TransformComponent>().position);
}

View File

@ -1,10 +1,10 @@
#include "RenderObject.h" #include "RenderObject.h"
#include "RenderManager.h" #include "RenderManager.h"
RenderObject::RenderObject(int zIndex, RenderManager& renderManager) : zIndex(zIndex), renderManager(renderManager) { RenderObject::RenderObject(int zIndex, RenderManager* renderManager) : zIndex(zIndex), renderManager(renderManager) {
renderManager.add(this); renderManager->add(this);
} }
RenderObject::~RenderObject() { RenderObject::~RenderObject() {
this->renderManager.remove(this); this->renderManager->remove(this);
} }

View File

@ -15,6 +15,9 @@ void TextureManager::addSingleTexture(Textures texture, const char* filePath) {
SDL_SetTextureScaleMode(sdlTexture, this->scaleMode); // linear scaling results in blurry images SDL_SetTextureScaleMode(sdlTexture, this->scaleMode); // linear scaling results in blurry images
this->texture_cache.emplace(texture, sdlTexture); this->texture_cache.emplace(texture, sdlTexture);
if (filePath != nullptr) {
this->texture_references.emplace(texture, std::string(filePath));
}
std::cout << "Loaded texture at " << filePath << std::endl; std::cout << "Loaded texture at " << filePath << std::endl;
} }