Améliorations du gestionnaire de ressources
Le gestionnaire devient un singleton pour faciliter le partage des ressources. Possibilité de charger dans les sous-dossiers. Fonction de listing de tous les fichiers dans un dossier. Adieu au chargement initial pour éviter des problèmes de performance (à tester)
This commit is contained in:
		
							parent
							
								
									f67e95306d
								
							
						
					
					
						commit
						bee32a1fb1
					
				|  | @ -6,48 +6,83 @@ | ||||||
| #include <boost/filesystem.hpp> | #include <boost/filesystem.hpp> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <string> | #include <string> | ||||||
| #include <fstream> |  | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Gestionnaire de ressources du jeu. Conserve des |  * Gestionnaire de ressources du jeu. La classe agit comme une interface | ||||||
|  * références vers toutes les ressources pour éviter |  * avec le dossier res/. Elle permet de précharger les ressources gourmandes | ||||||
|  * de les charger deux fois, permet l'accès uniforme |  * telles que les textures ou les polices | ||||||
|  * aux ressources |  | ||||||
|  */ |  */ | ||||||
| class ResourceManager { | class ResourceManager { | ||||||
| private: | private: | ||||||
|     bool preloaded; |     /**
 | ||||||
| 
 |      * Chemins vers les différents dossiers de ressources | ||||||
|  |      */ | ||||||
|     boost::filesystem::path textures_path; |     boost::filesystem::path textures_path; | ||||||
|     boost::filesystem::path fonts_path; |     boost::filesystem::path fonts_path; | ||||||
|     boost::filesystem::path levels_path; |     boost::filesystem::path levels_path; | ||||||
|     boost::filesystem::path musics_path; |     boost::filesystem::path musics_path; | ||||||
| 
 | 
 | ||||||
|     std::unordered_map<std::string, std::shared_ptr<sf::Image>> images; |     std::unordered_map< | ||||||
|     std::unordered_map<std::string, std::shared_ptr<sf::Texture>> textures; |         boost::filesystem::path, | ||||||
|     std::unordered_map<std::string, std::shared_ptr<sf::Font>> fonts; |         std::shared_ptr<sf::Image> | ||||||
|     sf::Music music; |     > images_cache; | ||||||
| 
 | 
 | ||||||
|  |     std::unordered_map< | ||||||
|  |         boost::filesystem::path, | ||||||
|  |         std::shared_ptr<sf::Texture> | ||||||
|  |     > textures_cache; | ||||||
|  | 
 | ||||||
|  |     boost::filesystem::path current_music_path; | ||||||
|  |     bool is_playing; | ||||||
|  |     sf::Music current_music; | ||||||
|     float music_volume; |     float music_volume; | ||||||
|     bool playing_state; |  | ||||||
|     std::string current_music; |  | ||||||
| 
 | 
 | ||||||
| public: |     /**
 | ||||||
|  |      * Construit le gestionnaire de ressources. Comme on ne | ||||||
|  |      * veut qu'une seule instance du gestionnaire, c'est privé | ||||||
|  |      */ | ||||||
|     ResourceManager(); |     ResourceManager(); | ||||||
| 
 | 
 | ||||||
|  | public: | ||||||
|     /**
 |     /**
 | ||||||
|      * Précharge toutes les ressources préchargeables |      * Renvoie l'unique instance du singleton gestionnaire de ressources | ||||||
|      */ |      */ | ||||||
|     void preload(); |     static ResourceManager& get(); | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Récupère une image préchargée |      * Récupère la liste des fichiers dans le dossier donné | ||||||
|  |      */ | ||||||
|  |     std::vector<boost::filesystem::path> getFiles(boost::filesystem::path dir) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Récupère le chemin vers le dossier des textures | ||||||
|  |      */ | ||||||
|  |     const boost::filesystem::path& getTexturesPath() const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Récupère le chemin vers le dossier des polices | ||||||
|  |      */ | ||||||
|  |     const boost::filesystem::path& getFontsPath() const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Récupère le chemin vers le dossier des niveaux | ||||||
|  |      */ | ||||||
|  |     const boost::filesystem::path& getLevelsPath() const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Récupère le chemin vers le dossier des musiques | ||||||
|  |      */ | ||||||
|  |     const boost::filesystem::path& getMusicsPath() const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Charge l'image dont le chemin est donné en paramètre | ||||||
|      */ |      */ | ||||||
|     std::shared_ptr<sf::Image> getImage(std::string name); |     std::shared_ptr<sf::Image> getImage(std::string name); | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Récupère une texture préchargée |      * Charge l'image dont le chemin est donné en paramètre | ||||||
|  |      * et la charge vers le GPU en tant que texture | ||||||
|      */ |      */ | ||||||
|     std::shared_ptr<sf::Texture> getTexture(std::string name); |     std::shared_ptr<sf::Texture> getTexture(std::string name); | ||||||
| 
 | 
 | ||||||
|  | @ -57,18 +92,7 @@ public: | ||||||
|     std::shared_ptr<sf::Font> getFont(std::string name); |     std::shared_ptr<sf::Font> getFont(std::string name); | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Récupère le chemin vers le fichier du niveau portant le |      * Joue la musique de fond donnée en paramètre | ||||||
|      * nom passé en argument |  | ||||||
|      */ |  | ||||||
|     std::string getLevelPath(std::string name); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Récupère la liste de tous les niveaux |  | ||||||
|      */ |  | ||||||
|     std::vector<std::string> getLevelList(); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Démarre la musique de fond donnée |  | ||||||
|      */ |      */ | ||||||
|     void playMusic(std::string name); |     void playMusic(std::string name); | ||||||
| 
 | 
 | ||||||
|  | @ -80,7 +104,7 @@ public: | ||||||
|     /**
 |     /**
 | ||||||
|      * Récupère le volume de la musique de fond |      * Récupère le volume de la musique de fond | ||||||
|      */ |      */ | ||||||
|     float getMusicVolume(); |     float getMusicVolume() const; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Modifie le volume de la musique de fond |      * Modifie le volume de la musique de fond | ||||||
|  |  | ||||||
|  | @ -1,143 +1,141 @@ | ||||||
| #include "resource_manager.hpp" | #include "resource_manager.hpp" | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <algorithm> | #include <utility> | ||||||
|  | 
 | ||||||
|  | namespace fs = boost::filesystem; | ||||||
|  | 
 | ||||||
|  | ResourceManager::get() { | ||||||
|  |     static ResourceManager manager; | ||||||
|  |     return manager; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| ResourceManager::ResourceManager() : preloaded(false), | ResourceManager::ResourceManager() : preloaded(false), | ||||||
|     music_volume(20), playing_state(false), current_music("") { |     music_volume(20), playing_state(false), current_music("") { | ||||||
|  | 
 | ||||||
|  |     // mise en mémoire des chemins vers les dossiers de ressources
 | ||||||
|  |     fs::path res_path = fs::current_path() / "res"; | ||||||
|  | 
 | ||||||
|  |     textures_path = res_path / "textures"; | ||||||
|  |     fonts_path = res_path / "fonts"; | ||||||
|  |     levels_path = res_path / "levels"; | ||||||
|  |     musics_path = res_path / "musics"; | ||||||
|  | 
 | ||||||
|     // initialisation de la musique en bouclage et au volume par défaut
 |     // initialisation de la musique en bouclage et au volume par défaut
 | ||||||
|     music.setLoop(true); |     music.setLoop(true); | ||||||
|     music.setVolume(music_volume); |     music.setVolume(music_volume); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ResourceManager::preload() { | std::vector<fs::path> ResourceManager::getFiles(fs::path dir) const { | ||||||
|     if (preloaded) { |     fs::recursive_directory_iterator dir(path), end; | ||||||
|         return; |     std::vector<fs::path> result; | ||||||
|  | 
 | ||||||
|  |     // on boucle sur tous les fichiers du dossier
 | ||||||
|  |     // et de ses sous-dossiers et on les garde en mémoire
 | ||||||
|  |     while (dir != end) { | ||||||
|  |         if (fs::is_regular_file(dir->path())) { | ||||||
|  |             result.push_back(dir->path()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     boost::filesystem::path current = boost::filesystem::current_path(); |         ++dir; | ||||||
|     boost::filesystem::directory_iterator end; |  | ||||||
| 
 |  | ||||||
|     // on garde une référence aux chemins des différentes ressources
 |  | ||||||
|     textures_path = current / "res/textures"; |  | ||||||
|     fonts_path = current / "res/fonts"; |  | ||||||
|     levels_path = current / "res/levels"; |  | ||||||
|     musics_path = current / "res/musics"; |  | ||||||
| 
 |  | ||||||
|     // préchargement de toutes les textures
 |  | ||||||
|     for (boost::filesystem::directory_iterator it(textures_path); it != end; ++it) { |  | ||||||
|         if (boost::filesystem::is_regular_file(it->path())) { |  | ||||||
|             std::string full_path = boost::filesystem::canonical(it->path()).string(); |  | ||||||
|             std::string name = it->path().filename().string(); |  | ||||||
| 
 |  | ||||||
|             auto image = std::shared_ptr<sf::Image>(new sf::Image); |  | ||||||
|             auto texture = std::shared_ptr<sf::Texture>(new sf::Texture); |  | ||||||
|             texture->setSmooth(true); |  | ||||||
| 
 |  | ||||||
|             std::cout << "Chargement de l'image " << name << "... "; |  | ||||||
| 
 |  | ||||||
|             if (!image->loadFromFile(full_path)) { |  | ||||||
|                 std::cerr << "ERREUR!" << std::endl; |  | ||||||
|             } else { |  | ||||||
|                 std::cout << "OK!" << std::endl; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|             std::cout << "Mise en mémoire de la texture " << name << "... "; |     return result; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|             if (!texture->loadFromImage(*image)) { | const fs::path& getTexturesPath() const { | ||||||
|                 std::cerr << "ERREUR!" << std::endl; |     return textures_path; | ||||||
|             } else { | } | ||||||
|                 std::cout << "OK!" << std::endl; |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             images[name] = std::move(image); | const fs::path& getFontsPath() const { | ||||||
|             textures[name] = std::move(texture); |     return fonts_path; | ||||||
|         } | } | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     // préchargement de toutes les polices
 | const fs::path& getLevelsPath() const { | ||||||
|     for (boost::filesystem::directory_iterator it(fonts_path); it != end; ++it) { |     return levels_path; | ||||||
|         if (boost::filesystem::is_regular_file(it->path())) { | } | ||||||
|             std::string full_path = boost::filesystem::canonical(it->path()).string(); |  | ||||||
|             std::string name = it->path().filename().string(); |  | ||||||
| 
 | 
 | ||||||
|             auto font = std::shared_ptr<sf::Font>(new sf::Font); | const fs::path& getMusicsPath() const { | ||||||
|             std::cout << "Chargement de la police " << name << "... "; |     return musics_path; | ||||||
| 
 |  | ||||||
|             if (!font->loadFromFile(full_path)) { |  | ||||||
|                 std::cerr << "ERREUR!" << std::endl; |  | ||||||
|             } else { |  | ||||||
|                 std::cout << "OK!" << std::endl; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             fonts[name] = std::move(font); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     preloaded = true; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<sf::Image> ResourceManager::getImage(std::string name) { | std::shared_ptr<sf::Image> ResourceManager::getImage(std::string name) { | ||||||
|     if (images.count(name) == 0) { |     // si l'image a déjà été chargée, on retourne la
 | ||||||
|         throw std::runtime_error( |     // version en cache mémoire
 | ||||||
|             "Impossible de récupérer l'image inexistante : " + name |     if (images_cache.count(name) > 0) { | ||||||
|         ); |         return images_cache[name]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return images[name]; |     fs::path image_path = textures_path / name; | ||||||
|  |     std::string full_path = fs::canonical(image_path).string(); | ||||||
|  | 
 | ||||||
|  |     // on tente de charger l'image depuis son emplacement
 | ||||||
|  |     auto image = std::shared_ptr<sf::Image>(new sf::Image); | ||||||
|  |     std::cout << "Chargement de l'image " << name << " : "; | ||||||
|  | 
 | ||||||
|  |     if (image->loadFromFile(full_path)) { | ||||||
|  |         std::cout << "OK!" << std::endl; | ||||||
|  |     } else { | ||||||
|  |         std::cerr << "ERR!" << std::endl; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // on met en cache l'image pour les requêtes suivantes
 | ||||||
|  |     // puis on la renvoie
 | ||||||
|  |     images_cache[name] = std::move(image); | ||||||
|  |     return images_cache[name]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<sf::Texture> ResourceManager::getTexture(std::string name) { | std::shared_ptr<sf::Texture> ResourceManager::getTexture(std::string name) { | ||||||
|     if (textures.count(name) == 0) { |     // si la texture est déjà dans le GPU, on renvoie son pointeur
 | ||||||
|         throw std::runtime_error( |     if (textures_cache.count(name) > 0) { | ||||||
|             "Impossible de récupérer la texture inexistante : " + name |         return textures_cache[name]; | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return textures[name]; |     // on récupère l'image depuis le disque
 | ||||||
|  |     std::shared_ptr<sf::Image> image = getImage(name); | ||||||
|  | 
 | ||||||
|  |     // on transfère l'image vers le GPU
 | ||||||
|  |     auto texture = std::shared_ptr<sf::Texture>(new sf::Texture); | ||||||
|  |     std::cout << "Création de la texture " << name << " : "; | ||||||
|  | 
 | ||||||
|  |     if (texture->loadFromImage(*image)) { | ||||||
|  |         std::cout << "OK!" << std::endl; | ||||||
|  |     } else { | ||||||
|  |         std::cerr << "ERR!" << std::endl; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // on met en cache la texture pour les requêtes suivantes
 | ||||||
|  |     // puis on la renvoie
 | ||||||
|  |     textures_cache[name] = std::move(texture); | ||||||
|  |     return textures_cache[name]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<sf::Font> ResourceManager::getFont(std::string name) { | std::shared_ptr<sf::Font> ResourceManager::getFont(std::string name) { | ||||||
|     if (fonts.count(name) == 0) { |     // on ne maintient pas de cache pour les polices, car ceci
 | ||||||
|         throw std::runtime_error( |     // est géré par la librairie du GUI (SFGUI). On tente de
 | ||||||
|             "Impossible de récupérer la police inexistante : " + name |     // charger la police depuis le disque
 | ||||||
|         ); |     fs::path font_path = fonts_path / name; | ||||||
|  |     std::string full_path = fs::canonical(font_path).string(); | ||||||
|  | 
 | ||||||
|  |     auto font = std::shared_ptr<sf::Font>(new sf::Font); | ||||||
|  |     std::cout << "Chargement de la police " << name << ": "; | ||||||
|  | 
 | ||||||
|  |     if (font->loadFromFile(full_path)) { | ||||||
|  |         std::cout << "OK!" << std::endl; | ||||||
|  |     } else { | ||||||
|  |         std::cerr << "ERR!" << std::endl; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return fonts[name]; |     return font; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::string ResourceManager::getLevelPath(std::string name) { |  | ||||||
|     return (levels_path / name).string(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::vector<std::string> ResourceManager::getLevelList() { |  | ||||||
|     boost::filesystem::directory_iterator iter(levels_path); |  | ||||||
|     std::vector<boost::filesystem::path> list; |  | ||||||
|     std::vector<std::string> path_list; |  | ||||||
| 
 |  | ||||||
|     // récupération de la liste de tous les niveaux
 |  | ||||||
|     std::copy( |  | ||||||
|         iter, boost::filesystem::directory_iterator(), |  | ||||||
|         std::back_inserter(list) |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     // tri par ordre alphabétique
 |  | ||||||
|     std::sort(list.begin(), list.end()); |  | ||||||
| 
 |  | ||||||
|     // conversion en chemins absolus
 |  | ||||||
|     for (auto it = list.begin(); it != list.end(); it++) { |  | ||||||
|         path_list.push_back((*it).string()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return path_list; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ResourceManager::playMusic(std::string name) { | void ResourceManager::playMusic(std::string name) { | ||||||
|  |     fs::path music_path = musics_path / name; | ||||||
|  | 
 | ||||||
|     // si la musique est déjà chargée, on la relance si elle
 |     // si la musique est déjà chargée, on la relance si elle
 | ||||||
|     // est en pause, sinon on ne fait rien
 |     // est en pause, sinon on ne fait rien
 | ||||||
|     if (current_music == name) { |     if (current_music_path == music_path) { | ||||||
|         if (!playing_state) { |         if (!is_playing) { | ||||||
|             playing_state = true; |             is_playing = true; | ||||||
|             music.play(); |             music.play(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -145,29 +143,26 @@ void ResourceManager::playMusic(std::string name) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // tente de charger la musique depuis le dossier "res/musics"
 |     // tente de charger la musique depuis le dossier "res/musics"
 | ||||||
|     std::string full_path = boost::filesystem::canonical(musics_path / name).string(); |     std::string full_path = fs::canonical(music_path).string(); | ||||||
|     std::cout << "Lecture de la musique " << name << "... "; |     std::cout << "Lecture de la musique " << name << "... "; | ||||||
| 
 | 
 | ||||||
|     if (!music.openFromFile(full_path)) { |     if (music.openFromFile(full_path)) { | ||||||
|         std::cerr << "ERREUR!" << std::endl; |  | ||||||
|     } else { |  | ||||||
|         std::cout << "OK!" << std::endl; |         std::cout << "OK!" << std::endl; | ||||||
|  |     } else { | ||||||
|  |         std::cerr << "ERR!" << std::endl; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     current_music = name; |     current_music_path = music_path; | ||||||
|     playing_state = true; |     is_playing = true; | ||||||
|     music.play(); |     music.play(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ResourceManager::stopMusic() { | void ResourceManager::stopMusic() { | ||||||
|     // on n'arrête la musique que si elle ne l'est pas déjà
 |     is_playing = false; | ||||||
|     if (playing_state) { |  | ||||||
|         playing_state = false; |  | ||||||
|     music.stop(); |     music.stop(); | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| float ResourceManager::getMusicVolume() { | float ResourceManager::getMusicVolume() const { | ||||||
|     return music_volume; |     return music_volume; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue