Le module core. (partie 3)

La problématique.

Dans cette dernière partie je vais vous présenté mon système de chargement de ressources, car, celui de SFML ne suffit pas, en voici les raisons.

Inconvénients des fonctions de chargement SFML.

-Il est impossible de vérifier si les ressources ont déjà été chargée.

-Il est impossible de récupérer les ressources chargée n’importe ou dans le programme sans devoir les passer dans une classe ou bien dans une fonction.

-Il est impossible de lier plusieurs alias à une ressource que ça soit aussi bien des identifiants de même type que des identifiants de type différent.

-Il est impossible de charger des ressources en utilisant ses propres types de fonction.

-Si le chargement de la ressource échoue, les fonctions de chargements de SFML renvoie un booléen au lieu de renvoyer une exception.

-Et enfin, les fonctions loadFromFile et loadFromMemory ne sont pas thread safe. (c’est à dire qu’il faut penser à les protéger avec un mutex dans le cas ou la ressource est chargée au fur et à mesure que celle-ci est utilisée)

-Obligation de libérer les resssource à la main, si on veut les libérer à un moment précis dans le programme.

Pour régler tout ses problèmes j’ai décidé d’écrire mon propre gestionnaire de ressources qui pemettra d’accéder à n’importe quelle ressource, partout dans le programme de manière thread safe.

Pour cela on va devoir utiliser un gestionnaire de ressources, ce gestionnaire de ressource va  servir de contexte qui va contenir toutes les ressources d’un même type.

On peut de plus choisir un type d’identifiant  pour chaque ressources (un std::string ou bien une enum)

Comme la ressource peut être libérée à n’importe quel moment, on va utilisé un pointeur, le gestionnaire de ressource va être propriétaire de la ressource (je vais donc utilise un std::unique_ptr), et toutes les ressources seront détruite lorsque le gestionnaire de ressource sera détruit.

Je vais utiliser une classe interne que je vais appeler Resource, cette classe se chargera de contenir la localisation de la ressource (son path), celui-ci pourra soit être sa localisation sur le disque dur si la ressource est chargée à partir d’un fichier ou bien sa localisation en mémoire si la ressource est chargée à partir de la mémoire.

Cette classe contiendra aussi un pointeur sur la ressource, ainsi que un id unique qui référencera la ressource dans le std::vector du gestionnaire de ressource.

Le reste de la classe ne contient rien d’autres que des fonctions pour charger la ressource à partir d’un fichier, de la mémoire ou bien même à partir d’un pointeur sur une fonction membre qui charge la ressource.

Il ne faut pas oublier de faire une std::map en plus du std::vector avec comme  clé les identifiants qui référencerons les ressources, et comme valeur la position des ressources dans le std::vector.

Et il ne faut pas oublie que lorsqu’on libère une ressource, les éléments dans le std::vector son décalés, il faut donc remettre à jour les identifiants des ressources qui suivent la ressource supprimée dans le std::vector.

Ensuite, il faut faire diverses méthodes pour récupérer une ressource à partir de sa position dans le std::vector, son identifiant, son path, etc…

J’ai défini des using vers des gestionnaires de ressources particulier pour les ressources déja définie dans ODFAEG c’est à dire, les textures, les shaders, les sons et le fonts.

Cette classe possède deux paramètre template, le 1er est le type de ressource que le gestionnaire de ressource doit gérer, et le second est le type des identifiants qui pointent vers les ressources. (std::string, ou bien une enum)

Vous pouvez remarquer aussi que j’ai défini un constructeur de copie et un opérateur d’affectation dans la classe interne Resource et une méthode clone dans le gestionnaire de ressource pour passer les ressources d’un contexte à un autre, l’ancien contexte ne devient donc plus propriétaire des ressources qu’il contient et devient donc invalide si l’on passe les ressources d’un contexte à un autre.

Ceci est utile par exemple si on veut faire plusieurs classes dérivées pour charger un type de ressource commun, on va charger la ressource avec un contexte pour chaque classe dérivées, et on va passer ses ressources au contexte commun.

Mais il faut encore créer cette classe qui va servir de contexte commun pour plusieurs types de ressources, en effet, le contexte graphique par exemple peut contenir des contextes de plusieurs types. (Des texture, des shaders, des fonts, etc…) et toutes ces ressources doivent être détruite à la destruction du contexte commun.

J’ai donc créer une classe qui peut contenir plusieurs contextes de type différents et qui détruit tout les contextes qu’elle contient lors de sa destruction, cette classe s’appelle la classe ResourceCache.

Cette classe peut aussi passer tout les pointeurs de ressources de plusieurs contextes dérivés à un contexte commun, et permet d’accéder ainsi à n’importe quel type de context à partir d’un identifiant.

Cette classe ne contient rien d’autre qu’une map, avec comme clé, un identifiant qui pointe vers un contexte (ou bien un gestionnaire de ressources), et la valeur qui n’est rien d’autre que un type commun que j’utilise pour tout les types de contexte. (C’est la classe ResourceManagerBase)

Voila, on a donc ici créer plusieurs classes qui servent de contexte pour nos ressources, sur la prochaine page je vais vous parler de plusieurs classes utilite lors du le codage du gameplay des jeux.

 

Leave a comment