Tutoriaux / aides : Programmer sur dreamcast le how-to / Découverte du Développement sur Dreamcast avec KOS et SDL / des liens

  

 

        

 

 

1er chapitre – 4eme Partie : Pareil avec des objets qui bougent tout seul

 

1er chapitre – 4eme Partie : Pareil avec des objets qui bougent tout seul

 

 

Par pur hasard :), nous allons choisir comme objet qui bouge tout seul d'utiliser une balle. Nous dessinons donc un rond de 8x8 pixels que nous sauvegardons sous le nom de ball.bmp. Nous l'incluons à notre romdisk et nous rajoutons, de la même manière que précédemment un objet de type surface qui va accueillir notre image

        SDL_Surface *Ball ; 

puis dans Init()

         Ball = SDL_LoadBMP("/rd/Ball.bmp");

       

 Dans la mesure où notre objet va bouger tout seul, il va avoir une position à un moment m et une direction. Cette direction est représentée par un vecteur. Sans vous offenser, on va résumer le vecteur à : « quand je bouge de NB_X pixels sur la droite, je bouge de NB_Y pixels vers le bas. Les valeurs NB_X et NB_Y peuvent être négatives et dans ce cas, la direction ira vers la gauche ou le haut.

Pour matérialiser le vecteur, on crée une structure :

         typedef struct {

              int8 Nb_X;

              int8 Nb_Y;

           } Vecteur;

   

et on crée aussi une structure représentant un objet qui bouge (on va l'appeler Move_Object) ayant une direction (vecteur) et une position qui lui est propre :

       typedef struct {

                Vecteur vecteur;

                Position position;

           } Move_Objects; 

    

On va créer un objet global de ce type que l'on va appeler Ball1:

        Move_Object Ball1;

   

 Pour lequel on va initialiser les propriétés dans Init()

         // initialise ball1

           Ball1.vecteur.Nb_X=5;

           Ball1.vecteur.Nb_Y=5;

           Ball1.position.X=0;

           Ball1.position.Y=0; 

  

Et voilà, on a un objet de type 'bougeant' à la position (0,0) qui va se déplacer, à chaque rafraîchissement de 5 pixels vers la droite et de 5 vers le bas.

Il ne reste plus qu'à dessiner cet objet. Pour ça, il serait malin d'utiliser notre fonction dessiner image mais cette fonction, en plus de dessiner l'image à une position donnée, efface l'écran et zappe le buffer. Pour utiliser plusieurs fois notre fonction Dessiner_Image dans la même boucle, on doit sortir de cette fonction ces 2 actions (sinon, on ne verrait rien car à chaque appel, la fonction nous nettoierait ce qu'on  à déjà dessiné). Ces deux actions seront donc déplacées dans la boucle du main(). Et la fonction Dessiner_Image sera appelé pour dessiner la balle.

         // dessin background --> Morceau déplacé

                Creation_Background(Background_Pattern, MainScreen);

       

             // dessin image --> appel d'origine

                return_value = Dessiner_Image(Image ,Pos_ACtualPosition);

       

              // dessin balle

                return_value = Dessiner_Image(Ball, Ball1.position);

       

              // Refresh de l'écran --> Morceau déplacé

                SDL_Flip(MainScreen); 

     

Et voilà, la balle est dessinée mais, au bout de quelques secondes, elle sort de l'écran. Pas cool pour bien l'observer !! On va donc décider qu'elle rebondit sur les bords de l'écran (facile, on à déjà fait !!). Le seul truc est que la balle doit changer de direction quand elle rebondit. On teste donc la collision et si la balle touche un bord, on inverse la direction de son vecteur (ex: si elle va à droite et qu'elle touche le bord droit, elle change sa direction et va vers la gauche).

      if ((Ball1.position.X>= ( (*MainScreen).w - (*Ball).w) )||(Ball1.position.X<0) )

                     {Ball1.vecteur.Nb_X= (- Ball1.vecteur.Nb_X);}

      if ((Ball1.position.Y>= ( (*MainScreen).h - (*Ball).h) )||(Ball1.position.Y<0) )

                     {Ball1.vecteur.Nb_Y= (- Ball1.vecteur.Nb_Y);}

 

Comme précédemment, on s'adapte à la taille de l'écran et à la taille de l'image pour détecter les collisions.

Bon, une balle c'est chouette mais 5 ou 6, c'est mieux.

La 1ere chose qu'on va faire c'est rajouter un état à notre structure Move_Objects car quand on a un seul objet, on veut généralement l'afficher. Quand on en a plusieurs, c'est moins sûr.

On ajoute donc : uint8 etat;

On va maintenant créer un tableau qui va accueillir toutes nos balles. On va définir sa taille au travers d'une constante de compilation :

       #define Nb_balles 5

           Move_Object Tab_Balls[Nb_balles];

     

On supprime toutes les déclarations de Ball1 et on remplace l'initialisation de ses valeurs par une initialisation de toutes les balles :

          // initialise les balles

       

           Tab_Balls[0].vecteur.Nb_X=5;

           Tab_Balls[0].vecteur.Nb_Y=5;

           Tab_Balls[0].position.X=0;

           Tab_Balls[0].position.Y=0;

           Tab_Balls[0].etat=1;

       

           Tab_Balls[1].vecteur.Nb_X=-2;

           Tab_Balls[1].vecteur.Nb_Y=5;

           Tab_Balls[1].position.X=50;

           Tab_Balls[1].position.Y=150;

           Tab_Balls[1].etat=1;

       

           Tab_Balls[2].vecteur.Nb_X=-2;

           Tab_Balls[2].vecteur.Nb_Y=-3;

           Tab_Balls[2].position.X=10;

           Tab_Balls[2].position.Y=350;

           Tab_Balls[2].etat=1;

       

           Tab_Balls[3].vecteur.Nb_X=-2;

           Tab_Balls[3].vecteur.Nb_Y=0;

           Tab_Balls[3].position.X=510;

           Tab_Balls[3].position.Y=250;

           Tab_Balls[3].etat=1;

       

           Tab_Balls[4].vecteur.Nb_X=-9;

           Tab_Balls[4].vecteur.Nb_Y=-1;

           Tab_Balls[4].position.X=410;

           Tab_Balls[4].position.Y=150;

           Tab_Balls[4].etat=1;

      

 On les met toutes en état 1 car on part du principe que l'état 0 est caché et 1 affiché (on peut imaginer d'autres états plus tard).

Enfin, on doit maintenant créer une boucle d'affichage qui nous affiche toutes les balles les unes après les autres :

      for(int_compt=0;int_compt<Nb_balles; int_compt++)

      {

           if (Tab_Balls[int_compt].etat>0)

           {

              if ((Tab_Balls[int_compt].position.X>= ( (*MainScreen).w - (*Ball).w) )||(Tab_Balls[int_compt].position.X<0) )

                     {Tab_Balls[int_compt].vecteur.Nb_X= (- Tab_Balls[int_compt].vecteur.Nb_X);}

              if ((Tab_Balls[int_compt].position.Y>= ( (*MainScreen).h - (*Ball).h) )||(Tab_Balls[int_compt].position.Y<0) )

                     {Tab_Balls[int_compt].vecteur.Nb_Y= (- Tab_Balls[int_compt].vecteur.Nb_Y);}

       

              // move ball

                Tab_Balls[int_compt].position.X = Tab_Balls[int_compt].position.X + Tab_Balls[int_compt].vecteur.Nb_X;

                Tab_Balls[int_compt].position.Y = Tab_Balls[int_compt].position.Y + Tab_Balls[int_compt].vecteur.Nb_Y;

       

              // dessin balle

                return_value = Dessiner_Image(Ball, Tab_Balls[int_compt].position);

           }

      }

 

 Je passe assez vite sur cette partie car rien n'est vraiment nouveau et le traitement se résume à :

– On passe toutes les balles avec une boucle for

– On reprend le traitement précédent en changeant Ball1 par la position dans le tableau en cours de traitement par la boucle for : Tab_Balls[int_compt]

– On ajoute enfin une condition qui ne traite la balle que si son état est différent de 0

 

On compile et on se rend compte qu'on voit 5 balles qui rebondissent dans tous les sens. Toutefois, on voit que l'image de la balle est carrée avec des coins noirs disgracieux. On va essayer d'arranger ça  avec une fonction toute faite de Sdl :           SDL_SetColorKey

On va y définir, pour une image (surface) quelle est la couleur qui sert de transparence. Dans notre cas, c'est le noir (soit (0,0,0) en RGB). Donc la fonction donnera

      SDL_SetColorKey(Ball, SDL_SRCCOLORKEY, SDL_MapRGB(Ball->format,0,0,0));

        

On définie pour la surface Ball la couleur (0,0,0) comme transparente.

Du coup, notre programme donne ça :

  

       #define _arch_dreamcast

       #include <kos.h>

       #include <SDL/SDL.h>

        

      extern uint8 romdisk[];

        

      KOS_INIT_FLAGS(INIT_DEFAULT | INIT_MALLOCSTATS);

      KOS_INIT_ROMDISK(romdisk);

        

      SDL_Surface *MainScreen ;

      SDL_Surface *Image ;

      SDL_Surface *Ball ;

       SDL_Surface *Background_Pattern ;

       SDL_Joystick* joyStick;

       

      typedef struct {

            int16 X;

            int16 Y;

       } Position;

       typedef struct {

            int8 Nb_X;

            int8 Nb_Y;

       } Vecteur;

       typedef struct {

            Vecteur vecteur;

            Position position;

            uint8 etat;

       } Move_Object;

        

      #define Nb_balles 5

       

       Move_Object Tab_Balls[Nb_balles];

       

       // prototypes

       Position Move_Image (Position IN_Position);

       int Init();

       int Dessiner_Image(SDL_Surface *Image, Position IN_Position);

       void Creation_Background(SDL_Surface *pattern, SDL_Surface *Destination);

       Position Image_inclus (SDL_Surface *Image_A_Tester, SDL_Surface *Image_Support, Position IN_Position);

       int Init()

       {

            // Init SDL et test

            if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0 )    //  

               {

                 fprintf( stderr, "Couldn't initialize SDL: %s\n", SDL_GetError() ) ;

                 return -1;

               }

            // Init ecran principal et test

            MainScreen = SDL_SetVideoMode(640, 480, 0, SDL_HWSURFACE|SDL_DOUBLEBUF);

            if ( MainScreen == NULL )

               {

                 fprintf("Unable to set 640x480 video: %s\n", SDL_GetError());

                 return -1;

                 }

            // pas de curseur

            SDL_ShowCursor(0);

       

            // charge l'image dans une nouvelle surface

            Image = SDL_LoadBMP("/rd/JMD.bmp");

            Background_Pattern     = SDL_LoadBMP("/rd/Aquarium.bmp");

            Ball = SDL_LoadBMP("/rd/Ball.bmp");

            SDL_SetColorKey(Ball, SDL_SRCCOLORKEY, SDL_MapRGB(Ball->format,0,0,0));

       

           // Si des pad sont branchés, initialise la joystick

            if( SDL_NumJoysticks() > 0){

                 joyStick = SDL_JoystickOpen( 0 );

               if( joyStick == NULL ){

                 printf( "Couldn't initialize SDL_JoystickOpen: %s\n",SDL_GetError() );

               }

            }else{

                 printf( "Couldn't Enum SDL_NumJoysticks: %s\n", SDL_GetError() );

               }

            // initialise les balles

            Tab_Balls[0].vecteur.Nb_X=5;

            Tab_Balls[0].vecteur.Nb_Y=5;

            Tab_Balls[0].position.X=0;

            Tab_Balls[0].position.Y=0;

            Tab_Balls[0].etat=1;

       

            Tab_Balls[1].vecteur.Nb_X=-2;

            Tab_Balls[1].vecteur.Nb_Y=5;

            Tab_Balls[1].position.X=50;

            Tab_Balls[1].position.Y=150;

            Tab_Balls[1].etat=1;

        

           Tab_Balls[2].vecteur.Nb_X=-2;

            Tab_Balls[2].vecteur.Nb_Y=-3;

            Tab_Balls[2].position.X=10;

            Tab_Balls[2].position.Y=350;

            Tab_Balls[2].etat=1;

        

           Tab_Balls[3].vecteur.Nb_X=-2;

            Tab_Balls[3].vecteur.Nb_Y=0;

            Tab_Balls[3].position.X=510;

            Tab_Balls[3].position.Y=250;

            Tab_Balls[3].etat=1;

        

           Tab_Balls[4].vecteur.Nb_X=-9;

            Tab_Balls[4].vecteur.Nb_Y=-1;

            Tab_Balls[4].position.X=410;

            Tab_Balls[4].position.Y=150;

            Tab_Balls[4].etat=1;

       

             return 0;

       

      }

       void Creation_Background(SDL_Surface *pattern, SDL_Surface *Destination)

       {

            SDL_Rect dest;

            int int_compt , int_compt2;

       

           // Dessin du background

            int_compt2=0;

            while((int_compt2*(*Background_Pattern).h) <= (*Destination).h)

            {

                 int_compt=0;

                 while((int_compt*(*Background_Pattern).w) <= (*Destination).w)

                 {

                    dest.x = int_compt*(*Background_Pattern).w;

                    dest.y = int_compt2*(*Background_Pattern).h;

                    SDL_BlitSurface(Background_Pattern, NULL, Destination, &dest);

                    int_compt++;

               }

                 int_compt2++;

            }

       }

       int main(int argc, char **argv)

       {

            int return_value;

            int int_compt;

           Position Pos_ACtualPosition;

           Pos_ACtualPosition.X = 0;  

           Pos_ACtualPosition.Y = 0;

        

           // Init all

            if (Init()==0) {printf("Init : OK\n");}

            else {return 0;}

        

           // affiche une premiere fois l'image

            return_value = Dessiner_Image(Image ,Pos_ACtualPosition);

       

              // on boucle à l'infinie pour montrer le spectacle

            SDL_Event event;

            int  a, b=0;

            while(a==0)

            {

               // changement de position

                 Pos_ACtualPosition = Move_Image(Pos_ACtualPosition);

        

              // test de collisions sur les bord d'ecrans

                 Pos_ACtualPosition = Image_inclus(Image , MainScreen , Pos_ACtualPosition);

        

              // dessin background

                 Creation_Background(Background_Pattern, MainScreen);

       

              // dessin image

                 return_value = Dessiner_Image(Image ,Pos_ACtualPosition);

                 for(int_compt=0;int_compt<Nb_balles; int_compt++)

               {

                    if (Tab_Balls[int_compt].etat>0)

                 {

                      if ((Tab_Balls[int_compt].position.X>= ( (*MainScreen).w - (*Ball).w) )||(Tab_Balls[int_compt].position.X<0) )

                              {Tab_Balls[int_compt].vecteur.Nb_X= (- Tab_Balls[int_compt].vecteur.Nb_X);}

                      if ((Tab_Balls[int_compt].position.Y>= ( (*MainScreen).h - (*Ball).h) )||(Tab_Balls[int_compt].position.Y<0) )

                              {Tab_Balls[int_compt].vecteur.Nb_Y= (- Tab_Balls[int_compt].vecteur.Nb_Y);}

       

                     // move ball

                      Tab_Balls[int_compt].position.X = Tab_Balls[int_compt].position.X + Tab_Balls[int_compt].vecteur.Nb_X;

                      Tab_Balls[int_compt].position.Y = Tab_Balls[int_compt].position.Y + Tab_Balls[int_compt].vecteur.Nb_Y;

        

                     // dessin balle

                      return_value = Dessiner_Image(Ball, Tab_Balls[int_compt].position);

                 }

               }

               // Refresh de l'ecran

                 SDL_Flip(MainScreen);

            }

       

           return 0;

       

      }

       Position Image_inclus (SDL_Surface *Image_A_Tester, SDL_Surface *Image_Support, Position IN_Position)

       {

               if (IN_Position.X >= (*Image_Support).w - (*Image_A_Tester).w) {IN_Position.X = (*Image_Support).w - (*Image_A_Tester).w;}

               if (IN_Position.Y >= (*Image_Support).h - (*Image_A_Tester).h) {IN_Position.Y = (*Image_Support).h - (*Image_A_Tester).h;}

               if (IN_Position.X <= 0) {IN_Position.X = 0;}

               if (IN_Position.Y <= 0) {IN_Position.Y = 0;}

                 return IN_Position;

       }

        int Dessiner_Image(SDL_Surface *Image, Position IN_Position)

       {

               // creation d'un rectangle destination et definition de la position

            SDL_Rect dest;

            dest.x = IN_Position.X;

            dest.y = IN_Position.Y;

            SDL_BlitSurface(Image, NULL, MainScreen, &dest);

       

           return 0;

       

      }

       Position Move_Image (Position IN_Position)

            {

                    cont_cond_t cond;

                    cont_get_cond(maple_first_controller(), &cond);

                     if (!(cond.buttons & CONT_DPAD_UP)) {

                      IN_Position.Y = IN_Position.Y - 8;    }

                     if (!(cond.buttons & CONT_DPAD_DOWN)) {

                      IN_Position.Y = IN_Position.Y + 8;    }

                    if (!(cond.buttons & CONT_DPAD_RIGHT)) {

                      IN_Position.X = IN_Position.X + 8;    }

                    if (!(cond.buttons & CONT_DPAD_LEFT)) {

                      IN_Position.X = IN_Position.X - 8;    }

                    if (!(cond.buttons & CONT_START)) {

                      arch_reboot();   }

                    return IN_Position;        

            }