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!
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);
}
}
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!
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 :)