Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: Impossible de draw un sprite "texturisé" dans une autre classe  (Lu 5143 fois)

0 Membres et 2 Invités sur ce sujet

I_Red_I

  • Newbie
  • *
  • Messages: 11
    • Voir le profil
Impossible de draw un sprite "texturisé" dans une autre classe
« le: Février 20, 2018, 11:38:09 am »
Bonsoir,

Je crée un jeu "tile mappé".

Je dispose d'une classe Block qui me permet de créer object Block et une classe World qui me permet de gérer mon monde (composé d'object Block).

Je m'explique, dans le constructeur de World je remplis une liste à deux dimensions d'object Block.Lors de leurs création, le constructeur de Block affecte à l'object créer un Sprite auquel il lui applique une texture. Ensuite à l'aide d'une méthode de World que j'appelle dans le Main, je draw les objects Block contenu dans World (dans la liste à deux dimensions) .. seulement j'obtiens le fameux carré blanc.

Donc le Sprite est bien là, c'est la texture qui n'existe plus. J'ai fais des recherches et j'en ai conclu que la texture était supprimé à la sortie de la classe dans laquelle elle a été crée (ce qui explique pourquoi cela marche quand je draw à partir de la classe Block). J'ai aussi cru comprendre que je devais utiliser des allocations dynamiques pour résoudre ce problème ... mais je n'y arrive pas .. vraiment .. ça fait 3 semaines que je suis dessus .. Quelqu'un pourrait il m'aider ?

Cordialement.

Voici mon code car je doute que mon explication soit suffisamment clair :

Block.hpp :

#ifndef Block_hpp
#define Block_hpp
 
#include <cstdio>
#include <string>
#include <SFML/Graphics.hpp>
 
class Block : public sf::Drawable
{
   
public:
    Block(int type, int x, int y);
    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
    {
        target.draw(block_sprite, states);
    }
protected:
    TextureManager texture;
    sf::Sprite block_sprite;
    sf::Texture *block_texture;
    int type;
};
 
#endif
------------------------------------------------------

Block.cpp

#include "Block.hpp"
#include <cstdio>
#include <iostream>
#include <SFML/Graphics.hpp>
 
Block::Block(int type, int x, int y)
{
    if (type == 0)
    {
        block_texture = &texture.getTexture(type);
    }
    else if (type == 1)
    {
        block_texture = &texture.getTexture(type);
    }
    this->type = type;
    block_sprite.setTexture(*block_texture);
    block_sprite.setPosition(x, y);
}
------------------------------------------------------

World.hpp :

#ifndef World_hpp
#define World_hpp
 
#include <cstdio>
#include <SFML/Graphics.hpp>
#include <string>
#include "Block.hpp"
 
class World
{
public:
    World();
    void render(sf::RenderWindow& window) const;
   
protected:
    Block map[20][20];
};
#endif
------------------------------------------------------

World.cpp

#include "World.hpp"
#include "Block.hpp"
#include <SFML/Graphics.hpp>
#include <string>
#include <cstdio>
 
World::World()
{
    map[0][0] = Block(0, 0, 0);
    map[1][0] = Block(1, 32, 0);
}

void World::render(sf::RenderWindow& window) const
{
    window.draw(map[0][0]);
    window.draw(map[1][0]);
}
------------------------------------------------------

main.cpp

#include <SFML/Audio.hpp>
#include <SFML/Graphics.hpp>
#include <string>
#include <iostream>
#include <cstdio>
#include "World.hpp"
 
int main(int, char const**)
{
    const int w = 800;
    const int h = 450;
 
    sf::RenderWindow window;
    sf::View view;
    World world;
    sf::Event event;
     
    window.create(sf::VideoMode(w, h), "SFML window",sf::Style::Default);
    window.setVerticalSyncEnabled(true);
     
    while (window.isOpen())
    {
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }
 
        window.clear();
         
        view.reset(sf::FloatRect(0,0,w,h));
        world.render(window);
         
        window.display();
    }
    return EXIT_SUCCESS;
}
« Modifié: Février 20, 2018, 04:33:33 pm par I_Red_I »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #1 le: Février 20, 2018, 12:58:56 pm »
Chaque bloc de ton monde charge et stocke sa propre version de la même texture. Tu ne penses pas qu'il y a un problème à ce niveau ?
Laurent Gomila - SFML developer

I_Red_I

  • Newbie
  • *
  • Messages: 11
    • Voir le profil
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #2 le: Février 20, 2018, 01:06:50 pm »
Si si bien sûr, mon code était beaucoup plus complexe, mais je l'ai simplifié au minium pour pouvoir résoudre le problème de "la texture s'affiche pas quand elle provient d'une autre classe", là je considère qu'un seul et unique Block sera crée ;) Car j'aurais pu tout aussi bien créer une classe TextureManager ou utiliser une variable de classe pour ne charger la texture que si c'est le premier object Block qui est crée, mais ce n'est pas le but ici, j'aurais toujours le même problème de texture qui disparait dans les tréfonds :o
« Modifié: Février 20, 2018, 01:10:27 pm par I_Red_I »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #3 le: Février 20, 2018, 01:36:17 pm »
Non au contraire, si la texture est gérée en dehors de Block, tu n'auras très certainement plus de problème, puisqu'elle ne sera plus chargée/copiée/détruite à tout va.
Laurent Gomila - SFML developer

I_Red_I

  • Newbie
  • *
  • Messages: 11
    • Voir le profil
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #4 le: Février 20, 2018, 04:33:44 pm »
D'accord  ;D !

De ce faite j'ai fais quelques modifications (et revu quelques notions sur les pointeurs) les voici :
  • J'ai modifié la classe Block :

    Pour rendre le cas plus concret les objets Block possède un attribut type qui leur conféra une texture relative à ce dernier ainsi que des coordonnées x et y correspondant au point d'affichage.
    Leur constructeur assigne à chaque block une référence de la texture à partir de TextureManager dans une pointeur texture, cette texture est ensuite appliqué au block_sprite de l'objet Block
    La classe Block instancie un objet TextureManager
  • J'ai modifié la classe World :

    Mon tableau map reçoit deux object Block avec en paramètre un type, et des coordonnées x et y
  • J'ai crée une classe TextureManager :

    Son constructeur charge les textures
    Sa methode getTexture() renvoit une reference de la texture relative au type mit en paramètre

Afin d'illustrer tout ça j'ai modifier le code de mon premier post afin d'éviter le flood

Après avoir fait cela, Xcode me donne une erreur (et une seul ;D) :

Semantic Issue > World.cpp:7:8: Constructor for 'World' must explicitly initialize the member 'map' which does not have a default constructor
« Modifié: Février 20, 2018, 04:36:11 pm par I_Red_I »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #5 le: Février 20, 2018, 06:13:16 pm »
Tu ne peux pas construire les éléments d'un tel tableau avec autre chose que le constructeur par défaut. Or ta classe Block n'en a pas. Il faut que tu revoie la conception de tes classes et/ou ta façon de faire.

Ensuite tu n'as rien résolu puisque chaque bloc instancie maintenant son propre TextureManager, qui va à son tour charger sa propre version des mêmes textures. L'idée d'un gestionnaire de texture est en général d'en avoir une instance unique, afin de ne pas dupliquer les textures.
Laurent Gomila - SFML developer

I_Red_I

  • Newbie
  • *
  • Messages: 11
    • Voir le profil
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #6 le: Février 20, 2018, 07:26:06 pm »
Pour ce qui est de TextureManager je pense qu'avoir passé l'objet TextureManager en static dans Block a résolu le problème, par contre pour l'erreur avec map .. je ne comprends pas ça fait plusieurs semaines que je suis sur le même problème je dois commencer à ne plus voir clair ..

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #7 le: Février 21, 2018, 06:33:19 am »
Citer
Pour ce qui est de TextureManager je pense qu'avoir passé l'objet TextureManager en static dans Block a résolu le problème
Cela va sans doute en amener d'autres : ton instance statique est construite globalement, avant d'entrer dans la fonction main() ; or dans ce constructeur tu charges des textures, et les classes de ressources de SFML comme sf::Texture utilisent elles aussi des variables globales. Bref un jour ou l'autre ça peut te péter à la figure :)

Citer
par contre pour l'erreur avec map .. je ne comprends pas ça fait plusieurs semaines que je suis sur le même problème je dois commencer à ne plus voir clair ..
Je t'ai déjà expliqué la cause de l'erreur dans ma réponse précédente, qu'est-ce qui te bloque ?
Laurent Gomila - SFML developer

I_Red_I

  • Newbie
  • *
  • Messages: 11
    • Voir le profil
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #8 le: Février 21, 2018, 10:01:18 am »
Malheureusement ne voit pas comment faire .. je pourrais sans doute crée une instance TextureManager dans le main et récupérer les textures en passant une reference l'instance TextureManager enfin quoi que cela ne marcherait pas puisque je ne crée pas les Block dans le main.

Et bien je ne vois pas comment le réaliser, si j'ai bien compris (si et seulement si) étant donné que je crée le tableau de cette manière : Block map[20][20]; je ne peux lui mettre que des objets Block provenant du constructeur par défaut (qui n'existe pas), de ce faite je ne peux pas faire cela : map[1][0] = Block(1, 0, 0);, toujours si j'ai bien compris, je devrais donc créer un constructeur par default de Block et je ne pourrais que mettre des Block issus du constructeur par default dans le tableau, c'est à dire que je ne peux faire que cela :
map[1][0] = Block();, seulement je cherche à mettre des Block "construits" (= avec des paramètres) dans ma liste .. dois-je faire des map[1][0] = Block(); et ensuite modifier les attributs avec des méthodes set ?


Edit : En utilisant uniquement le constructeur par default cela semble fonctionner (ça me fait une erreur si l'instance TextureManager est static) .. mais toujours un carré blanc  :'(
« Modifié: Février 21, 2018, 10:15:55 am par I_Red_I »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #9 le: Février 21, 2018, 11:46:42 am »
Peux-tu poster ta dernière version de la classe Block ?
Laurent Gomila - SFML developer

I_Red_I

  • Newbie
  • *
  • Messages: 11
    • Voir le profil
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #10 le: Février 21, 2018, 01:29:52 pm »
D'accord, voici la dernière (avec le constructeur par default et TextureManager non static)

Block.hpp
class Block : public sf::Drawable
{
public:
    Block(int type, int x, int y);
    Block();
    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
    {
        target.draw(block_sprite, states);
    }
protected:
    TextureManager texture;
    sf::Sprite block_sprite;
    sf::Texture *block_texture;
    int type;
};
 

Block.cpp
Block::Block()
{
    type = 0;
    block_texture = &texture.getTexture(type);
    block_sprite.setTexture(*block_texture);
    block_sprite.setPosition(0, 0);
}
Block::Block(int type, int x, int y)
{
    if (type == 0)
    {
        block_texture = &texture.getTexture(type);
    }
    else if (type == 1)
    {
        block_texture = &texture.getTexture(type);
    }
    this->type = type;
    block_sprite.setTexture(*block_texture);
    block_sprite.setPosition(x, y);
}
 

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #11 le: Février 21, 2018, 01:49:45 pm »
Tu as toujours exactement la même configuration qu'au tout début, avec chaque Block qui se trimballe ses propres versions des textures... on tourne un peu en rond là ;D

Est-ce qu'au moins maintenant tu comprends pourquoi ce genre de code produit des sprites blancs ?
Laurent Gomila - SFML developer

I_Red_I

  • Newbie
  • *
  • Messages: 11
    • Voir le profil
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #12 le: Février 21, 2018, 02:15:13 pm »
Oui c'est le moins qu'on puisse dire :'(, j'avais fais ma classe TextureManager pour résoudre le problème de carré blanc mais ça ne la pas résolu, j'ai voulu passer TextureManager en static pour résoudre le faite que chaque Block avait son instance TextureManager mais ça ma donné une autre erreur .. sérieusement, je suis maudis  :-[

Vais-je recevoir des menaces de morts si je dis non ? :o Parce que admettons, c'est moche, ça utilise de la ressource pour rien, ça mérite la mort mais admettons : J'ai un TextureManager par Block pourquoi ça changerait quelque chose sur le faite de produire des carrés blanc ? Puisque même si pour chaque Block je me réfère à SON TextureManager, chaque couple Block/TextureManager et indépendant des autres, ils possèdent leurs variables, leurs pointeurs et communiquent que ENTRE eux non ? :-X

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #13 le: Février 21, 2018, 06:34:32 pm »
Si tu instanciais un bloc et l'utilisais directement, ça fonctionnerait. Le problème c'est que tu crées/copies/détruis tes blocs :

map[1][0] = Block(...);
Ici tu crées un nouveau bloc et tu le copies dans map[1][0]. Donc tout ce qui se trouve dans l'instance de Block va être copié (donc créé ailleurs en mémoire). Or un sprite stocke un pointeur direct sur sa texture (sf::Texture*). Donc si tu fais sprite.setTexture(texture) puis que texture est copié ailleurs en mémoire, sprite se retrouve avec un pointeur vers une zone mémoire qui ne contient plus sa texture (il ne peut pas magiquement mettre à jour ses membres pour "suivre" le pointeur qui a bougé en mémoire). Donc il affiche du caca (souvent un rectangle blanc).

J'espère que c'est plus clair :(
Laurent Gomila - SFML developer

I_Red_I

  • Newbie
  • *
  • Messages: 11
    • Voir le profil
Re: Impossible de draw un sprite "texturisé" dans une autre classe
« Réponse #14 le: Février 21, 2018, 06:38:14 pm »
ENFIN ! Je me hais ! L'illumination ! L'humiliation ! Pourquoi chercher coute que coute à mettre des Blocks dans ma liste ? Pourquoi chercher à créer une liste d'object Block quand on peut créer .. une liste dynamique et y mettre des référence de Block ?! et MAGIE les textures sont là et plus aucune erreur .. quelle libération, c'est si peu mais je ne cache pas mon euphorie. Ceci dit mon dernier problème reste le faite d'associer à chaque Block un TextureManager  ???

Edit :
j'ai écris ce post avant de voir celui que tu as postés avant, je m'y penche tout de suite :P

Edit :
Oui merci beaucoup c'est plus clair :)
Problème résolu ! :D
J'ai maintenant toutes les textures qui sont bien présentes et chargées une et une seule fois ;D
Il se fait tard, je viendrais exposer ma méthode demain, ça pourra toujours servir à quelqu'un ???

Edit :
Ce que j'ai fait c'est que World et Block possèdent un pointeur TextureManager et que je crée un TextureManager dans le main que je passe en paramètre du constructeur World qui fait pointer son TextureManager vers celui passé en reference dans constructeur. De la à chaque création d'instance Block, je passe en paramètre le pointeur TextureManager de World ainsi chaque Block le stock, et ne possèdent pas un TextureManager mais le même un pointeur pointant vers le l'instance TextureManager crée dans le main.

En clair je fais passer ma référence du TextureManager crée dans le main jusqu'a mes instances de Block.

Ps: Je ne sais pas c'était clair ???

« Modifié: Février 22, 2018, 02:13:57 pm par I_Red_I »

 

anything