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

Auteur Sujet: VertexArray - Jeu qui plante  (Lu 3541 fois)

0 Membres et 1 Invité sur ce sujet

FatihLD

  • Newbie
  • *
  • Messages: 19
  • A dix-mille pieds sous l'océan
    • Voir le profil
    • E-mail
VertexArray - Jeu qui plante
« le: Septembre 16, 2017, 09:37:42 pm »
Salut!

Je suis entrain de travailler sur un jeu. J'ai crée une tilemap mais la map était tellement grande (2000 x 2000 tiles de 16 pixels) que j'avais genre 5 fps (même en Release), j'ai donc décidé de n'afficher que les tiles qui sont dans une rectangle autour du joueur. Pour faire je met à jour ma map tous les secondes sur un thread (pour pas que ça gène la boucle principale, j'ai un for qui traverse 4 millions de tiles quand même). J'suis très content du résultat.

Mais j'ai un problème, quand mon joueur vas vers le bas et qu'il dépasse les 5200 pixels environ en Y, le jeu plante (En X, je peux aller aussi loin que je veux). Le problème viens de la tilemap (quand je l'enlève il n'y a plus de problème). Voici mon code qui met à jour la tilemap:

void Tilemap::uptade(sf::FloatRect rect) //On prend en paramètre la rectangle dont on doit afficher les tiles à l'intérieur
{
    //Tilemap
    sf::VertexArray vertex; //Tableau temporaire car sur le tableau principale on doit clear et ça clignote, moche
    unsigned width = rect.width / mTileSize.x + mTileSize.x * 16; //Nombre de tile par ligne à afficher
    unsigned height = rect.height / mTileSize.y + mTileSize.y * 16; //Nombre de tile par colonne à afficher
    vertex.setPrimitiveType(sf::Quads);
    vertex.resize(width * height * 4); //On redimmensionne le tableau
   
    for(unsigned i = 0; i < mLevelSize.x; ++i)
        for(unsigned j = 0; j < mLevelSize.y; ++j)
        {
            int tileX = i * mTileSize.x; //Position X du tile courant dans le jeu
            int tileY = j * mTileSize.y; //Position Y du tile courant dans le jeu

            //Si le tile courant est dans la rectangle, on l'ajoute au tableau
            if(rect.intersects(sf::FloatRect(tileX, tileY, mTileSize.x, mTileSize.y)))
            {
                //Comme dans le tutoriel à partir de là
                int tileNumber = mLevel[i + j * mLevelSize.x];
                int tu = tileNumber % (mTileset.getSize().x / mTileSize.x);
                int tv = tileNumber / (mTileset.getSize().x / mTileSize.x);
                sf::Vertex* quad = &vertex[(i + j * width) * 4];
               
                //Quand j'enlève les 8 lignes suivants, aucun problème, ça vient d'ici
                quad[0].position = sf::Vector2f(i * mTileSize.x, j * mTileSize.y);
                quad[1].position = sf::Vector2f((i + 1) * mTileSize.x, j * mTileSize.y);
                quad[2].position = sf::Vector2f((i + 1) * mTileSize.x, (j + 1) * mTileSize.y);
                quad[3].position = sf::Vector2f(i * mTileSize.x, (j + 1) * mTileSize.y);
                quad[0].texCoords = sf::Vector2f(tu * mTileSize.x, tv * mTileSize.y);
                quad[1].texCoords = sf::Vector2f((tu + 1) * mTileSize.x, tv * mTileSize.y);
                quad[2].texCoords = sf::Vector2f((tu + 1) * mTileSize.x, (tv + 1) * mTileSize.y);
                quad[3].texCoords = sf::Vector2f(tu * mTileSize.x, (tv + 1) * mTileSize.y);
            }
        }
    mVertices = vertex; //On met à jour la map
}

Comme il est écrit, quand j'enlève les 8 lignes qui définit la position dans le jeu du tile et la position dans la texture, tous vas bien. Je ne comprend vraiment pas pourquoi :/

Merci de bien vouloir m'aider!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #1 le: Septembre 17, 2017, 12:06:48 pm »
Je ne sais pas pourquoi ces 8 lignes de code en particulier provoquent le crash, mais je peux déjà te dire que si tu partages des données entre plusieurs threads (le vertex array), sans protéger les accès -- potentiellement coucurrents -- à celles-ci, tu vas avoir de sérieux problèmes.

Tu peux commencer par utiliser ton debugger pour voir où, comment et dans quelles circonstances ça crashe. Ca aiderait déjà beaucoup, plutôt que d'essayer de jouer aux devinettes. Mais je vais quand même tenter ma chance : avec les jeux d'arrondis, il se pourrait que ta boucle retienne "width + 1" et/ou "height + 1" tuiles, et que tu dépasses donc la capacité de ton vertex array. Vérifie tes calculs, et/ou remplace width par width + 1 et height par height + 1 pour voir.

Ensuite, ton approche n'est pas la bonne. Déjà, ton algorithme est loin d'être le plus efficace, tu pourrais aboutir au même résultat sans double-boucle et en moins de 5 lignes de code. En effet, puisque tes tuiles sont organisées en quadrillage régulier aligné sur les axes, et que ta vue est également un rectangle aligné sur les axes, tu peux retrouver la fourchette de tuiles à afficher avec de simples divisions de coordonnées. Parcourir et tester toutes les tuiles est complètement inutile.

Exemple : (à creuser, c'est juste pour l'inspiration)
unsigned int firstTileX = std::floor(rect.left / mTileSize.x);
unsigned int lastTileX = std::ceil((rect.left + rect.width) / mTileSize.x);
unsigned int firstTileY = std::floor(rect.top / mTileSize.y);
unsigned int lastTileY = std::ceil((rect.top + rect.height) / mTileSize.y);
... et ensuite tu remplis ton vertex array avec les tuiles qui se trouvent entre les colonnes firstTileX et lastTileX, et entre les lignes firstTileY et lastTileY.

Autre piste à creuser : construis un vertex array par ligne de tuiles, et au moment de l'affichage, tu pourras n'envoyer que les tuiles visibles, sans avoir à reconstruire constamment un vertex array. Ca fait plus d'appels à la fonction draw (un par ligne visible), mais ça t'évite beaucoup de code et d'allocations mémoires, donc à voir.

Avec ces optimisations, tu devrais largement pouvoir te passer d'un thread, et beaucoup de problèmes.
« Modifié: Septembre 17, 2017, 12:09:02 pm par Laurent »
Laurent Gomila - SFML developer

FatihLD

  • Newbie
  • *
  • Messages: 19
  • A dix-mille pieds sous l'océan
    • Voir le profil
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #2 le: Septembre 17, 2017, 05:53:37 pm »
Merci d'avoir répondu, j'ai suivi tes supers conseils, j'ai changé le code. Maintenant, au lieu de tester les 4 millions de tiles à chaque fois, je place directement dans le tableau ceux qui sont dans la rectangle. Et comme j'ai plus une long boucle, je met à jour ma map directement dans la boucle principale et ça ne gène plus le jeu, donc plus de thread. :D

void Tilemap::uptade(sf::FloatRect rect)
{
    unsigned firstTileX = abs(std::floor(rect.left / mTileSize.x));
    unsigned lastTileX = abs(std::ceil((rect.left + rect.width) / mTileSize.x));
    unsigned firstTileY = abs(std::floor(rect.top / mTileSize.y));
    unsigned lastTileY = abs(std::ceil((rect.top + rect.height) / mTileSize.y));
    unsigned width = rect.width / mTileSize.x;
    unsigned height = rect.height / mTileSize.y;

    mVertices.clear();
    mVertices.setPrimitiveType(sf::Quads);
    mVertices.resize((width + mTileSize.x) * (height + mTileSize.y) * 4);

    for(unsigned i = firstTileX; i < lastTileX; ++i)
        for(unsigned j = firstTileY; j < lastTileY; ++j)
        {
            int tileNumber = mLevel[i + j * mLevelSize.x];
            int tu = tileNumber % (mTileset.getSize().x / mTileSize.x);
            int tv = tileNumber / (mTileset.getSize().x / mTileSize.x);
            sf::Vertex* quad = &mVertices[(i + j * width) * 4];

            quad[0].position = sf::Vector2f(i * mTileSize.x, j * mTileSize.y);
            quad[1].position = sf::Vector2f((i + 1) * mTileSize.x, j * mTileSize.y);
            quad[2].position = sf::Vector2f((i + 1) * mTileSize.x, (j + 1) * mTileSize.y);
            quad[3].position = sf::Vector2f(i * mTileSize.x, (j + 1) * mTileSize.y);
            quad[0].texCoords = sf::Vector2f(tu * mTileSize.x, tv * mTileSize.y);
            quad[1].texCoords = sf::Vector2f((tu + 1) * mTileSize.x, tv * mTileSize.y);
            quad[2].texCoords = sf::Vector2f((tu + 1) * mTileSize.x, (tv + 1) * mTileSize.y);
            quad[3].texCoords = sf::Vector2f(tu * mTileSize.x, (tv + 1) * mTileSize.y);
        }
}

Citer
il se pourrait que ta boucle retienne "width + 1" et/ou "height + 1" tuiles, et que tu dépasses donc la capacité de ton vertex array

J'ai déjà eu ce problème mais je l'avais réglé en ajoutant 1 tile à width quand je resize le tableau (comme tu peux le voir dans code). Et effectivement ça aussi ça faisait planter le jeu.

Sinon j'ai debug, il me dit que ça vient de la ligne "quad[0].." comme je l'avais deviné. Et il a reçu ce signal là quand le jeu a planté.
Program received signal SIGSEGV, Segmentation fault.

Je ne sais pas ce que ça veut dire, merci de m'aider!

FatihLD

  • Newbie
  • *
  • Messages: 19
  • A dix-mille pieds sous l'océan
    • Voir le profil
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #3 le: Septembre 20, 2017, 04:17:16 pm »
Bonjour!

Je crois que j'ai trouvé la source de mes problèmes, je n'ai pas encore testé mais il y a rien d'autre.
Alors j'tiens à dire que j'suis parti loin pour trouver ça, en gros "fallais y penser".
Le tableau d'int type C de ce style:
int tableau {0,1,2,3,4};
a une taille maximum de seulement entre 1 mb ou au plus 3, or un int fait 4 bytes donc un int array ne peut contenir qu'environ 250000 nombres (ou 700000 si 3mb), enfin un peu moins car le tableau stock d'autres informations de base. Moi dans ce tableau j'ai mit un level de 2000*2000 qui fais 4 millions de nombres, ce qui dépasse largement ce que ce tableau peut contenir. Aussi, mes 5000 pixels vers le bas font 300 tiles et 300x2000 ca fait environ 650000 nombres, et c'est pile ce que mon tableau peut contenir avec 3mb, c'etait donc pas un hasard si à cette distance le jeu crashait. Il faut donc que j'utilise un std::vector. Faudrais peut-être le préciser dans le tutoriel.
Merci pour ton aide Laurent!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #4 le: Septembre 20, 2017, 08:24:50 pm »
C'est la pile qui a une taille limitée. Or sf::VertexArray utilise en interne un std::vector, qui ne sera en rien différent de ce que tu voudrais faire, et qui alloue sa mémoire sur le tas. Donc pas de limitation de ce côté-là.
Laurent Gomila - SFML developer

FatihLD

  • Newbie
  • *
  • Messages: 19
  • A dix-mille pieds sous l'océan
    • Voir le profil
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #5 le: Septembre 20, 2017, 09:26:51 pm »
C'est la pile qui a une taille limitée. Or sf::VertexArray utilise en interne un std::vector, qui ne sera en rien différent de ce que tu voudrais faire, et qui alloue sa mémoire sur le tas. Donc pas de limitation de ce côté-là.

Oui mais je ne parle pas du VertexArray mais de ça:
const int level[] =
    {
        0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
        1, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3,
        0, 1, 0, 0, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0,
        0, 1, 1, 0, 3, 3, 3, 0, 0, 0, 1, 1, 1, 2, 0, 0,
        0, 0, 1, 0, 3, 0, 2, 2, 0, 0, 1, 1, 1, 1, 2, 0,
        2, 0, 1, 0, 3, 0, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1,
        0, 0, 1, 0, 3, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1,
    };
Qui ne peut contenir que un nombre limité de tuiles. C'est ça qu'il faut que je remplace par un array ou un vector.

Mais en tous cas merci pour m'avoir aidé, mon problème est résolu :)
« Modifié: Septembre 20, 2017, 09:28:32 pm par FatihLD »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #6 le: Septembre 21, 2017, 06:26:58 am »
Tu as un tel tableau de 2000x2000 dans ton code ??
Laurent Gomila - SFML developer

FatihLD

  • Newbie
  • *
  • Messages: 19
  • A dix-mille pieds sous l'océan
    • Voir le profil
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #7 le: Septembre 21, 2017, 04:45:30 pm »
Tu as un tel tableau de 2000x2000 dans ton code ??

Bah où tu veux que je garde les numéros de mes tuiles? Quand t'as une map de 2000x2000 tu as forcément un tableau de 4'000'000 d'int. Il faut un numéro qui correspond à chaque tile (c'est le principe même d'un tilemap non?). Sinon comment le programme peut savoir si ce tile là c'est de la terre ou de l'eau etc. Et sinon je ne creer pas un tableau à chaque uptade, je le créer qu'une fois dans le constructeur puis je l'utilise dans la fonction uptade :)

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #8 le: Septembre 21, 2017, 07:31:29 pm »
Oui mais pas un tableau hard-codé comme ça... ?? Et même s'il n'est pas hard-codé, je ne vois pas pourquoi il serait déclaré sur la pile.
Laurent Gomila - SFML developer

FatihLD

  • Newbie
  • *
  • Messages: 19
  • A dix-mille pieds sous l'océan
    • Voir le profil
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #9 le: Septembre 21, 2017, 08:53:02 pm »
Oui mais pas un tableau hard-codé comme ça... ?? Et même s'il n'est pas hard-codé, je ne vois pas pourquoi il serait déclaré sur la pile.

Mdrr j'vois vraiment pas de quoi tu parles ^^

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re: VertexArray - Jeu qui plante
« Réponse #10 le: Septembre 22, 2017, 06:30:45 am »
Laisse tomber, le plus important est que tu aies résolu ton problème ;)
Laurent Gomila - SFML developer