Partage
  • Partager sur Facebook
  • Partager sur Twitter

[SDL2]N'imprimer qu'une partie de l'écran

Attention c'est long !

Sujet résolu
    24 septembre 2018 à 21:26:51

    J’essaye actuellement de créer un petit logiciel en C avec la bibliothèque SDL 2. 

    Pour l’instant je n’ai pas une idée très conçue de ce que je veux faire. Néanmoins, il y a quelque chose sur laquelle je bloque.

    Mon code est assez long et, comme vous pouvez vous en douter, est constitué de plusieurs fichiers. C’est pour cette raison que je ne vais pas tout mettre mais je vais vous expliquer la logique rapidement.

    Le programme démarre dans un « Main » avec une boucle classique pour éviter que le programme ne se termine. Durant cette boucle, je récupère les touches pressées. Il s’agit de faire bouger un personnage de case en case sur une carte. Dans ma boucle, je teste donc les touches appuyées et je renvoie le cas échéant vers une fonction qui va effacer l’écran et imprimer tous les sprites au bon endroit. Aucune difficulté la dessus, je maitrise.

    La ou ça se complique, c’est lorsque je place mon personnage face à un « PNJ » et que j’appuie sur la touche ‘p’. J’ai fais en sorte dans mon « Main » que ça appelle une fonction de dialogue.

    S’ouvre alors une boite de dialogue avec du texte et deux boutons réponse. Dans cette fonction de dialogue, j’ai mis en place une seconde boucle qui va tester lorsqu’on appuie sur la souris ses coordonnées pour savoir si la personne appuie sur le premier bouton ou le second. A noter que la boite de dialogue ne prend pas toute la fenêtre, de telle sorte qu’on voit encore « l’arrière plan ». De plus, les boutons ne sont pas des vrais boutons mais du simple texte. 

    On sort de cette seconde boucle en appuyant sur la touche échappe, ce qui renvoie alors à la boucle principale.

    Voici ce que ça donne en image : 

    Cap

    Pour cependant que l’on identifie le texte comme des boutons, je veux qu’au passage de la souris (sans clic), le texte initialement blanc devienne rouge. Et c’est la que ça coince.

    Je ne veux pas « SDL_RenderClear » parce que ça m’obligerait : 

    - ou bien à passer à ma fonction de dialogue tous les éléments de l’arrière plan pour les réimprimer à l’écran,

    - ou, au sein de ma fonction de dialogue, appeler la fonction utilisée dans le « Main » pour imprimer tous les sprites.

    Je suis sur qu’il y a un moyen de faire ce que je souhaite à savoir : n’imprimer à l’écran qu’une partie du « Renderer » et conserver le reste en l’état.

    J’ai fais beaucoup (mais vraiment beaucoup !) de recherche sur le net pour trouver une solution.

    J’ai essayer à travers un SDL_RenderSetViewport. Le résultat est inattendu. Lorsque j’amène ma souris sur le bouton « Oui », le fenêtre de dialogue clignote. Plus précisément, au gré des mouvements de ma souris sur le bouton « Oui », tantôt ma boite de dialogue est présente (mais sans le bouton « Oui » en couleur rouge, il reste en blanc), tantôt la boite de dialogue disparait et laisse apparaitre tout mon arrière plan. Ce qui est bizarre c’est que le résultat est donc différent alors que je suis toujours dans la boucle de ma fonction de boite de dialogue et que c’est le même code qui est exécuté ! 

    J’ai peut être une piste de réflexion mais j’atteints alors mes limites en connaissance technique. J’ai pu lire que dans l’esprit de la SDL 2, il n’était pas prévu de faire un SDL_RenderPresent que d’une partie de l’écran, puisque ce qui a été précédemment imprimé à l’écran n’a pas vocation à être réutilisé, et que l’intégrité de ces données dans la mémoire de la carte vidéo n’est pas assurée. Je m’interroge alors sur l’utilité de SDL_RenderSetViewport.

    Mon message est donc une bouteille à la mer (puisque je doute que quiconque aura le courage de tout lire !), mais si vous avez une solution ou une explication, je suis preneur.

    Ci dessous le code de la seconde boucle de la boite de dialogue.

     while (continuer)
        {
            while (SDL_PollEvent(&event))
            {
                   switch (event.type)
                {
                    case SDL_KEYDOWN:
                        switch (event.key.keysym.sym)
                    {
                        case SDLK_ESCAPE:
                            continuer=0;
                            break;
                            
                    }
                        break;
                        
                    case SDL_MOUSEMOTION:
                        if((event.motion.x>positionBoutonReponseUn.x)&&(event.motion.x<positionBoutonReponseUn.x+positionBoutonReponseUn.w)&&(event.motion.y>positionBoutonReponseUn.y)&&(event.motion.y<positionBoutonReponseUn.y+positionBoutonReponseUn.h))
                        {
                            SDL_RenderSetViewport(sdlrenderer, &positionBoutonReponseUn);
                            BoutonReponseOui=TTF_RenderText_Blended(police, "Oui", CouleurRouge);
                            BoutonReponseOuiTexture=SDL_CreateTextureFromSurface(sdlrenderer, BoutonReponseOui);
                         
                            
                         
                            SDL_RenderCopy(sdlrenderer, BoutonReponseOuiTexture, NULL, &positionBoutonReponseUn);
                            SDL_RenderPresent(sdlrenderer);
                           
                            
                        }
                     
                        
                        break;
                   
                    
                }
    
            }
             usleep(50000);
        }


    (J'ai volontairement supprimé tous les "Free" de surfaces et "Destroy" de textures pour épurer le code et ne laisser que le stricte minimum afin de comprendre pourquoi j'obtenais des résultats différents avec le même code).

    Merci beaucoup.

    Coldpe

    -
    Edité par coldpe 24 septembre 2018 à 21:28:15

    • Partager sur Facebook
    • Partager sur Twitter
      25 septembre 2018 à 14:10:39

      Hello,

      Lorsque tu as dessiné l'écran qui vient se superposer à ton background, tu y as mis toutes les infos: le fond, le cadre, les textes. Et tous ça se trouve dans le rdr.

      Il suffit donc de modifier une partie du rdr et de le ré-afficher.

      Voici un petit exemple qui change la couleur du texte:

      	SDL_SetRenderDrawColor(rdr,0x40,0x40,0x40,0xff);
      	SDL_RenderClear(rdr);
      	SDL_RenderPresent(rdr);
      	
      	SDL_Color color1={0xff,0x00,0x00,0xff};
      	SDL_Color color2={0x00,0xff,0x00,0xff};
      	SDL_Color *pc=&color1;
      	while(!quitPgm()) {
      		SDL_Surface *text=TTF_RenderText_Blended(font,"Hello",*pc);
      		SDL_Texture *tx_text=SDL_CreateTextureFromSurface(rdr,text);
      		SDL_FreeSurface(text);
      		SDL_Rect text_coo;
      		SDL_QueryTexture(tx_text,NULL,NULL,&text_coo.w,&text_coo.h);
      		text_coo.x=(WIN_WIDTH-text_coo.w)/2;
      		text_coo.y=(WIN_HEIGHT-text_coo.h)/2;
      		SDL_RenderCopy(rdr,tx_text,NULL,&text_coo);
      		SDL_DestroyTexture(tx_text);
      		SDL_RenderPresent(rdr);
      		pc=pc==&color1 ? &color2 : &color1;
      		SDL_Delay(1000);
      	}

      -
      Edité par edgarjacobs 25 septembre 2018 à 14:13:56

      • Partager sur Facebook
      • Partager sur Twitter

      On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

        25 septembre 2018 à 14:58:51

        Merci de ta réponse.

        Je suis bien d'accord avec toi mais il se trouve que ça n'a pas l'air de fonctionner... Ta solution revient à écrire mon code sans le SDL_RenderSetViewport(sdlrenderer, &positionBoutonReponseUn); ce que j'ai fais mais ça ne fonctionne pas mieux... 

        En réalité, le résultat est le même que décrit dans mon post initial sauf que le texte "Oui" apparait alors en rouge mais légèrement plus épais, ce qui est logique puisqu'il y a une impression de texture sur d'autre texture au fur et à mesure de mon passage de la souris sur le texte. En revanche j'ai toujours le problème de "clignotement" de ma fenêtre de dialogue...

        • Partager sur Facebook
        • Partager sur Twitter
          25 septembre 2018 à 19:16:40

          Re,-

          Voici un code qui fonctionne. Il y a certainement moyen de faire mieux (ceci n'est qu'un brouillon), mais je n'avais pas envie d'aller plus loin:

          #include <stdbool.h>
          
          #include <sdl2/sdl.h>
          #include <sdl2/sdl_ttf.h>
          
          #define WND_WIDTH	800
          #define WND_HEIGHT	600
          
          void displayText(SDL_Renderer *rdr,TTF_Font *font,char *str,SDL_Color *color,SDL_Rect *text_coo) {
          	SDL_Surface *text=TTF_RenderText_Blended(font,str,*color);
          	SDL_Texture *tx_text=SDL_CreateTextureFromSurface(rdr,text);
          	SDL_QueryTexture(tx_text,NULL,NULL,&text_coo->w,&text_coo->h);
          	text_coo->x=(WND_WIDTH-text_coo->w)/2;
          	text_coo->y=(WND_HEIGHT-text_coo->h)/2;
          	SDL_RenderCopy(rdr,tx_text,NULL,text_coo);
          	SDL_RenderPresent(rdr);
          	SDL_DestroyTexture(tx_text);
          	SDL_FreeSurface(text);
          }
          
          int main(int argc,char *argv[]) {
          	SDL_Window *wnd;
          	SDL_Renderer *rdr;
          	SDL_Rect text_coo;
          
          	SDL_Init(SDL_INIT_VIDEO);
          	TTF_Init();
          	TTF_Font *font=TTF_OpenFont("c:/windows/fonts/trebuc.ttf",24);
          	wnd=SDL_CreateWindow("Noname",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,WND_WIDTH,WND_HEIGHT,SDL_WINDOW_HIDDEN);
          	rdr=SDL_CreateRenderer(wnd,-1,SDL_RENDERER_ACCELERATED);
          	SDL_ShowWindow(wnd);
          	SDL_SetRenderDrawColor(rdr,0x40,0x40,0x40,0xff);
          	SDL_RenderClear(rdr);
          	SDL_SetRenderDrawColor(rdr,0x80,0x80,0x80,0xff);
          	SDL_Rect r={WND_WIDTH/2-100,WND_HEIGHT/2-100,200,200};
          	SDL_RenderFillRect(rdr,&r);
          	SDL_SetRenderDrawColor(rdr,0x40,0x40,0x40,0xff);
          	
          	SDL_Color normal_color={0x00,0x00,0xff,0xff};
          	SDL_Color selected_color={0x00,0xff,0x00,0xff};
          	SDL_Color *pc=&normal_color;
          	displayText(rdr,font,"Hello",pc,&text_coo);
          	bool done=false;
          	
          	while(!done) {
          		SDL_Event event;
          		if(SDL_PollEvent(&event)) {
          			if(event.type==SDL_QUIT)
          				done=true;
          			if(event.type==SDL_MOUSEMOTION) {
          				if(event.motion.x>=text_coo.x && event.motion.x<=text_coo.x+text_coo.w &&
          				   event.motion.y>=text_coo.y && event.motion.y<=text_coo.y+text_coo.h) {
          					if(pc!=&selected_color) {
          						pc=&selected_color;
          						displayText(rdr,font,"Hello",pc,&text_coo);
          					}
          				}
          				else
          					if(pc!=&normal_color) {
          						pc=&normal_color;
          						displayText(rdr,font,"Hello",pc,&text_coo);
          					}
          			}
          		}
          	}
          		
          	SDL_DestroyRenderer(rdr);
          	SDL_DestroyWindow(wnd);
          	TTF_CloseFont(font);
          	TTF_Quit();
          	SDL_Quit();
          
          	return(0);
          }
          

          -
          Edité par edgarjacobs 25 septembre 2018 à 19:36:50

          • Partager sur Facebook
          • Partager sur Twitter

          On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

            25 septembre 2018 à 20:10:09

            Effectivement, ça fonctionne même très bien. Mais je ne comprends pas pourquoi. Comment le fond change t-il de couleur alors que dans ta boucle à aucun moment tu n'imprimes le fond de telle ou telle couleur ? Tu ne fais que changer la couleur du texte et imprimer le texte et pourtant, même le fond change ! Il y a vraiment quelque chose que je ne saisis pas...
            • Partager sur Facebook
            • Partager sur Twitter
              25 septembre 2018 à 23:17:43

              Là, il y a un souci. Chez moi, le fond ne change pas. Juste la couleur du texte:

              Peux-tu poster un code minimal qui provoque ce changement de couleur de fond ?

              • Partager sur Facebook
              • Partager sur Twitter

              On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                26 septembre 2018 à 9:03:37

                Pas besoin que je mette un bout de code puisque j'ai fait un copié collé du tien ! (Avec changement bien entendu des chemins d'accès à la SDL et à la police pour que ça corresponde à mon système). Pour info je suis sous Xcode 10. Je souhaite te mettre des captures d'écrans, mais depuis cette nuit et la mise à jour vers Mac OS X Mojave j'ai une alerte au compilateur qui est : [default] Unable to load Info.plist exceptions (eGPUOverrides)
                Et depuis, tout mes projets se lance simplement avec une fenêtre noire... il faut donc que je règle ce problème avant de mettre des captures d'écran. 

                • Partager sur Facebook
                • Partager sur Twitter
                  16 octobre 2018 à 21:50:21

                  Rebonjour ! 

                  Bon finalement j'ai "réglé" le problème que j'avais avec Xcode. Pour ceux que ça intéresse, Xcode 10 a semble t-il un bug avec OpenGL de telle façon c'est qu'au lancement de l'application, la fenêtre est noire jusqu'à ce qu'on la redimensionne. J'ai préféré donc revenir à une version de Xcode 9 pour avancer (j'ai toujours le message d'erreur mais peu importe ça compile et "fonctionne" comme avant).

                  Mais mon principal problème n'est pas résolu ! :)

                  Comme tu le disais edgarjacobs, j'obtiens un résultat différent du tien. J'ai fais un copié collé de ton code (j'ai quand même modifié les chemins d'accès au sein du code pour que ça compile chez moi) et j'obtiens tout autre chose ! 

                  Voilà ce que j'obtiens quand j'ai la souris en dehors du texte : 

                  Et voila ce que j'obtiens lorsque la souris est sur le texte : 

                  Une bonne âme saurait pourquoi j'obtiens cela ? Problème d'IDE ? De compilateur ? d'OS ?

                  Merci de votre aide ! :)

                  • Partager sur Facebook
                  • Partager sur Twitter
                    19 octobre 2018 à 17:16:53

                    J'ai trouvé une "solution" au problème. En réalité la SDL n'est pas franchement prévu pour faire un SDL_RenderPresent que sur une seule partie de l'écran. C'est ce qui ressort de la documentation SDL et plus précisément cette phrase : "The backbuffer should be considered invalidated after each present". 

                    L'idée est donc de faire tous les SDL_RenderCopy du jeux "en fond", sur une texture "socle" et non sur le renderer principal. Ce qui nous permet de passer à la fonction de dessin de la boite de dialogue la texture "socle" sur lesquelles les textures ont fait l'objet de SDL_RenderCopy. Au final, on fera SDL_RenderCopy de cette fameuse texture "socle" sur le renderer principal et on imprimera les textures de la boite de dialogue. Du coup on aura pas à redessiner toutes les texture du jeux "en fond" mais on se contentera d'imprimer la texture "socle" qui ne constitue ni plus ni moins qu'une "capture d'écran" de notre fond au moment où la boite de dialogue s'ouvre.

                    Merci de votre aide !

                    • Partager sur Facebook
                    • Partager sur Twitter

                    [SDL2]N'imprimer qu'une partie de l'écran

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                    • Editeur
                    • Markdown