I decided to write a 2D game with SFML, and for the beginning of the game I wrote classes to manage resources like sounds, musics, textures... I would like to get reviews of my code: how would you have write the program, how can I improve it?
I also wrote a class "Logger" to make the debug of the game easier. I would like to get reviews of this class too.
ResourceManager.hpp
#ifndef RESOURCE_MANAGER_HPP
#define RESOURCE_MANAGER_HPP
#include "FontManager.hpp"
#include "MusicManager.hpp"
#include "TextureManager.hpp"
#include "SoundManager.hpp"
class ResourceManager
{
public:
static ResourceManager& getInstance();
void loadResources();
private:
ResourceManager();
~ResourceManager();
FontManager& fontManager = FontManager::getInstance();
MusicManager& musicManager = MusicManager::getInstance();
TextureManager& textureManager = TextureManager::getInstance();
SoundManager& soundManager = SoundManager::getInstance();
};
#endif // RESOURCE_MANAGER_HPP
ResourceManager.cpp
#include "ResourceManager.hpp"
ResourceManager::ResourceManager()
{
}
ResourceManager::~ResourceManager()
{
}
ResourceManager& ResourceManager::getInstance()
{
static ResourceManager instance;
return instance;
}
void ResourceManager::loadResources()
{
musicManager.addMusic("music1", "music1.wav");
musicManager.addMusic("music2", "music2.wav");
fontManager.addFont("font1", "font1.ttf");
fontManager.addFont("font2", "font2.ttf");
textureManager.addTexture("texture1", "texture1.png");
soundManager.addSound("sound1", "sound1.wav");
}
SoundManager.hpp
#ifndef SOUND_MANAGER_HPP
#define SOUND_MANAGER_HPP
#include "Logger.hpp"
#include <unordered_map>
#include <SFML/Audio.hpp>
class SoundManager
{
public:
static SoundManager& getInstance();
int addSound(const std::string& id, const std::string& soundPath);
std::unique_ptr<sf::Sound> getSound(const std::string &id);
private:
SoundManager();
~SoundManager();
std::unordered_map<std::string, std::unique_ptr<sf::Sound>> soundMap_;
std::vector<sf::SoundBuffer> soundBufferVec_;
};
#endif // SOUND_MANAGER_HPP
SoundManager.cpp
#include "SoundManager.hpp"
SoundManager::SoundManager()
{
}
SoundManager::~SoundManager()
{
}
SoundManager& SoundManager::getInstance()
{
static SoundManager instance;
return instance;
}
int SoundManager::addSound(const std::string& id, const std::string& soundPath)
{
auto it = soundMap_.find(id);
if(it != soundMap_.end())
{
Logger::getInstance().log(LogLevel::ERROR, "Unable to add sound: '"
+ id + "' already exists");
return -1;
}
std::unique_ptr<sf::SoundBuffer> soundBuffer =
std::make_unique<sf::SoundBuffer>();
if(!soundBuffer->loadFromFile(soundPath))
{
Logger::getInstance().log(LogLevel::ERROR, "Unable to open sound '" +
soundPath + "'");
return -1;
}
soundBufferVec_.push_back(std::move(*(soundBuffer)));
unsigned int soundBufferVecSize = soundBufferVec_.size();
std::unique_ptr<sf::Sound> sound = std::make_unique<sf::Sound>
(std::move(soundBufferVec_[soundBufferVecSize - 1]));
soundMap_.emplace(id, std::move(sound));
Logger::getInstance().log(LogLevel::INFO, "'" + soundPath +
"' has been added");
return 0;
}
std::unique_ptr<sf::Sound> SoundManager::getSound(const std::string &id)
{
auto it = soundMap_.find(id);
if(it == soundMap_.end())
{
Logger::getInstance().log(LogLevel::ERROR, "Unable to load sound: " +
id + " doesn't exist");
return nullptr;
}
return std::move(it->second);
}
TextureManager.hpp
#ifndef TEXTURE_MANAGER_HPP
#define TEXTURE_MANAGER_HPP
#include "Logger.hpp"
#include <unordered_map>
#include <SFML/Graphics.hpp>
class TextureManager
{
public:
static TextureManager& getInstance();
int addTexture(const std::string& id, const std::string& texturePath);
std::unique_ptr<sf::Texture> getTexture(const std::string &id);
private:
TextureManager();
~TextureManager();
std::unordered_map<std::string, std::unique_ptr<sf::Texture>> textureMap_;
};
#endif // TEXTURE_MANAGER_HPP
TextureManager.cpp
#include "TextureManager.hpp"
TextureManager::TextureManager()
{
}
TextureManager::~TextureManager()
{
}
TextureManager& TextureManager::getInstance()
{
static TextureManager instance;
return instance;
}
int TextureManager::addTexture(const std::string& id,
const std::string& texturePath)
{
auto it = textureMap_.find(id);
if(it != textureMap_.end())
{
Logger::getInstance().log(LogLevel::ERROR, "Unable to add texture: '"
+ id + "' already exists");
return -1;
}
std::unique_ptr<sf::Texture> texture = std::make_unique<sf::Texture>();
if(!texture->loadFromFile(texturePath))
{
Logger::getInstance().log(LogLevel::ERROR, "Unable to open texture '"
+ texturePath + "'");
return -1;
}
textureMap_.emplace(id, std::move(texture));
Logger::getInstance().log(LogLevel::INFO, "'" + texturePath +
"' has been added");
return 0;
}
std::unique_ptr<sf::Texture> TextureManager::getTexture(const std::string &id)
{
auto it = textureMap_.find(id);
if(it == textureMap_.end())
{
Logger::getInstance().log(LogLevel::ERROR, "Unable to load texture: " +
id + " doesn't exist");
return nullptr;
}
return std::move(it->second);
}
Logger.hpp
#ifndef LOGGER_HPP
#define LOGGER_HPP
#include <sstream>
#include <iostream>
enum class LogLevel
{
NO = 0,
ERROR = 1,
INFO = 2,
DEBUG = 3
};
class Logger
{
public:
static Logger& getInstance();
void setLogLevel(const LogLevel& logLevel);
void log(const LogLevel& messageLevel, const std::string& message);
private:
Logger();
~Logger();
void setPrefix(const LogLevel& messageLevel);
std::string logPrefix_;
LogLevel currentLogLevel_;
LogLevel messageLevel_;
};
#endif // LOGGER_HPP
Logger.cpp
#include "Logger.hpp"
Logger::Logger()
{
}
Logger::~Logger()
{
}
void Logger::setPrefix(const LogLevel& messageLevel)
{
switch(messageLevel)
{
case LogLevel::DEBUG:
logPrefix_ = "[DEBUG] ";
break;
case LogLevel::INFO:
logPrefix_ = "[INFO] ";
break;
case LogLevel::ERROR:
logPrefix_ = "[ERROR] ";
break;
default:
break;
}
}
Logger& Logger::getInstance()
{
static Logger instance;
return instance;
}
void Logger::setLogLevel(const LogLevel& logLevel)
{
currentLogLevel_ = logLevel;
}
void Logger::log(const LogLevel& messageLevel, const std::string& message)
{
if(messageLevel == LogLevel::NO)
return;
if(currentLogLevel_ != LogLevel::DEBUG)
{
if(messageLevel == LogLevel::ERROR && currentLogLevel_ !=
LogLevel::ERROR)
return;
if(messageLevel == LogLevel::INFO && currentLogLevel_ !=
LogLevel::INFO)
return;
}
setPrefix(messageLevel);
std::cout << logPrefix_ << message << std::endl;
}
How to use this classes
main.cpp
#include <iostream>
#include "Logger.hpp"
#include "ResourceManager.hpp"
int main()
{
Logger::getInstance().setLogLevel(LogLevel::DEBUG);
ResourceManager& resourceManager = ResourceManager::getInstance();
resourceManager.loadResources();
std::unique_ptr<sf::Texture> texture = TextureManager::getInstance().
getTexture("texture1");
if(texture == nullptr)
return -1;
sf::RenderWindow window(sf::VideoMode(800, 600), "Window");
sf::Sprite sprite;
sprite.setTexture(*(texture));
std::unique_ptr<sf::Sound> sound = SoundManager::getInstance().
getSound("sound1");
if(sound == nullptr)
return -1;
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
if(event.type == sf::Event::Closed)
window.close();
else if(event.type == sf::Event::KeyPressed)
sound->play();
}
window.clear(sf::Color::Black);
window.draw(sprite);
window.display();
}
return 0;
}
I don't post the two manager classes (FontManager and MusicManager) because the code is fundamentally the same as SoundManager and TextureManager.
because the code is fundamentally the same
Duplicate code is a sign of trouble. You might want to rethink you design decisions. \$\endgroup\$