minicarte pour un jeu en OpenGL

minicarte pour un jeu en OpenGL - C++ - Programmation

Marsh Posté le 02-03-2007 à 20:08:38    

Bonjour
 
je développe un petit jeu de "stratégie" et je galère pour l'affichage de la mini carte.
Pour une carte de 100*100 tout se passe bien mais quand je choisis une carte 1000*1000 (par exemple) mon nombre d'images/seconde s'écroule.
Je cherche donc une idée pour augmenter la vitesse de l'affichage.
 
J'étais parti sur le principe d'afficher toujours une image réduite de la carte (un fichier bmp) et de dessiner dessus les cases noires (non découvertes par le joueur). Comme ça, plus on avance dans le jeu moins il y a de cases noires à afficher; ça compense un  peu les temps de calcul et d'affichage des unités.
Le problème c'est que je n'ai pas trouvé de façon efficace d'afficher ces cases noires...
 

Code :
  1. void Carte::afficherMini(const Point& ecran) const
  2. {
  3.   //calcul de l'emplacement à l'écran et du nombre de pixels par case:
  4.   float pasX=float(TAILLE_MINI_CARTE)/largeur;
  5.   float X=LARGEUR/100, y=HAUTEUR-(float(HAUTEUR)/100+TAILLE_MINI_CARTE);
  6.  
  7.   //affichage de l'image réduite de la carte:
  8.   glBindTexture(GL_TEXTURE_2D,fondMiniMap->numero);
  9.   glCallList(FOND_MINI_MAP);
  10.  
  11.   //c'est à partir d'ici que c'est long. Si je commente cette partie, le jeu tourne beaucoup plus vite
  12.   glDisable(GL_TEXTURE_2D);
  13.  
  14.   glColor3f(0,0,0);
  15.   glBegin(GL_QUADS);
  16.   float xDebut,xFin;
  17.   for (unsigned int i=0;i<largeur;i++) //Pour chaque ligne de la carte
  18.   {
  19.     xDebut=X;//On marque le début d'une série de cases noires avec ceci
  20.     xFin=X;//On marque ou on en est dans la ligne
  21.     for(unsigned int j=0;j<largeur;j++)//Pour chaque case de la ligne
  22.     {
  23.       if(devoilee[i*largeur+j]) //Si la case n'est pas noire
  24.       {
  25.         if(xFin!=xDebut)//Si != alors c'est qu'il y a des cases noires à dessiner
  26.         {
  27.           glVertex2f(xDebut,y); //on dessine une ligne de cases noires
  28.           glVertex2f(xFin,y);
  29.           glVertex2f(xFin,y+pasX);
  30.           glVertex2f(xDebut,y+pasX);
  31.          xDebut=xFin;
  32.         }
  33.         xDebut+=pasX;//On réinitialise après cette case
  34.       }
  35.       xFin+=pasX;//On avance
  36.     }
  37.     if(xDebut!=xFin) //Si la ligne se finit par une (série de) case(s) noire(s)
  38.     {
  39.       glVertex2f(xDebut,y); //Alors on les dessine
  40.       glVertex2f(xFin,y);
  41.       glVertex2f(xFin,y+pasX);
  42.       glVertex2f(xDebut,y+pasX);
  43.     }
  44.     y+=pasX;
  45.   }
  46.  
  47.   glEnd();
  48.  
  49.   ...
  50.   //la suite ne pose pas de problème.


 
Si quelqu'un a une petite idée pour m'aider elle sera la bienvenue merci.


---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 02-03-2007 à 20:08:38   

Reply

Marsh Posté le 02-03-2007 à 20:43:21    

Tu fais à chaque redraw des calculs que tu pourrais ne faire que lorsque de nouvelles cases se dévoilent, fait les uniquement à ce moment là et stocke le résultat dans une Liste, ensuite quand tu affiche, tu ne fais qu'un CallList, ce sera pas miraculeux mais tu devrais gagner un peu ainsi.

Message cité 1 fois
Message édité par 0x90 le 02-03-2007 à 20:43:41

---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 02-03-2007 à 22:38:12    

0x90 a écrit :

Tu fais à chaque redraw des calculs que tu pourrais ne faire que lorsque de nouvelles cases se dévoilent, fait les uniquement à ce moment là et stocke le résultat dans une Liste, ensuite quand tu affiche, tu ne fais qu'un CallList, ce sera pas miraculeux mais tu devrais gagner un peu ainsi.


 
Ca fonctionne plutôt pas mal j'ai remplacé par:

Code :
  1. if(redessinerMiniCarte>MAJ_MINI_CARTE)
  2.     redessinerCasesNoires();
  3.  
  4.   glCallList(CASES_NOIRES_MINI_MAP);


 
avec redessinerMiniCarte un entier non signé incrémenté a chaque fois qu'une case est dévoilée.
et MAJ_MINI_CARTE une constante fixée à 200 (un écran entier fait 400 cases environ).
 
c'est beaucoup plus stable au niveau des images secondes mais j'ai peur que le problème ne soit que reporté, que cela repose problème le jour ou beaucoup d'unités explorerons la carte en même temps... On verra plus tard... Peut être en faisant dépendre MAJ_MINI_CARTE de la vitesse du jeu...
 
 
edit: quel boulet, j'ai oublié de dire merci. Merci!


Message édité par ptitchep le 02-03-2007 à 22:40:30

---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 03-03-2007 à 20:20:07    

Salut,
 
comme l'interruption l'a déjà précisé plus haut, pas besoin de calculer (FPS) fois par seconde ta mini-map, et même en 1 seconde pas ou peu de changements ont le temps d'être opérés. A la limite même, tu peux restreindre son rafraîchissement à toutes les 2-3 secondes, voire plus encore, pour rendre compte de la situation de manière suffisamment réaliste, et sans pénaliser le joueur.

Reply

Marsh Posté le 05-03-2007 à 01:39:00    

lkolrn a écrit :

Salut,
 
comme l'interruption l'a déjà précisé plus haut, pas besoin de calculer (FPS) fois par seconde ta mini-map, et même en 1 seconde pas ou peu de changements ont le temps d'être opérés. A la limite même, tu peux restreindre son rafraîchissement à toutes les 2-3 secondes, voire plus encore, pour rendre compte de la situation de manière suffisamment réaliste, et sans pénaliser le joueur.


 
En effet, mettre à jour la mini carte chaque fois que X cases sont dévoilées ne semble pas être une bonne solution, cela me retarde trop.
J'ai donc placé cette mise à jour dans la partie calcul et non plus affichage et je l'execute toutes les 5 secondes. Voilà pourquoi:
http://forum.hardware.fr/hfr/Progr [...] m#t1523451
 
Par contre voilà mon affichage alors que rien ne bouge dans le jeu:
750 itérations /sec    9 unites               =>c'est bas
897 itérations /sec    9 unites
899 itérations /sec    9 unites
900 itérations /sec    9 unites
922 itérations /sec    9 unites
799 itérations /sec    9 unites               =>5 sec plus tard, c'est bas!!!
870 itérations /sec    9 unites
868 itérations /sec    9 unites
931 itérations /sec    9 unites
881 itérations /sec    9 unites
803 itérations /sec    9 unites                =>5 sec encore plus tard c'est encore plus bas!
804 itérations /sec    9 unites
884 itérations /sec    9 unites
868 itérations /sec    9 unites
915 itérations /sec    9 unites
920 itérations /sec    9 unites
839 itérations /sec    9 unites              =>ici il a falu 6 sec (entières ça a pu arriver à 5.00000001 seconde.)
 
 
Donc je recherche une idée pour diminuer ce temps de calcul de la liste d'affichage afin d'éviter de perdre autant de temps.
Remplir un tableau avec les sommets puis un appel à glDrawArrays() est-il plus lent ou plus rapide que tous ces appels à glVertex() puis glCallList()?
 
Et un glDrawArrays() ensuite stocké dans une Liste??
 
 


---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 05-03-2007 à 10:29:59    

1) création d'une texture de taille raisonnable.
2) MAJ / coloriage à la main (lire: coté cpu).
3) upload de la région modifiée (google: glTexSubImage2D).
4) goto 2.
 
option a) streamage de la texture avec pbo.
option b) dessinage sur place avec fbo + shaders qui vont bien.

Message cité 1 fois
Message édité par tbp le 05-03-2007 à 10:34:21
Reply

Marsh Posté le 13-03-2007 à 17:15:20    

Bon alors le glDrawArrays() n'augmente pas la vitesse, en tout cas, ça n'est pas vraiment visible.
 

tbp a écrit :

1) création d'une texture de taille raisonnable.
2) MAJ / coloriage à la main (lire: coté cpu).
3) upload de la région modifiée (google: glTexSubImage2D).
4) goto 2.
 
option a) streamage de la texture avec pbo.
option b) dessinage sur place avec fbo + shaders qui vont bien.


 
 
Merci, je comprend bien le principe exposé, mais je ne sais pas du tout comment faire le coloriage à la main. Sans doute parce que je ne comprend pas ce que tu me demandes de lire. C'est à dire "coté cpu?"


---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 13-03-2007 à 19:22:01    

{ relecture du code }
Bon. Disons que vous avez votre carte complète. Vous l'uploadez, sans modifications et n'y touchez plus.
Ensuite vous créez une sorte de texture de masquage (ou genre).
 
{ attention les yeux, mauvais pseudo code }

Code :
  1. enum { w = 128, h = w };
  2. char pixels[h*w];
  3. std::memset(pixels, 0xFF, sizeof(pixels));
  4. glTexSubImage2D(..., pixels);


 
Vous la mettez à jour, en la trouant

Code :
  1. foreach_case_noire
  2. pixels[y*w + y] = 0;
  3. glTexSubImage2D(..., pixels);


 
A l'affichage, peindre la texture de la carte puis combinez les trous. L'avantage étant que la carte p-ê détaillée à loisir et qu'en utilisant un seul channel et une résolution minable, la texture de masquage est ridiculement petite (avec 1 texel = 1 case noire) et pas besoin de Bresenham & compagnie.

Message cité 1 fois
Message édité par tbp le 13-03-2007 à 19:23:35
Reply

Marsh Posté le 13-03-2007 à 19:46:15    

tbp a écrit :


Vous la mettez à jour, en la trouant

Code :
  1. foreach_case_noire
  2. pixels[y*w + y] = 0;
  3. glTexSubImage2D(..., pixels);


 
A l'affichage, peindre la texture de la carte puis combinez les trous.


Ok je suppose qu'il existe un moyen de rendre une couleur transparente quand j'applique la texture.
 
Par contre

tbp a écrit :

L'avantage étant que la carte p-ê détaillée à loisir et qu'en utilisant un seul channel et une résolution minable, la texture de masquage est ridiculement petite (avec 1 texel = 1 case noire) et pas besoin de Bresenham & compagnie.


Si ma carte fait 1000 cases sur 1000 cases, ce qui correspond à la plus grande carte de mon jeu (enfin je pense ça fait vraiment grand à parcourir), ça me donne quand même 1 000 000 de texels. C'est toujours ridicule?
 
C'est à dire un seul channel?? (question posée avant la recherche google, pendant que j'y suis)


---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 13-03-2007 à 20:32:17    

5 secondes, ça passait inapercu sur une grande carte. Par contre sur une petite, c'était génant. J'ai donc réduit à deux secondes.
Bizarrement cela n'a pas changé la fréquence de mes ralentissements.  
Ce n'est donc pas la minicarte qui me ralentissait cela dit je n'ai rien contre le fait de gagner de la vitesse et je vais continuer à chercher à optimiser.
Par contre j'aimerais comprendre pourquoi mon prog ralentit toutes les 5 secondes alors que la minicarte était la seule chose que je faisais à intervalle régulier. Tout le reste est constant puisque quand je teste je ne fais rien, je me contente de regarder les nombre d'itérations par seconde défiler dans ma console...  
Alors pourquoi cette baisse soudaine toutes les 5 secondes et cette remontée progressive? Est-ce lié à openGL, SDL, linux...?
Si quelqu'un a une idée...


---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 13-03-2007 à 20:32:17   

Reply

Marsh Posté le 14-03-2007 à 09:17:13    

ptitchep a écrit :

Ok je suppose qu'il existe un moyen de rendre une couleur transparente quand j'applique la texture.
C'est à dire un seul channel?? (question posée avant la recherche google, pendant que j'y suis)


Une texture GL_ALPHA a 1 channel/composante,  GL_LUMINANCE_ALPHA 2, GL_RBG 3, GL_RGBA 4 etc...
Classiquement l'alpha channel est utilisé pour la transparence et le masquage. Avec les shaders, tout est concevable.
 
 

ptitchep a écrit :

Si ma carte fait 1000 cases sur 1000 cases, ce qui correspond à la plus grande carte de mon jeu (enfin je pense ça fait vraiment grand à parcourir), ça me donne quand même 1 000 000 de texels. C'est toujours ridicule?


Plus franchement. Mais on peut supposer que vos MAJ sont localisées, et comme je l'ai dit dans le premier message on peut uploader une région de la texture au lieu de la texture complète.
Ensuite on peut compliquer la solution à loisir.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed