Partage
  • Partager sur Facebook
  • Partager sur Twitter

Process terminated with status -805306369

    25 septembre 2021 à 12:58:38

    Bonjour !

    Je suis en train de faire un jeu en C++ avec la bibliothèque SDL2, et je programme une implémentation d'un Entity Component System. Mon programme se lance bien, le renderer a bien mis à jour la fenêtre avec la couleur passée dans le SetRenderDrawColor, mais directement après la fenêtre ne répond pas et le programme retourne le code d'erreur -805306369... J'ai tenté d'utiliser le debugger mais il ne m'indique aucune erreur... Quelqu'un saurait d'où vient le problème ? Je n'ai absolument rien trouvé quand j'ai cherché...

    Merci d'avance pour votre aide !

    Si vous avez besoin de mon code source je reste dispo pour vous le passer

    • Partager sur Facebook
    • Partager sur Twitter
      25 septembre 2021 à 13:25:01

      Salut,

      Est-ce que tu as lancé ton programme pas à pas ? Par exemple met un breakpoint jusqu' où ton programme tourne normalement puis fait l'exécution pas à pas pour voir où ça plante. Sinon, est-ce que tu pense à utiliser les assertions ?Aussi, il faut vérifier le retour des fonctions qui peuvent échouer comme IMG_Load() par exemple. Sans code on ne va pas pouvoir t' aider.

      • Partager sur Facebook
      • Partager sur Twitter

      Mon site web de jeux SDL2 entre autres : https://www.ant01.fr

        25 septembre 2021 à 13:33:34

        J'ai essayé de mettre des breakpoint mais je comprends pas trop comment ça fonctionne parce que qu'importe le breakpoint ma fenêtre ne répond pas ^^'

        J'ai uploadé un zip contenant tout mon code source sur Mediafire, voici le lien : https://www.mediafire.com/file/w5v6by5979mjc2f/ECS_Essai3.zip/file

        Merci pour ta réponse en tout cas et merci d'avance pour l'aide ! :p

        • Partager sur Facebook
        • Partager sur Twitter
          25 septembre 2021 à 15:12:45

          Quel est l' IDE que tu utilises (si c'est le cas) ? Sinon tu peux poster du code avec la balise '</>' dans la fenêtre d' édition des messages. Garde bien en tête que plus tu donneras d'informations, meilleure sera l'aide apportée.

          -
          Edité par Warren79 25 septembre 2021 à 15:14:41

          • Partager sur Facebook
          • Partager sur Twitter

          Mon site web de jeux SDL2 entre autres : https://www.ant01.fr

            25 septembre 2021 à 15:50:46

            J'utilise Code::Blocks 20.03, et j'envoie tout mon code ici du coup :

            main.cpp

            #include <iostream>
            #include "ECS.h"
            #include "Component.h"
            #include "Entity.h"
            #include "EntityManager.h"
            #include "Engine.h"
            
            int main(int argc, char** argv) {
                constexpr int FPS = 60;
                constexpr int FRAME_DELAY = 1000/FPS;
            
                Uint32 frameStart;
                int frameTime;
            
                Engine::get()->init();
            
                while(Engine::get()->isRunning()) {
                    frameStart = SDL_GetTicks();
            
                    Engine::get()->update();
                    Engine::get()->render();
            
                    frameTime = SDL_GetTicks() - frameStart;
                    if(FRAME_DELAY > frameTime) {
                        SDL_Delay(FRAME_DELAY - frameTime);
                    }
                }
            
                return 0;
            }
            

            Engine.h

            #pragma once
            
            #include <iostream>
            #include <SDL2/SDL.h>
            
            #include "EntityManager.h"
            #include "SystemManager.h"
            #include "TextureManager.h"
            
            constexpr int SCREEN_WIDTH = 1280;
            constexpr int SCREEN_HEIGHT = 720;
            constexpr SDL_Color DARK = {30,30,30,255};
            
            class Engine {
                public:
                    Engine();
                    virtual ~Engine() = default;
            
                    void init();
                    void quit();
                    void clean();
            
                    void update();
                    void render();
                    void events();
            
                    bool isRunning();
            
                    inline static Engine* get() {
                        if(s_instance == nullptr) {
                            s_instance = new Engine();
                        }
                        return s_instance;
                    }
                    inline SDL_Renderer* getRenderer() {
                        return renderer;
                    }
            
                private:
                    static Engine* s_instance;
            
                    bool running;
                    SDL_Window* window;
                    SDL_Renderer* renderer;
                    SDL_Color clearColor;
            
                    EntityManager* eManager;
            };
            

            Engine.cpp

            #include "Engine.h"
            
            Engine* Engine::s_instance = nullptr;
            
            Engine::Engine() {
                window = nullptr;
                renderer = nullptr;
                running = false;
            }
            
            void Engine::init() {
                if(SDL_Init(SDL_INIT_VIDEO) != 0) {
                    std::cerr << "ERROR " << "[Engine (init #sdl)]: " << SDL_GetError() << std::endl;
                }
            
                auto winFlags = (SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI); //|SDL_WINDOW_MAXIMIZED
                window = SDL_CreateWindow("Game Engine ECS",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,SCREEN_WIDTH,SCREEN_HEIGHT,winFlags);
                if(!window) {
                    std::cerr << "ERROR " << "[Engine (init #window)]: " << SDL_GetError() << std::endl;
                }
            
                auto renderFlags = (SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);
                renderer = SDL_CreateRenderer(window,-1,renderFlags);
                if(!renderer) {
                    std::cerr << "ERROR " << "[Engine (init #renderer)]: " << SDL_GetError() << std::endl;
                }
            
                eManager = new EntityManager();
            
                clearColor = DARK;
                running = true;
            
                std::cout << "INFO " << "[Engine (init)]: " << "Init done." << std::endl;
            }
            
            void Engine::quit() {
                running = false;
            }
            
            void Engine::clean() {
                TextureManager::getInstance().clean();
                SDL_DestroyWindow(window);
                SDL_DestroyRenderer(renderer);
                SDL_Quit();
            }
            
            void Engine::update() {
                eManager->refresh();
                eManager->update();
            }
            
            void Engine::render() {
                SDL_SetRenderDrawColor(renderer,clearColor.r,clearColor.g,clearColor.b,clearColor.a);
                SDL_RenderClear(renderer);
            
                //render systems
            
                SDL_RenderPresent(renderer);
            }
            
            void Engine::events() {
                SDL_Event ev;
            
                while(SDL_PollEvent(&ev)) {
                    switch(ev.type) {
                        case SDL_QUIT:
                            running = false;
                            break;
                        case SDL_KEYDOWN:
                            running = false;
                            break;
                    }
                }
            }
            
            bool Engine::isRunning() {
                return running;
            }
            

            ECS.h

            #pragma once
            
            #include <array>
            #include <vector>
            #include <bitset>
            #include <memory>
            #include <iostream>
            #include <algorithm>
            
            class Entity;
            class Component;
            
            using ComponentID = std::size_t;
            
            inline ComponentID getUniqueComponentID() {
                static ComponentID lastID = 0u;
                return lastID++;
            }
            
            template<typename T>
            inline ComponentID getComponentTypeID() noexcept {
                static ComponentID typeID = getUniqueComponentID();
                return typeID;
            }
            
            constexpr std::size_t MAX_COMPONENT = 32;
            
            using ComponentList = std::array<Component*, MAX_COMPONENT>;
            using ComponentBitset = std::bitset<MAX_COMPONENT>;

            Entity.h

            #pragma once
            
            #include "ECS.h"
            #include "Component.h"
            
            class Entity {
                public:
                    Entity() {}
                    virtual ~Entity() = default;
            
                    bool isActive() const {return active;}
                    void destroy() {active = false;}
            
                    template<typename T,typename... TArgs>
                    T& addComponent(TArgs&&... args) {
                        T* c(new T(std::forward<TArgs>(args)...));
                        c->entity = this;
                        std::unique_ptr<Component> uptr{c};
                        components.emplace_back(std::move(uptr));
            
                        compBitset[getComponentTypeID<T>()] = true;
                        compList[getComponentTypeID<T>()] = c;
            
                        c->init();
                        return *c;
                    }
            
                    template<typename T>
                    bool hasComponent() const {
                        return compBitset[getComponentTypeID<T>()];
                    }
            
                    template<typename T>
                    T& getComponent() const {
                        auto ptr(compList[getComponentTypeID<T>()]);
                        return *static_cast<T*>(ptr);
                    }
            
                private:
                    bool active = true;
                    std::vector<std::unique_ptr<Component>> components;
                    ComponentList compList;
                    ComponentBitset compBitset;
            };
            

            Component.h

            #pragma once
            
            #include "ECS.h"
            
            class Component {
                public:
                    virtual ~Component() {}
            
                    Entity* entity;
                    virtual void init();
            };
            

            System.h

            #pragma once
            
            #include "ECS.h"
            #include "Entity.h"
            
            class System {
                public:
                    System() = default;
                    virtual ~System() = default;
            
                    template<class... Args>
                    bool hasComponents(Entity* e) const {
                        const std::array<bool, sizeof...(Args)> values = {e->hasComponent<Args>()...};
                        return std::all_of(values.begin(), values.end(), [](bool b) { return b; });
                    }
                    virtual void update(Entity* e);
            };
            

            EntityManager.h

            #pragma once
            
            #include "ECS.h"
            #include "Entity.h"
            #include "SystemManager.h"
            
            #include <vector>
            #include <memory>
            
            class EntityManager {
                public:
                    EntityManager() = default;
                    virtual ~EntityManager() = default;
            
                    void update();
                    void render();
                    void refresh();
            
                    Entity& addEntity();
                    Entity& addEntity(Entity* e);
            
                private:
                    SystemManager* sManager;
                    std::vector<std::unique_ptr<Entity>> entities;
            };
            

            EntityManager.cpp

            #include "EntityManager.h"
            
            void EntityManager::update() {
                for(auto& e : entities) {
                    sManager->update(e.get());
                }
            }
            
            void EntityManager::render() {
            //    màj des entities par RenderSystem (?)
            }
            
            void EntityManager::refresh() {
                entities.erase(std::remove_if(std::begin(entities),std::end(entities),
                                              [](const std::unique_ptr<Entity> &e){
                                                return !e->isActive();
                                              }),std::end(entities));
            }
            
            Entity& EntityManager::addEntity() {
                Entity* e = new Entity();
                std::unique_ptr<Entity> uptr{e};
                entities.emplace_back(std::move(uptr));
                return *e;
            }
            
            Entity& EntityManager::addEntity(Entity* e) {
                std::unique_ptr<Entity> uptr{e};
                entities.emplace_back(std::move(uptr));
                return *e;
            }
            

            SystemManager.h

            #pragma once
            
            #include <vector>
            #include <memory>
            
            #include "System.h"
            
            class SystemManager
            {
                public:
                    SystemManager() = default;
                    virtual ~SystemManager() = default;
            
                    void update(Entity* e) {
                        for(auto& s : systems) {
                            s->update(e);
                        }
                    }
            
                    template<typename T>
                    T& addSystem() {
                        T* s(new T());
                        std::unique_ptr<System> uptr{s};
                        systems.emplace_back(std::move(uptr));
            
                        return *s;
                    }
            
                private:
                    std::vector<std::unique_ptr<System>> systems;
            };
            

            TextureManager.h

            #pragma once
            
            #include <map>
            #include <string>
            #include <iostream>
            #include <SDL2/SDL.h>
            #include <SDL2/SDL_ttf.h>
            #include <SDL2/SDL_image.h>
            
            #include "Engine.h"
            
            class TextureManager {
                public:
                    TextureManager();
                    virtual ~TextureManager() = default;
            
                    SDL_Texture* getTexture(std::string id);
                    void loadTexture(std::string id, std::string path);
            
                    TTF_Font* getFont(std::string id);
                    void loadFont(std::string id, std::string path,int fontsize);
            
                    void clean();
            
                    inline static TextureManager& getInstance() {
                        if(s_instance == nullptr) {
                            s_instance = new TextureManager();
                        }
                        return *s_instance;
                    }
            
                private:
                    static TextureManager* s_instance;
            
                    std::map<std::string, TTF_Font*> fonts;
                    std::map<std::string, SDL_Texture*> textures;
            };
            

            TextureManager.cpp

            #include "TextureManager.h"
            
            TextureManager* TextureManager::s_instance = nullptr;
            
            TextureManager::TextureManager() {
                if(TTF_Init() != 0) {
                    std::cerr << "ERROR " << "[TextureManager (constructor #ttf)]: " << TTF_GetError() << std::endl;
                }
            
                if(((IMG_Init(IMG_INIT_JPG|IMG_INIT_PNG))&(IMG_INIT_JPG|IMG_INIT_PNG)) != (IMG_INIT_JPG|IMG_INIT_PNG)) {
                    std::cerr << "ERROR " << "[TextureManager (constructor #img)]: " << IMG_GetError() << std::endl;
                }
            }
            
            SDL_Texture* TextureManager::getTexture(std::string id) {
                return (textures.count(id) > 0 ? textures[id] : nullptr);
            }
            
            void TextureManager::loadTexture(std::string id, std::string path) {
                if(textures.count(id) <= 0) {
                    SDL_Texture* newTextu = IMG_LoadTexture(Engine::get()->getRenderer(),path.c_str());
                    if(newTextu) {
                        textures[id] = newTextu;
                        std::cout << "INFO " << "[TextureManager (loadTexture)]: " << "Texture (" << path << ") loaded." << std::endl;
                    } else {
                        std::cerr << "ERROR " << "[TextureManager (loadTexture)]: " << IMG_GetError() << std::endl;
                    }
                }
            }
            
            TTF_Font* TextureManager::getFont(std::string id) {
                if(fonts.count(id) > 0) {
                    return fonts[id];
                }
                return nullptr;
            }
            
            void TextureManager::loadFont(std::string id, std::string path, int fontsize) {
                TTF_Font* newttf = TTF_OpenFont(path.c_str(),fontsize);
                if(newttf != nullptr) {
                    fonts.emplace(id,newttf);
                    std::cout << "INFO " << "[TextureManager (loadFont)]: " << "Font (" << path << ") loaded." << std::endl;
                } else {
                    std::cerr << "ERROR " << "[TextureManager (loadFont)]: " << TTF_GetError() << std::endl;
                }
            }
            
            void TextureManager::clean() {
                for(auto it = textures.begin() ; it != textures.end() ; it++) {
                    SDL_DestroyTexture(it->second);
                    textures.erase(it);
                }
                textures.clear();
            
                for(auto it = fonts.begin() ; it != fonts.end() ; it++) {
                    TTF_CloseFont(it->second);
                    fonts.erase(it);
                }
                fonts.clear();
            
                std::cout << "INFO " << "[TextureManager (clean)]: " << "Assets cleaned." << std::endl;
            }
            

            Voilà tout mon code source :p 



            -
            Edité par EthanWright 25 septembre 2021 à 15:54:03

            • Partager sur Facebook
            • Partager sur Twitter
              25 septembre 2021 à 17:50:07

              Salut,

              Déjà, ce que tu devrais faire, c'est lancer une exception pour chacun des tests que tu effectue dans la fonction init, car, pour l'instant, il n'y a absolument rien qui empêche la fonction d'aller plus loin si "quelque chose" vient à ne pas fonctionner correctement.

              Or, chaque étape est "éliminatoire", dans le sens où, si une étape donnée ne fonctionne pas, l'étape suivante ne pourra pas fonctionner non plus. Et il ne suffit pas d'afficher un message d'erreur du type "hey, tu sais que tel truc n'a pas fonctionné" pour pouvoir considérer que le problème a été traité ;)

              Ta fonction init pourrait donc prendre une forme proche de

              void Engine::init() {
                  if(SDL_Init(SDL_INIT_VIDEO) != 0) {
                      /* on crée directement une chaine de caractères qui 
                       * contient l'ensemble des informations intéressantes
                       */
                      std::string error="Engine init #sdl :";
                      error+=std::string{SDL_GetError()};
                      /* on affiche cette chaine de caractères */
                      std::cerr << error<<"\n";
                      /* on lance une exception contenant cette chaine de
                       * caractères car il ne sert à rien d'aller plus loin
                       */
                      trhow std::runitme_error(error);
                  }
               
                  auto winFlags = (SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI); //|SDL_WINDOW_MAXIMIZED
                  window = SDL_CreateWindow("Game Engine ECS",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,SCREEN_WIDTH,SCREEN_HEIGHT,winFlags);
                  if(!window) {
                      std::string error="Engine init #window :";
                      error+=std::string{SDL_GetError()};
                      std::cerr << error<<"\n";
                      trhow std::runitme_error(error);
                  }
               
                  auto renderFlags = (SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);
                  renderer = SDL_CreateRenderer(window,-1,renderFlags);
                  if(!renderer) {
                      std::string error="Engine init #renderer:";
                      error+=std::string{SDL_GetError()};
                      std::cerr << error<<"\n";
                      trhow std::runitme_error(error);
                  }
                  /* ouch, là, on va ver un problème */
                  eManager = new EntityManager();
               
                  clearColor = DARK;
                  running = true;
               
              }

              La deuxième chose à faire, ce serait d'abandonner cet horrible anti-pattern qu'est le singleton, mais, comme l'idée nécessite pas mal d'explications que je n'ai pas le temps de donner maintenant, on va se contenter de l'améliorer un tout petit peu

              • en supprimant le besoin de faire appel à la fonction init (car c'est une étape qu'il est "trop facile" d'oublier)
              • en supprimant le besoin d'une allocation dynamique de la mémoire
              • en faisant renvoyer une référence au lieu d'un pointeur (car elles offrent, entre autres, la garantie d'existence de la donnée, alors que tu devrais tester systématiquement le pointeur)
              • en rendant quelques fonctions qui n'ont aucun besoin de modifier l'état de Engine constantes

              Ta classe Engine pourrait donc prendre une forme proche de

              class Engine {
                  public:
                      Engine();
                      virtual ~Engine() = default;
               
                      void quit();
                      void clean();
               
                      void update();
                      void render() const;
                      void events();
               
                      bool isRunning() const;
               
                      inline static Engine& get() {
                          if(! m_initialized)
                              init();
                          static Engine engine;
                          return engine;
                      }
                      inline SDL_Renderer& getRenderer() {
                          if(!m_renderer)
                              init();
                          return *m_renderer;
                      }
                      inline EntityManager & entities(){
                          static EntityManager entities_;
                          return entities_;
                      }
                  private:
                      static bool initialized{false};
               
                      bool m_running{false};
                      SDL_Window* m_window{nullptr};
                      SDL_Renderer* m_renderer{nullptr};
                      SDL_Color m_clearColor;
               
              };

              Mais, du coup, il faut encore corriger un tout petit peu la fonction init pour qu'elle fasse passer la valeur de inititialized à true (et, accessoirement, qu'elle ne s'occupe plus du EntityManager:

              void Engine::init() {
                  if(SDL_Init(SDL_INIT_VIDEO) != 0) {
                      std::string error="Engine init #sdl :";
                      error+=std::string{SDL_GetError()};
                      std::cerr << error<<"\n";
                      trhow std::runitme_error(error);
                  }
               
                  auto winFlags = (SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI); //|SDL_WINDOW_MAXIMIZED
                  m_window = SDL_CreateWindow("Game Engine ECS",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,SCREEN_WIDTH,SCREEN_HEIGHT,winFlags);
                  if(!m_window) {
                      std::string error="Engine init #window :";
                      error+=std::string{SDL_GetError()};
                      std::cerr << error<<"\n";
                      trhow std::runitme_error(error);
                  }
               
                  auto renderFlags = (SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);
                  renderer = SDL_CreateRenderer(window,-1,renderFlags);
                  if(!m_renderer) {
                      std::string error="Engine init #renderer:";
                      error+=std::string{SDL_GetError()};
                      std::cerr << error<<"\n";
                      trhow std::runitme_error(error);
                  }
                  m_clearColor = DARK;
                  m_running = true;
                  m_initialized = true;
               
              }

              (j'en ai profité pour homogénéiser le noms des différentes données ;) )

              Il va bien sur de soi que l'on serait sans doute bien inspirés de faire la même chose pour toutes les classes qui se présentent comme un singleton (je pense principalement à la classe TextureManager)

              Ensuite, ce qui me choque le plus, c'est qu'il y a, dans la classe EntityManager, un pointeur sur SystemManager qui ... n'est jamais initialisé.

              Il n'y a donc rien d'étonnant au fait que la fonction

              void EntityManager::update() {
                  for(auto& e : entities) {
                      sManager->update(e.get());
                  }
              }

              nous envoie dans les choux, car, au mieux, sManager vaut nullptr, et nous aurions sans doute une erreur de fragmentation, au pire, sManager vaut "n'importe quoi" (peut être les "crasses" laissées par une utilisation plus ancienne de la mémoire) et l'on se retrouve donc à hacher la mémoire, menu .

              Enfin, et ce n'est qu'un avis, tu devrais éviter le terme "manager" autant que faire se peut.  Car ce terme est beaucoup trop générique, vu qu'il englobe les fait

              • de créer des données
              • de maintenir des données en mémoire
              • de manipuler les données
              • de décider de détruire les données
              • j'en passe, et peut-être de meilleures

              Bref, tu te retrouve avec une classe qui présente au moins quatre responsabilités, et cela en fait définitivement trois de trop ;)

              • Partager sur Facebook
              • Partager sur Twitter
              Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                25 septembre 2021 à 18:09:52

                Je pense en plus de ce que dit Koala01, que le PO devrait wrapper les pointeurs de la SDL2 avec des std::unique_ptr, par exemple:

                #ifndef H_UNIQ_PTR_SDL
                #define H_UNIQ_PTR_SDL
                
                #include "SDL.h"
                #include "SDL_image.h"
                #include "SDL_ttf.h"
                #include "SDL_mixer.h"
                #include <memory>
                 
                namespace sdl2{
                    struct SDL_Deleter {
                		void operator()(SDL_Window*   ptr) { if (ptr) SDL_DestroyWindow(ptr); }
                		void operator()(SDL_Renderer* ptr) { if (ptr) SDL_DestroyRenderer(ptr); }
                		void operator()(SDL_Surface*  ptr) { if (ptr) SDL_FreeSurface(ptr); }
                		void operator()(SDL_Texture*  ptr) { if (ptr) SDL_DestroyTexture(ptr); }
                		void operator()(SDL_RWops*    ptr) { if (ptr) SDL_RWclose(ptr); }
                		void operator()(SDL_Joystick* ptr) { if (ptr) SDL_JoystickClose(ptr); }
                		void operator()(TTF_Font*     ptr) { if (ptr) TTF_CloseFont(ptr); }
                		void operator()(Mix_Chunk*    ptr) { if (ptr) Mix_FreeChunk(ptr); }
                		void operator()(Mix_Music*    ptr) { if (ptr) Mix_FreeMusic(ptr); }
                		void operator()(SDL_Haptic*   ptr) { if (ptr) SDL_HapticClose(ptr); }
                	};
                	using WindowPtr    = std::unique_ptr<SDL_Window,   SDL_Deleter>;
                	using RendererPtr  = std::unique_ptr<SDL_Renderer, SDL_Deleter>;
                	using SurfacePtr   = std::unique_ptr<SDL_Surface,  SDL_Deleter>;
                	using TexturePtr   = std::unique_ptr<SDL_Texture,  SDL_Deleter>;
                	using RWopsPtr     = std::unique_ptr<SDL_RWops,    SDL_Deleter>;
                	using JoystickPtr  = std::unique_ptr<SDL_Joystick, SDL_Deleter>;
                	using FontPtr      = std::unique_ptr<TTF_Font,     SDL_Deleter>;
                	using Mix_ChunkPtr = std::unique_ptr<Mix_Chunk,    SDL_Deleter>;
                	using Mix_MusicPtr = std::unique_ptr<Mix_Music,    SDL_Deleter>;
                	using HapticPtr    = std::unique_ptr<SDL_Haptic,   SDL_Deleter>;
                }
                
                #endif // H_UNIQ_PTR_SDL
                


                Le PO aurait mis les fonctions de libération des pointeurs dans un destructeur, je n'aurais rien dit mais là il les met dans une fonction nommée clean, c'est pas terrible. :)

                -
                Edité par Warren79 25 septembre 2021 à 20:36:45

                • Partager sur Facebook
                • Partager sur Twitter

                Mon site web de jeux SDL2 entre autres : https://www.ant01.fr

                  25 septembre 2021 à 19:13:37

                  Merci beaucoup pour ta réponse Koala01 !

                  Je testerai ça ce soir, je suis pas chez moi là :p

                  Et j'ai pas trop compris pour le Deleter Warren79, il devrait remplacer quoi ? :p

                  ==================================================

                  EDIT : Donc !

                  J'ai testé, et j'ai quelques soucis :

                  • Je peux pas utiliser la fonction get(), parce que init() n'est pas utilisé avec un objet... Je devrais mettre cette méthode en static ?
                  • Je peux pas utiliser runtime_error parce que... "runtime_error is not a member of std"... Alors que j'ai bien inclus les headers <stdexcept> et <exception>...

                  Pour l'instant c'est les seules erreurs que j'ai, mais comme l'erreur pour laquelle j'avais demandé de l'aide était renvoyée après lancement du programme je peux pas savoir si elle persiste :p

                  D'ailleurs, avec la fonction get() qui renvoie une référence, on est bien d'accord que si je veux utiliser mon objet je dois pas faire (par exemple) :

                  Engine::get()->getRenderer();

                  Mais bien :

                  Engine::get().getRenderer();

                  C'est ça ? Parce que j'avais vu une vidéo où quelqu'un avait une fonction qui renvoyait une référence, mais il utilisait -> malgré tout, alors que quand je fais ça mon IDE m'envoie balader :p

                  -
                  Edité par EthanWright 25 septembre 2021 à 20:13:04

                  • Partager sur Facebook
                  • Partager sur Twitter
                    25 septembre 2021 à 20:42:42

                    En fait la classe template std::unique_ptr se charge de libérer la ressource qu' elle détient (on parle d' ownership ) au moment opportun, ou alors lorsque la fin de la portée du unique_ptr  est atteinte. Explication avec du code:

                    constexpr char ARIALFONTPATH[] = "fonts/arial.ttf";
                    
                    void fooBar()
                    {
                       FontPtr font{ TTF_OpenFont( ARIALFONTPATH, 22 ) }
                       
                       for( unsigned i{0} ; i < 8 ; ++i )
                       {
                           doSomething(i);
                       }
                    
                       //Ici on arrive à la fin de la portée de 'font' , la police est libérée grâce au RFID.              
                       //Pas besoin d' écrire le code pour libérer le pointeur sur police
                    }



                    -
                    Edité par Warren79 25 septembre 2021 à 20:45:05

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Mon site web de jeux SDL2 entre autres : https://www.ant01.fr

                      25 septembre 2021 à 20:56:46

                      EthanWright a écrit:

                      Merci beaucoup pour ta réponse Koala01 !

                      Je testerai ça ce soir, je suis pas chez moi là :p

                      Et j'ai pas trop compris pour le Deleter Warren79, il devrait remplacer quoi ? :p

                      -
                      Edité par EthanWright il y a 31 minutes

                      Ben, pour faire simple, std::unique_ptr utilise "normalement" deux paramètres template:

                      • le premier correspond au type du pointeur sous-jacent
                      • le deuxième correspond à "comment faut-il libérer correctement les ressources allouées à ce pointeur?"

                      L'astuce, c'est que le deuxième paramètre template utilise une valeur "par défaut" (std::default_deleter) qui ... appellera automatiquement delete (ou delete[]) sur le pointeur sous-jacent, qui s'applique "dans la plupart des cas", mais dont on peut décider de modifier le comportement si cela ne correspond pas à nos besoins.

                      Or,  il se fait justement que, lorsque l'on manipule des données issues de la SDL, le comportement "par défaut" (l'appel à delete) ne nous convient pas, vu  qu'il faut normalement appeler les fonctions spécifiques de la SDL pour libérer les ressources.

                      Ce que l'on va donc faire, c'est créer ce que l'on appelle "un foncteur" -- on pourrait dire "une classe fonction" car elle n'expose que l'opérateur () permettant de traiter la classe comme s'il s'agissait d'une "simple fonction" -- qui va fournir le "bon" comportement pour la libération des ressources.

                      Autrement dit, ce foncteur va "tout simplement" se contenter de faire appel à la bonne fonction permettant de libérer les ressources allouées au pointeurs des différents types de données exposés par la SDL.

                      Une fois que ce foncteur sera défini, il suffira de l'utiliser pour dire "voici les comportements que l'on veut te voir utiliser pour libérer les ressources allouées aux différents types de données fournis par la SDL", et le tour sera joué, car ce seront ces comportements qui seront utilisés.

                      Et, pour que ce soit encore plus facile, on va déclarer dans un espace de nom (le namespace sdl2) le foncteur en question (SDL_Deleter) et des alias de noms pour les différents types de données, qui seront en réalité ... des spécialisations totale de std::unique_ptr prenant, en premier paramètre, les types exposés par la SDL et, en deuxième paramètre, le foncteur que l'on vient de créer, à savoir

                          using WindowPtr    = std::unique_ptr<SDL_Window,   SDL_Deleter>;     //SDL_Window *
                          using RendererPtr  = std::unique_ptr<SDL_Renderer, SDL_Deleter>; //SDL_Renderer *
                          using SurfacePtr   = std::unique_ptr<SDL_Surface,  SDL_Deleter>; //SDL_Surface *
                          using TexturePtr   = std::unique_ptr<SDL_Texture,  SDL_Deleter>; //SDL_Texture *
                          using RWopsPtr     = std::unique_ptr<SDL_RWops,    SDL_Deleter>; //SDL_RWops *
                          using JoystickPtr  = std::unique_ptr<SDL_Joystick, SDL_Deleter>; //SDL_Joystick *
                          using FontPtr      = std::unique_ptr<TTF_Font,     SDL_Deleter>; //TTF_Font *
                          using Mix_ChunkPtr = std::unique_ptr<Mix_Chunk,    SDL_Deleter>; //Mix_Chunk *
                          using Mix_MusicPtr = std::unique_ptr<Mix_Music,    SDL_Deleter>; //Mix_Music *
                          using HapticPtr    = std::unique_ptr<SDL_Haptic,   SDL_Deleter>; //SDL_Haptic *

                      Et nous pourrons utiliser ces alias de types -- comme sdl2::WindowPtr -- en sachant que les ressources allouées au SDL_Window * sous-jacent seront correctement libérées une fois que l'on n'aura plus besoin de la donnée ;)

                      • Partager sur Facebook
                      • Partager sur Twitter
                      Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                        26 septembre 2021 à 12:27:36

                        J'ai essayé la technique avec les unique_ptr, mais je dois pas avoir compris comment faire réellement, parce que quand je fais ça, je mets, par exemple :

                        sdl2::WindowPtr window{nullptr};


                        Donc dans ma fonction init(), j'aurais ça :

                        window = SDL_CreateWindow(/*arguments*/);


                        Mais mon IDE me dit qu'il ne peut pas convertir SDL_Window* en std::unique_ptr<SDL_Window, SDL_Deleter> :p Il faudrait que je remplace, par exemple, ma variable window par window->get(), qui me retourne le pointeur en lui-même ?

                        En dehors de ça, j'ai toujours ce problème-là :

                        EthanWright a écrit:

                        J'ai testé, et j'ai quelques soucis :

                        • Je peux pas utiliser la fonction get(), parce que init() n'est pas utilisé avec un objet... Je devrais mettre cette méthode en static ?
                        • Je peux pas utiliser runtime_error parce que... "runtime_error is not a member of std"... Alors que j'ai bien inclus les headers <stdexcept> et <exception>...

                        -
                        Edité par EthanWright il y a environ 16 heures

                        Vous avez une idée pour résoudre ces problèmes ? :p 

                        ===================================

                        EDIT : J'ai remplacé (par exemple toujours) window par window.get(), et ça m'annule bien le problème de conversion du pointeur vers unique_ptr, par contre il me met maintenant l'erreur "lvalue required as left operand of assignment", qu'est-ce que je devrais faire je suis perdu :'(

                        ===================================

                        EDIT2 : J'ai réglé ce problème du coup, à la place de ça :

                        window.get() = SDL_CreateWindow(/*arguments*/);

                        J'ai mis :

                        window = sdl2::WindowPtr{SDL_CreateWindow(/*arguments*/};


                        Du coup mes seuls problèmes restants, c'est le runtime_error qui fonctionne pas et la fonction init() dans le Engine::get() qui demande un objet pour être exécutée :p

                        -
                        Edité par EthanWright 26 septembre 2021 à 13:02:07

                        • Partager sur Facebook
                        • Partager sur Twitter
                          26 septembre 2021 à 13:05:43

                          Salut,

                          Essaye avec le code suivant:

                          sdl2::WIndowPtr window;
                          
                          window.reset( SDL_CreateWindow(/* arguments*/ ) );

                          Sinon prend l'habitude de regarder la documentation officielle sur cppreference , ça te permettra de gagner du temps par rapport à une demande sur un forum (en principe). :)

                          Documentation  https://en.cppreference.com/w/cpp/memory/unique_ptr (recherche Qwant)

                          • Partager sur Facebook
                          • Partager sur Twitter

                          Mon site web de jeux SDL2 entre autres : https://www.ant01.fr

                            26 septembre 2021 à 13:39:57

                            Ça fonctionne aussi, merci et merci du conseil ! ^^  Bon par contre j'ai toujours les problèmes avec le runtime_error et la fonction Engine::get() :p 

                            =============================

                            EDIT :

                            J'ai mis de côté les problèmes avec runtime_error et Engine::get() pour l'instant, mais j'ai un problème avec ma classe TextureManager, il me renvoie l'erreur suivante :

                            |error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = _TTF_Font; _Dp = sdl2::SDL_Deleter]'|

                            J'avais 4 fois cette erreur, j'ai réussi à en supprimer 2 occurences (en mettant std::move(texture) au lieu de texture elle-même), mais pour les 2 autres occurrences je comprends pas trop où est le soucis :p Du coup je mets ici les fonctions incriminées :

                            TextureManager - getTexture :

                            sdl2::TexturePTR TextureManager::getTexture(std::string id) {
                                if(textures.count(id) > 0) {
                                    return textures[id];
                                }
                                return nullptr;
                            }

                            TextureManager - getFont :

                            sdl2::FontPTR TextureManager::getFont(std::string id) {
                                return (fonts.count(id) > 0 ? fonts[id] : nullptr);
                            }

                            Les attributs textures et fonts sont des map<string, TexturePTR (FontPTR pour fonts)>
                            Comment pourrais-je régler ce problème ? :p

                            -
                            Edité par EthanWright 26 septembre 2021 à 15:01:10

                            • Partager sur Facebook
                            • Partager sur Twitter
                              26 septembre 2021 à 16:24:33

                              Au temps pour moi, j'ai fait une petite erreur dans le code de get...

                              Ce devrait être

                              inline static Engine& get() {
                                  static Engine engine;
                                  if(! engine.m_initialized)
                                      engine.init();
                                  return engine;
                              }

                              Dans le sens où l'on doit créer l'engin avant de voir dans quel état il est ;), vu que ses données membres ne sont pas statiques.

                              Ceci dit, a bien y réfléchir, et parce que j'étais un peu "à coté de mes pompes" lorsque j'ai répondu, il faudrait idéalement même encore modifier un tout petit peu la classe Engine, car ce que l'on veut, c'est que l'on ne puisse y accéder qu'en faisant appel à la fonction get.

                              Du coup, nous devrions donc lui donner une forme proche de

                              class Engine {
                                  public:
                                      Engine();
                                      virtual ~Engine() = default;
                                
                                      void quit();
                                      void clean();
                                
                                      void update();
                                      void render() const;
                                      void events();
                                
                                      bool isRunning() const;
                                
                                      inline static Engine& get() {
                                          static Engine engine;
                                          return engine;
                                      }
                                      inline SDL_Renderer& getRenderer() {
                                          return *m_renderer;
                                      }
                                      inline EntityManager & entities(){
                                          static EntityManager entities_;
                                          return entities_;
                                      }
                                  private:
                                      Engine(){
                                          init();
                                      }
                                      bool m_running{false};
                                      SDL_Window* m_window{nullptr};
                                      SDL_Renderer* m_renderer{nullptr};
                                      SDL_Color m_clearColor;
                                
                              };

                              Et l'accès aux différentes fonctions se fera dés lors sous une forme proche de

                              Engine::get().quit();
                              Engine::get().clean();
                              auto & entities = Engine::get().entities();
                              if(Engine::get().isRunning() ){
                                  ...
                              }

                              (je ne met que quelques exemples ;) )

                              Pour tes problèmes de runtime, je suis pour aiinsi dire persuadé que c'est parce que ton pointeur sur SystemManager (dans ta classe EntityManager) nous envoie "dans les choux"...

                              Cela serait d'ailleurs sans doute confirmé en ajoutant une assertion dans la fonction update:

                              void EntityManager::update() {
                                  assert(sManager && "system manager not initialized");
                                  for(auto& e : entities) {
                                      sManager->update(e.get());
                                  }
                              }

                              (requière l'inclusion du fichier d'en-tête <cassert> )

                              Ceci étant dit, je ne crois sincèrement pas qu'il soit du ressort du gestionnaire d'entités de maintenir le ... gestionnaire de systèmes.

                              A priori, ces deux gestionnaires devraient se trouver "au même niveau", c'est à dire, dans l'état actuel des choses, que ton gestionnaire de systèmes devrait être accessible ... à partir de ta classe Engine, exactement de la même manière que ta classe EntityManager.

                              Ce qu'il faudra alors, c'est une fonction qui "fasse le lien" entre les deux gestionnaires, sous une forme qui pourrait ressembler à

                              /*on s'assure que toutes les entités du gestionnaire d'entités
                               * soient mises à  jour par tous les systèmes du gestionnaire de systèmes
                               */
                              void updateAll(){
                                 auto & entities = Engine::get().entities();
                                 // fonction à créer, sur le meme modèle que la fonction entities()
                                 auto & systems  = Engine::get().systems(); 
                                 /* et pour ce faire, on transmet le gestionnaire d'entités au gestionnaire de systèmes */
                                 systems.update(entities); 
                              }

                              Cette fonction update ressemblerait donc sans doute à quelque chose comme

                              /* on s'assure que toutes les entités soient traitées par tous les
                               * systèmes
                              void SystemManager::update(EntityManager & entities){
                                  /* et pour ce faire, on passe le gestionnaire d'entité à tous les systèmes
                                   * du gestionnaire de systèmes
                                   */
                                  for(auto & system : systems)
                                      system.update(entities);
                              }

                              Et, enfin, au niveau des différents systèmes, on parcourt l'ensemble des entités et on met à jour celles qui correspondent aux prérequis:

                              void System::update(EntityManager & entities){
                                  for(auto & entity: entities){ // il faudra les fonction 
                                                                // begin et end 
                                      if(hasComponent(entity.get()))
                                          update(entity.get());
                                  }
                              }

                              Alors, bien sur, on peut inverser la logique générale: j'ai décidé de parcourir l'ensemble des systèmes avant de parcourir l'ensemble des entités, mais on pourrait envisager de commencer par parcourir l'ensemble des entités et transmettre l'ensemble des systèmes à chacune d'elle séparément.  Il faudrait peut être faire des tests et des benchmarks pour savoir ce qui sera le plus efficace dans ta situation ;)

                              PS: Je viens de constater que  tu fais la même erreur avec ton gestionnaire de textures que celle que tu faisais avec ta classe Engine: si tu n'arrive pas à charger une texture, tu te contente d'afficher un message du genre "coucou, je n'ai pas su charger la texture", mais tu laisse continuer le programme.

                              Or, le problème reste toujours le même: si ta ressource (ta texture ou ta TTF_Font, en l'occurrence) n'est pas chargée, ben, elle n'existe tout simplement pas pour l'ordinateur (le pointeur est sans doute égal à nullptr).

                              Et si tu essaye d'accéder à cette ressource (autrement que pour t'assurer qu'elle existe), ben, tu vas partir dans "les choux". Il faut donc décider de l'approche que l'on a du problème, car on peut décider:

                              • Soit, que "c'est bloquant" si une de ces ressources n'existe pas, et donc, qu'on doit faire planter le programme directement (en lançant une exception, comme pour m_window ou m_renderer)
                              • soit que "c'est pas grave, et que l'on "skip" tout le processus qui  aurait du utiliser la ressource, si elle n'existe pas (mais cela implique de tester l'existence de la ressource de manière systématique).

                              Le truc, avec la deuxième solution, c'est que, du coup, on risque d'avoir des textes ou des textures qui ne seront pas affichées, et ca risque de donner un "drôle de gueule" à ton jeu :p

                              D'un autre coté, si on fait planter le jeu de manière systématique, ben, cela t'obligera à apporter une solution au problème (en gros: à t'assurer que la ressource que tu essaye de charger se trouve bel et bien à l'endroit où tu espères la trouver ;) ).

                              • Partager sur Facebook
                              • Partager sur Twitter
                              Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                26 septembre 2021 à 22:38:40

                                Pour commencer, mon erreur avec le runtime_error est corrigée, c'était simplement que j'avais écrit runitme_error... (oui je suis pas doué :p)

                                J'ai rajouté un renvoie d'erreur pour le chargement des ressources du coup, parce que c'est vrai qu'un jeu sans les ressources nécessaires ça peut donner des visuels assez spéciaux :p

                                Pour ce qui est de la fonction update, je devrais rajouter une méthode qui retourne une référence vers le vector<unique_ptr<Entity>> du coup ? Comme les Entity sont stockées dans le vector qui est privé :p

                                Merci énormément pour vos réponses et votre aide en tout cas !

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  27 septembre 2021 à 2:28:30

                                  Ben, tes  manager devraient sans doute avoir les fonction begin() et end() (en version const et non const), qui renvoient respectivement un itérateur sur le premier élément du conteneur sous-jacent et un itérateur sur ce qui suit le dernier élément du container...

                                  Un peu à la manière de ce que font toutes les collections de la bibliothèque standard (et, pour être précis, il ne s'agit que d'un déport de ces fonctions ;) )

                                  Cela te permettra d'utiliser les même mécanique (boucles et algorithmes) que celles que tu utiliserais avec les containers sous-jacent, et ca, ca n'a pas de prix ;)

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                    27 septembre 2021 à 10:26:58

                                    Comment je pourrais mettre en place ces fonctions dans mon EntityManager ? :p
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      27 septembre 2021 à 11:48:00

                                      Ca ressemblerait à quelque chose comme
                                      class EntityManager {
                                          /* un premier alias pour les unique_pointer
                                           * c'est -- typiquement -- un alias qui ne servira qu'ici
                                           * il n'y a donc pas besoin de l'exposer dans 
                                           * l'accessibilité publique
                                           */
                                          using EntityPointer = sd::unique_ptr<Entity>;
                                          /* Un autre alias qui ne servira qu'ici: le tableau de
                                           * (unique_pointer sur) des entités 
                                           */
                                          using EntityArray = std::vector<EntityPointer>;
                                          public:
                                              /* on a besoin des itérateur (constants et non 
                                               * constants sur le tableau (de unique_pointer) 
                                               * d'entités
                                               * on reprend exactement les mêmes noms que les noms
                                               * officiels (par facilité)
                                               */
                                              using const_iterator = typename EntityArry::const_iteroatr;
                                              using iterator = typename EntityArray::iterator;
                                              EntityManager() = default;
                                              /* toute la partie publique existante, à laquelle on ajoute */
                                              /* les fonctions begin et end en version non const */
                                              iterator begin(){
                                                  return entities.begin();
                                              }
                                              iterator end(){
                                                  return entities.end();
                                              }
                                              /* les fonctions begin et end en version const */
                                              const_iterator begin(){
                                                  return entities.begin();
                                              }
                                              const_iterator end(){
                                                  return entities.end();
                                              }
                                              /* et, pour faire bonne mesure, quelques fonctions 
                                               * "exportés" supplémentaire
                                               */
                                              size_t size() const{// parce qu'il est toujours intéressant
                                                                  // de savoir combien d'entités il y a
                                                  return entities.size(); 
                                              }
                                              bool empty() const{// parce que c'est plus facile
                                                                 // de savoir s'il est vide que de
                                                                 // commencer à se demander combien
                                                                 // d'entités il y a
                                                  return entities.empty();
                                              }
                                              void clear(){ // pour le cas où l'on voudrait virer
                                                            // toutes les entités
                                                  entities.clear();
                                              }
                                              /* il y aura peut-être d'autres fonctions qui te paraitront utiles à l'usage
                                               * mais celles-ci seront déjà "pas mal" et "utiles"
                                               */
                                          private:
                                              /* et, tant qu'à faire, on utilise les alias créés
                                               * ici aussi
                                               */
                                              EntityArray entities;
                                      };

                                      Comme tu le vois, il n'y a rien de bien compliqué dans l'histoire ;)

                                      EDIT: Le gros avantage, si tu suis le même raisonnement pour ton gestionnaire de services, les deux gestionnaires (de services et d'entités) n'ont même plus besoin de se connaitre l'un l'autre: il n'y a plus que la notion de service qui doive connaitre la notion d'entité.

                                      Et, du coup, tu peux déporter toute la logique que je t'expliquais plus haut dans une simple fonction (éventuellement libre) proche de

                                      void updateAll(){
                                          /* j'ai besoin des deux gestionnaires */
                                          auto & services = Engine::get().services();
                                          auto & entities = Engine::get().entities();
                                          for(auto & service : services) { // pour chaque service qui existe
                                              for(auto & entity : entities){ // je passe toutes les
                                                                             // entités en revue
                                                  /* je ne me souviens plus du nom de la fonction
                                                   * si l'entité doit être manipulée par le service
                                                   * j'ai choisi de l'appeler match et de lui donner
                                                   * une entité par référence
                                                   */
                                                  if(service.get()->match(*(entity.get())){
                                                      /* si l'entité correspond, on demande au
                                                       * service de la mettre à jour (on la passe
                                                       * aussi par référence pour le coup 
                                                       */
                                                      service.get()->update(*(entity.get()));
                                                  }
                                              }
                                          }
                                      }

                                      Et, bien sur, rien ne t'empêche d'intervertir les deux boucles ("pour chaque entité qui existe, je passe tous les services en revue") en fonction de ce que les benchmark mettront en évidence comme étant "le plus efficace" ;)

                                      -
                                      Edité par koala01 27 septembre 2021 à 12:10:10

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                      Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                        27 septembre 2021 à 18:40:03

                                        Merci pour l'aide ! J'ai donc fait les fonctions begin et end, en const_iterator et en iterator, et il me renvoie ces erreurs :

                                        • |error: 'EntityManager::const_iterator EntityManager::begin()' cannot be overloaded with 'EntityManager::iterator EntityManager::begin()'|
                                        • |error: 'EntityManager::const_iterator EntityManager::end()' cannot be overloaded with 'EntityManager::iterator EntityManager::end()'|

                                        Je comprends l'erreur (deux méthodes ayant le même nom qui rentre en conflit), mais comment je pourrais régler ce problème ? :p

                                        ==========================

                                        EDIT :

                                        En attendant une réponse pour l'itérateur, j'ai créé plusieurs classes, notamment le Component Sprite, dont voici le code :

                                        #pragma once
                                        
                                        #include <string>
                                        #include <iostream>
                                        #include <SDL2/SDL.h>
                                        
                                        #include "ECS.h"
                                        #include "Engine.h"
                                        #include "Component.h"
                                        #include "UptrSdl2.h"
                                        
                                        #include "TextureManager.h"
                                        
                                        class Sprite : public Component {
                                            public:
                                                Sprite() = default;
                                                Sprite(SDL_Renderer* target, std::string id) : renderTarget(target), textureID(id) {}
                                                virtual ~Sprite() = default;
                                        
                                                void init() override final {
                                                    texture = TextureManager::get().getTexture(textureID);
                                                    SDL_QueryTexture(texture,nullptr,nullptr,&width,&height);
                                        
                                                    dest.x = entity->getComponent<Transform>().position.x;
                                                    dest.y = entity->getComponent<Transform>().position.y;
                                                    dest.w = width*entity->getComponent<Transform>().scale.x;
                                                    dest.h = height*entity->getComponent<Transform>().scale.y;
                                        
                                                    src.x = 0;
                                                    src.y = 0;
                                                    src.w = width;
                                                    src.h = height;
                                                }
                                        
                                                //appelée dans système de mouvement ?
                                                void updateDestRect() {
                                                    dest.x = entity->getComponent<Transform>().position.x;
                                                    dest.y = entity->getComponent<Transform>().position.y;
                                                    std::cout << entity->getComponent<Transform>().scale.x << " " << entity->getComponent<Transform>().scale.y << std::endl;
                                                    dest.w = width*entity->getComponent<Transform>().scale.x;
                                                    dest.h = height*entity->getComponent<Transform>().scale.y;
                                                }
                                        
                                                int width = 0;
                                                int height = 0;
                                                SDL_Rect src = {0,0,0,0};
                                                SDL_Rect dest = {0,0,0,0};
                                        
                                                std::string textureID = "";
                                                SDL_Texture* texture = nullptr;
                                                SDL_Renderer* renderTarget = nullptr;
                                                SDL_RendererFlip flip = SDL_FLIP_NONE;
                                        };
                                        

                                        Mais il me dit que TextureManager n'est pas déclaré (Components\Sprite.h|21|error: 'TextureManager' has not been declared|), alors que comme on peut le voir je l'ai bien inclus en tête du code... J'ai fait quelque chose qu'il fallait pas ? ^^'

                                        -
                                        Edité par EthanWright 27 septembre 2021 à 22:01:46

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          28 septembre 2021 à 9:39:07

                                          Mea culpa, j'ai oublié deux const dans le code, je te donne la corection ici:
                                          class EntityManager {
                                              /* un premier alias pour les unique_pointer
                                               * c'est -- typiquement -- un alias qui ne servira qu'ici
                                               * il n'y a donc pas besoin de l'exposer dans
                                               * l'accessibilité publique
                                               */
                                              using EntityPointer = sd::unique_ptr<Entity>;
                                              /* Un autre alias qui ne servira qu'ici: le tableau de
                                               * (unique_pointer sur) des entités
                                               */
                                              using EntityArray = std::vector<EntityPointer>;
                                              public:
                                                  /* on a besoin des itérateur (constants et non
                                                   * constants sur le tableau (de unique_pointer)
                                                   * d'entités
                                                   * on reprend exactement les mêmes noms que les noms
                                                   * officiels (par facilité)
                                                   */
                                                  using const_iterator = typename EntityArry::const_iteroatr;
                                                  using iterator = typename EntityArray::iterator;
                                                  EntityManager() = default;
                                                  /* toute la partie publique existante, à laquelle on ajoute */
                                                  /* les fonctions begin et end en version non const */
                                                  iterator begin(){
                                                      return entities.begin();
                                                  }
                                                  iterator end(){
                                                      return entities.end();
                                                  }
                                                  /* les fonctions begin et end en version const */
                                                  const_iterator begin() const{ // ce sont des versions
                                                                                // const, qu'on a dit
                                                      return entities.begin();
                                                  }
                                                  const_iterator end() const{ // ce sont des versions
                                                                              // const, qu'on a dit
                                                      return entities.end();
                                                  }
                                                  /* et, pour faire bonne mesure, quelques fonctions
                                                   * "exportés" supplémentaire
                                                   */
                                                  size_t size() const{// parce qu'il est toujours intéressant
                                                                      // de savoir combien d'entités il y a
                                                      return entities.size();
                                                  }
                                                  bool empty() const{// parce que c'est plus facile
                                                                     // de savoir s'il est vide que de
                                                                     // commencer à se demander combien
                                                                     // d'entités il y a
                                                      return entities.empty();
                                                  }
                                                  void clear(){ // pour le cas où l'on voudrait virer
                                                                // toutes les entités
                                                      entities.clear();
                                                  }
                                                  /* il y aura peut-être d'autres fonctions qui te paraitront utiles à l'usage
                                                   * mais celles-ci seront déjà "pas mal" et "utiles"
                                                   */
                                              private:
                                                  /* et, tant qu'à faire, on utilise les alias créés
                                                   * ici aussi
                                                   */
                                                  EntityArray entities;
                                          };
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                          Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                            28 septembre 2021 à 16:31:52

                                            Ça règle bien le soucis, merci beaucoup !

                                            Par contre j'ai toujours l'erreur avec le "TextureManager has not been declared" :'(

                                            • Partager sur Facebook
                                            • Partager sur Twitter

                                            Process terminated with status -805306369

                                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                            • Editeur
                                            • Markdown