Partage
  • Partager sur Facebook
  • Partager sur Twitter

Gestion Collision et Deplacement de Mario

C/SDL2

    18 mai 2016 à 13:43:00

    Bonjour je travaille sur le développement d'une application écrite en C/SDL2, ici il s'agit de Mario.Mais j'ai un problème au niveau de la gestion des collisions. Notamment Mario se déplace dans les murs.

    -Le terrain qui est affiché en SDL est un terrain en txt (reprend un peu le concept de tile mapping)

    -Les images sont affichés grâce aux fonction image_draw (pour l'affichage) et aux fonction Image (pour l'initialisation)

    -Le test de collision marche normalement a peu près comme le forum du tile mapping https://openclassrooms.com/courses/tile-mapping/scrolling-automatique

    Voici le code :

    #include <assert.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    #include "src/sdlJeu.h"
    
    #define GRAVITY  0.6f
    #define MAX_FALL_SPEED 10
    #define MAX_WALK_SPEED 5
    #define JUMP_HEIGHT 11
    #define TAILLE_SPRITE 32
    
    
    int CollisionDecor(sdlJeu *sj,float dx, float dy){
    	/*retourne des nombres differents en fonction de la collision*/
    
    	int xmin,xmax,ymin,ymax,i,j;
    
        char c;
    
    	Mario *perso = jeuGetMario(&sj->jeu);
    	Niveau *niv = jeuGetNiveau(&sj->jeu);
    
        /*Personnage endehors du terrain*/
    	if (perso->x<0 || (perso->x + TAILLE_SPRITE -1)>=21*TAILLE_SPRITE
    	 || perso->y<0 || (perso->y + TAILLE_SPRITE -1)>=12*TAILLE_SPRITE)
    		return -1;
    
        if (dx<0){
            xmin=(perso->x + dx + TAILLE_SPRITE -1) / TAILLE_SPRITE;
            ymin=(perso->y + dy + TAILLE_SPRITE -1) / TAILLE_SPRITE;
    
            xmax=perso->x / TAILLE_SPRITE;
            ymax=perso->y / TAILLE_SPRITE;
    
         //   printf("b2\n");
        }
    
        else{
    
            xmin = perso->x / TAILLE_SPRITE;
            ymin = perso->y / TAILLE_SPRITE;
            xmax = (perso->x + dx + TAILLE_SPRITE -1) / TAILLE_SPRITE;
            ymax = (perso->y + dy + TAILLE_SPRITE -1) / TAILLE_SPRITE;
        }
    
    	for(i=xmin;i<=xmax;i++)
    	{
    		for(j=ymin;j<=ymax;j++)
    		{
                c=nivGetXY(niv,i,j);
    
                /*Collision Personnage sur le haut*/
                if(dx==0 && dy < 0){
    
                    if(c=='#'){ return 1;}
    
                }
    
                /*Collision Personnage sur le bas*/
                if(dx==0 && dy > 0){
    
                    if(c=='#'){ return 3;}
    
                }
    
                /*Collision Personnage sur la gauche*/
                else if(dx<0 && dy == 0){
    
                    if(c=='#'){ return 2;}
    
                }
                 /*Collision Personnage sur la droite*/
                else if(dx>0 && dy == 0){
    
                    if(c=='#'){ return 4;}
    
                }
    
                /*Dans une collision*/
                else{
    
                    if(c=='#'){ return 5;}
    
                }
    		}
    	}
    
        /*En cas de non collision*/
    	return 0;
    }
    
    void analyseCollision(sdlJeu *sj){
    
    /*Etudie les renvoies de la fonction CollisionDecor
    En fonction de chaque nombre renvoyer on etudie les collisions et on influe sur
    dx et dy !
    
    .Utilisez la variable OnGround ou la variable Onledge, le test de collision ou la
    graviter sera faite tant que OnGround ou OnLedge = 0*/
    Mario *mar =jeuGetMario(&sj->jeu);
    
        if(CollisionDecor(sj,mar->dx,mar->dy)==3)
        {
            mar->dy=0.0;
            mar->onGround=1;
    
        }
    
    
        if(CollisionDecor(sj,mar->dx,mar->dy)==1)
        {
            mar->dy = GRAVITY*10;
            mar->dx=0.0;
        }
    
    
    
    
        if(CollisionDecor(sj,mar->dx,mar->dy)==2)
        {
            mar->dx =0;
          //  mar->dy =5;
    
        }
    
    
        if(CollisionDecor(sj,mar->dx,mar->dy)==4)
        {
            mar->dx = 0;
    
        }
    
        if(CollisionDecor(sj,mar->dx,mar->dy)==5)
        {
            mar->dx = 0;
            mar->dy = 0;
    
        }
    
    
    }
    
    void updateplayer(sdlJeu *sj){
    
        Mario *mar =jeuGetMario(&sj->jeu);
        int d=CollisionDecor(sj,mar->dx,mar->dy);
            printf("1.  *%d* ",d);
        printf("X= %f, Y=%f, dx=%f, dy=%f, ground=%d\n", mar->x,mar->y,mar->dx,mar->dy,mar->onGround);
       analyseCollision(sj);
    
        int d1=CollisionDecor(sj,mar->dx,mar->dy);
        printf("2.  *%d* ",d1);
        printf("X= %f, Y=%f, dx=%f, dy=%f, ground=%d\n", mar->x,mar->y,mar->dx,mar->dy,mar->onGround);
    
        mar->x+=mar->dx;
        mar->y+=mar->dy;
    
        mar->dx=0;
        //mar->dy=0;
        mar->dy+=GRAVITY;
    
        /*Evite mario de tomber*/
        if(CollisionDecor(sj,mar->dx,mar->dy)==3)
        {
            mar->dy=0.0;
            mar->onGround=1;
    
        }
    
    
    
    }
    
    Image image(const char* filename, SDL_Renderer * renderer){
         Image res;
         SDL_Surface * surfaceCorrectPixelFormat;
        /*res.surface = SDL_LoadBMP(filename);*/
        res.surface = IMG_Load(filename);
    
        if (res.surface == NULL)
        {
           /* std::string nfn = std::string("../") + filename;
            std::cout<<"error: Can not load "<< filename<<". Trying "<<nfn<<std::endl;;
            res.surface = IMG_Load(nfn.c_str());*/
            if (res.surface == NULL)
            {
               /* nfn = std::string("../") + nfn;
                res.surface = IMG_Load(nfn.c_str());*/
            }
        }
        if (res.surface == NULL)
        {
           /* std::cout<<"error: Can not load "<< filename<<std::endl;
            return res;*/
        }
    
        surfaceCorrectPixelFormat = SDL_ConvertSurfaceFormat(res.surface,SDL_PIXELFORMAT_ARGB8888,0);
        SDL_FreeSurface(res.surface);
        res.surface = surfaceCorrectPixelFormat;
    
        /*res.texture = SDL_CreateTexture(g.renderer(), SDL_PIXELFORMAT_ARGB8888, 	SDL_TEXTUREACCESS_STREAMING,	res.surface->w, res.surface->h);*/
        res.texture = SDL_CreateTextureFromSurface( renderer, res.surface);
    
        if (res.texture == NULL)
        {
            printf("error: problem to create the texture of %s\n", filename);
            return res;
        }
    
        /*int ok = SDL_UpdateTexture(res.texture, NULL, res.surface->pixels, res.surface->pitch);*/
        /*assert(ok==0);*/
    
        printf("Image loaded: %s\n", filename);
        return res;
    }
    
    float temps(){
        /*return float(clock()) / CLOCKS_PER_SEC;*/
        return SDL_GetTicks() / CLOCKS_PER_SEC;  /* converti des ms en secondes en divisant par 1000*/
    }
    
    void image_draw(Image *im, SDL_Renderer * renderer, int x, int y, int w, int h){
        /*w=-1;
        h=-1;*/
        int ok;
        SDL_Rect r;
        r.x = x;
        r.y = y;
        r.w = w;/*if(w<0){im.surface->w};*/
        r.h = h;/*if(h<0){im.surface->h};*/
    
        if (im->has_changed)
        {
            ok = SDL_UpdateTexture(im->texture, NULL, im->surface->pixels, im->surface->pitch);
            assert(ok == 0);
            im->has_changed = 0;
        }
    
        ok = SDL_RenderCopy(renderer, im->texture, NULL, &r);
        /*ok = SDL_RenderCopyEx(g.renderer(), im.texture, NULL, &r, 0, NULL, SDL_FLIP_NONE);*/
        assert(ok == 0);
    }
    
    void sdljeuInit(sdlJeu* sj)
    {
        int dimx, dimy;
            int imgFlags;
    
        /*Initialisation du jeu avec le terrain1.txt(par defaut)*/
            jeuInit(&sj->jeu);
    
    
        /* Initialisation de la SDL*/
    
        if (SDL_Init(SDL_INIT_VIDEO) < 0)
        {
            fprintf(stderr, "Erreur d'initialisation de la SDL : %s\n",SDL_GetError());
            SDL_Quit();
           /* exit();*/
        }
    
        if (TTF_Init() != 0)
        {
    	printf("Erreur d'initialisation de la SDL : %s\n",SDL_GetError());
            SDL_Quit();
           /* exit();*/
    
        }
    
        imgFlags = IMG_INIT_PNG | IMG_INIT_JPG;
        if( !( IMG_Init( imgFlags ) & imgFlags ) )
        {
            printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
            SDL_Quit();
        }
    
        /*Recuperation de dimx et dimy du Niveau pour la creation de la fenetre*/
    
    	dimx = nivGetDimX( jeuGetConstNiveau(&sj->jeu) );
    	dimy = nivGetDimY( jeuGetConstNiveau(&sj->jeu) );
        /*printf("dimX = %d , dimY = %d\n",dimx,dimy);*/
    	dimx = dimx * TAILLE_SPRITE;
    	dimy = dimy * TAILLE_SPRITE;
        printf("dimX = %d , dimY = %d\n",dimx,dimy);
    
        /* Creation de la fenetre */
        	     sj->window = SDL_CreateWindow("CLASSIC MARIO v0.3",
                              SDL_WINDOWPOS_CENTERED,
                              SDL_WINDOWPOS_CENTERED,
                              dimx, dimy,
                              SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    
        /* Attribution de l'icone de la fenetre */
         SDL_SetWindowIcon(sj->window, IMG_Load("data/mario_icon.png"));
    
        if (sj->window == NULL)
        {
            fprintf(stderr, "Erreur d'initialisation de la SDL pour la fenetre: %s\n",SDL_GetError());
            SDL_Quit();
            assert(0);
            exit(1);
        }
    
    
       /*Use this function to create a 2D rendering context for a window.*/
       sj->renderer = SDL_CreateRenderer( sj->window, -1, SDL_RENDERER_ACCELERATED);
    
    
       /* Chargement des Images */
    
        sj->imMonster= image("data/Image/fantome.png", sj->renderer);
       	assert(sj->imMonster.surface);
    
        sj->imMario = image("data/Image/mario_droite.gif", sj->renderer);
       	assert(sj->imMario.surface);
    
       	sj->imMarioH = image("data/Image/mario_haut.gif", sj->renderer);
       	assert(sj->imMario.surface);
    
       	sj->imMarioB = image("data/Image/mario_bas.gif", sj->renderer);
       	assert(sj->imMarioB.surface);
    
        sj->imMarioG = image("data/Image/mario_gauche.gif", sj->renderer);
       	assert(sj->imMarioG.surface);
    
        sj->imMarioD = image("data/Image/mario_droite.gif", sj->renderer);
       	assert(sj->imMarioD.surface);
    
       sj->imMur = image("data/Image/mur.jpg", sj->renderer);
       	assert(sj->imMur.surface);
    
        /*Initialisation de Mario en SDL*/
        Mario* mar = jeuGetMario(&sj->jeu);
        mar->x=2*TAILLE_SPRITE;
        mar->y=5*TAILLE_SPRITE;
        mar->onLedge=0;
        mar->timerMort=0;
    
        /* printf("X= %f, Y=%f, dx=%f, dy=%f\n", mar->x,mar->y,mar->dx,mar->dy);*/
    
    }
    
    void sdljeuAff(sdlJeu* sj)
    {
        int direction;
        int x,y;
    	const Niveau* niv = jeuGetConstNiveau(&sj->jeu);
         const Mario* mar = jeuGetConstMario(&sj->jeu);
    	const Monster* mons = jeuGetConstMonster(&sj->jeu);
    
    
        /*Remplir l'écran de bleu*/
        SDL_SetRenderDrawColor( sj->renderer, 74, 118, 152, 255);
        SDL_RenderClear( sj->renderer);
    
       /*Affichage du terrain en fonction du terrain deja defini en TXT*/
    
    		for (x=0;x<nivGetDimX(niv);++x){
                for (y=0;y<nivGetDimY(niv);++y){
                    switch(nivGetXY(niv,x,y)){
                    case '#' :
    				image_draw( &sj->imMur, sj->renderer, x*TAILLE_SPRITE, y*TAILLE_SPRITE, TAILLE_SPRITE, TAILLE_SPRITE);
    				break;
    
                    case '.' :
    				/*image_draw(&sj->imPastille, sj->renderer, x*TAILLE_SPRITE, y*TAILLE_SPRITE, TAILLE_SPRITE, TAILLE_SPRITE);*/
    				break;
                    }
                }
            }
    
    
    	/* Copier le sprite de Mario sur la fenetre en fonction de la direction*/
    	direction=marGetDirection(mar);
    
    	/*ATTENTION LES DIRECTIONS BAS ET HAUT SONT INVERSER COMME LE TERRAIN*/
    	 switch (direction)
                {
                    case 3 :
                        image_draw( &sj->imMarioH, sj->renderer, mar->x, mar->y, TAILLE_SPRITE,TAILLE_SPRITE);
                        break;
                    case 2 :
                        image_draw( &sj->imMarioG, sj->renderer, mar->x, mar->y, TAILLE_SPRITE,TAILLE_SPRITE);
                        break;
                    case 1 :
                        image_draw( &sj->imMarioB, sj->renderer, mar->x, mar->y, TAILLE_SPRITE,TAILLE_SPRITE);
                        break;
                    case 4 :
                        image_draw( &sj->imMarioD, sj->renderer, mar->x, mar->y, TAILLE_SPRITE,TAILLE_SPRITE);
                        break;
                }
    
    	/* Copie le sprite de Monster sur l'ecran*/
    	image_draw( &sj->imMonster, sj->renderer, (posGetX(&mons->posMonster))*TAILLE_SPRITE,  posGetY(&mons->posMonster)*TAILLE_SPRITE, TAILLE_SPRITE, TAILLE_SPRITE);
    }
    
    
    void sdljeuBoucle(sdlJeu* sj)
    {
        Mario* mar = jeuGetMario(&sj->jeu);
        SDL_Event events;
    	int quit = 0;
        Jeu *jeu;
         Uint32 t, nt;
         t = SDL_GetTicks();
        jeu=&sj->jeu;
    
    	/* tant que ce n'est pas la fin ...*/
    	while ( quit == 0 )
    	{
            /*FONCTION DE VERIFICATION DU DYNAMISME DE MARIO*/
            nt = SDL_GetTicks();
            if (nt-t>20)    // 50 img / s
            {
                jeuActionsAutomatiques(jeu);
                updateplayer(sj);
                t = nt;
            }
                jeuVerificationPosition(jeu);
    
    		/* tant qu'il y a des evenements à traiter (cette boucle n'est pas bloquante)*/
    		while ( SDL_PollEvent( &events ) )
    		{
    			if ( events.type == SDL_QUIT ) quit = 1;              /* Si l'utilisateur a clique sur la croix de fermeture*/
    			else if ( events.type == SDL_KEYDOWN )       			/* Si une touche est enfoncee*/
    			{
    				switch ( events.key.keysym.scancode )
    				{
    				case SDL_SCANCODE_SPACE:
                        if(mar->onGround==1){
                            mar->dy-=JUMP_HEIGHT;
                            mar->onGround=0;
                        }
                        break;
                    case SDL_SCANCODE_ESCAPE:
                        quit = 1;
                    case SDL_SCANCODE_Q:
                        quit = 1;
                        break;
    				default: break;
    				}
    
    			}
    		}
    
    
    const Uint8 *state = SDL_GetKeyboardState(NULL);
    
        if(state[SDL_SCANCODE_UP])
        {
            mar->dy -=0.1f;
        }
    
      //Walking
        if(state[SDL_SCANCODE_LEFT])
        {
            mar->dx -= 0.5;
            if(mar->dx < -MAX_WALK_SPEED)
            {
                mar->dx = -MAX_WALK_SPEED;
            }
        }
    
        else if(state[SDL_SCANCODE_DOWN])
        {
            mar->dy +=0.5;
             if(mar->dy > MAX_WALK_SPEED)
        {
            mar->dy=MAX_WALK_SPEED;
        }
    
        }
    
        else if(state[SDL_SCANCODE_RIGHT])
        {
            mar->dx += 0.5;
            if(mar->dx > MAX_WALK_SPEED)
            {
                mar->dx =MAX_WALK_SPEED;
            }
    
        }
        else
        {
    
        mar->dx *= 0.8f;
    
            if(fabsf(mar->dx) < 0.1f)
            {
                mar->dx = 0;
            }
        }
    
    
            /*FONCTION QUI SERONT EXECUTER A L'INFINI*/
    
    		/* on affiche le jeu sur le buffer caché*/
    		sdljeuAff( sj );
    
    		/* on permute les deux buffers (cette fonction ne doit se faire qu'une seule fois dans a boucle)*/
            SDL_RenderPresent( sj->renderer);
    
    	}
    
    }
    
    
    
    void sdljeuDetruit( sdlJeu *sj)
    {
        //TTF_CloseFont( sj->font);
        TTF_Quit();
        SDL_DestroyRenderer( sj->renderer );
        SDL_DestroyWindow( sj->window);
        SDL_Quit();
    }
    
    int main ( int argc, char** argv )
    {
        sdlJeu sj;
        printf("debut\n");
    	sdljeuInit( &sj );
    	sdljeuBoucle(&sj);
    	sdljeuDetruit(&sj);
    
    	printf("Fin\n");
    	return 0;
    }
    
    
    



    • Partager sur Facebook
    • Partager sur Twitter
      18 mai 2016 à 14:43:54

      Salut !

      Si tu as bien lu mon tuto, normalement, Mario ne se retrouve JAMAIS dans les murs.

      On prend une position hypothétique, on la déplace, et si elle est dans le mur, on ne touche a rien (ou on affine).

      Sinon, on valide le déplacement :)

      • Partager sur Facebook
      • Partager sur Twitter

      Notre entreprise recrute, contactez moi en MP. (CDI, Bac+5, Lyon, C++, math, décodage binaire, conversions de modèles 2D/3D...)

        18 mai 2016 à 15:14:49

        Merci pour la réponse, mon projet est assez inspiré de ton tuto.

        Je vois, dans ma boucle d’événement sdlBoucle les valeurs  dx et dy qui seront affecté à notre position actuel pour le mouvement sont prise en compte.

        Lorsque le mouvement est a droite (dx>0) ou que le mouvement est vers le bas (dy>0), les test de collision marche plutôt correctement.Et il y a pas de collision.

        Mais quand pour les déplacements inverse qui sont gauche (dx<0) et haut (dy<0), ca pose problème.

        J'ai essayé de modifier (suggestion professeur) la fonction collisionDecor de ton tuto pour pouvoir renvoyer des nombres en fonction des collision.

        Ou je dois appliquer les valeurs de dx et dy ,vérifier la collision  et en cas de collision retourner en arrière ? (en ajoutant l'inverse de dx et dy à x et y).

        • Partager sur Facebook
        • Partager sur Twitter
          18 mai 2016 à 15:20:41

          Je relis le tuto et j'essaye de mieux comprendre.
          • Partager sur Facebook
          • Partager sur Twitter
            18 mai 2016 à 16:10:43

            Re :)

            De base, ma fonction de test de collision est négative : c'est à dire que on dit qu'il y a collision si on n'est pas "trop a gauche" ou "trop en bas" ou "trop en haut" ou "trop à droite". Donc elle ne dit pas par ou tu touches. Si tu veux dire par ou tu touches, alors il faut l'enrichir.

            Je vois que tu l'as fait, mais attention, tu dis que tu touches par exemple si ton vecteur est négatif en x, nul en y. Mais s'il y a une diagonale ? Si tu arrives sur le coin en diagonale, et que donc simultanément, tu toucherais la paroi droite et le haut. Gères tu cela ?

            • Partager sur Facebook
            • Partager sur Twitter

            Notre entreprise recrute, contactez moi en MP. (CDI, Bac+5, Lyon, C++, math, décodage binaire, conversions de modèles 2D/3D...)

              18 mai 2016 à 16:30:34

              Oui je vois,

              En cas de diagonale (ou quand on se trouve déjà dans une collision) normalement la fonction renvoie la valeur 5, car si ont est pas dans les autres cas de déplacement et qu'il y a quand même collision ça renvoie 5.

              Je l'ai gérer tout simplement en mettant dx et dy = 0..Des propositions ?

              Mais l'un des problème c'est que le déplacement se fait malgré les valeurs de dx et dy quand ont va a gauche ou en haut.Et c'est juste après que la fonction collision modifie dx et dy.

              Et la je sais pas trop comment faire.

              • Partager sur Facebook
              • Partager sur Twitter
                18 mai 2016 à 18:50:17

                J'ai modifié la fonction CollisionDecor pour les cas ou les déplacements sont à gauche (dx<0) et à droite (dy<0), en modifiant les xmin,ymin,xmax,ymax pour le test effectuer. Et la y'a plus de collision quand je vais vers le haut, ni à gauche.

                Mais la j'ai un problème avec les collision en diagonale.

                Voici le code :

                int CollisionDecor(sdlJeu *sj,float dx, float dy){
                	/*retourne des nombres differents en fonction de la collision*/
                
                	int xmin,xmax,ymin,ymax,i,j;
                
                    char c;
                
                	Mario *perso = jeuGetMario(&sj->jeu);
                	Niveau *niv = jeuGetNiveau(&sj->jeu);
                
                    /*Personnage endehors du terrain*/
                	if (perso->x<0 || (perso->x + TAILLE_SPRITE -1)>=21*TAILLE_SPRITE
                	 || perso->y<0 || (perso->y + TAILLE_SPRITE -1)>=12*TAILLE_SPRITE)
                		return -1;
                
                    if (dx<0 || dy<0){
                        xmin=(perso->x + dx) / TAILLE_SPRITE;
                        ymin=(perso->y + dy) / TAILLE_SPRITE;
                
                        xmax=perso->x / TAILLE_SPRITE;
                        ymax=perso->y / TAILLE_SPRITE;
                
                     //   printf("b2\n");
                    }
                
                    else{
                
                        xmin = perso->x / TAILLE_SPRITE;
                        ymin = perso->y / TAILLE_SPRITE;
                        xmax = (perso->x + dx + TAILLE_SPRITE -1) / TAILLE_SPRITE;
                        ymax = (perso->y + dy + TAILLE_SPRITE -1) / TAILLE_SPRITE;
                    }
                
                	for(i=xmin;i<=xmax;i++)
                	{
                		for(j=ymin;j<=ymax;j++)
                		{
                            c=nivGetXY(niv,i,j);
                
                            /*Collision Personnage sur le haut*/
                            if(dx==0 && dy <= 0){
                
                                if(c=='#'){ return 1;}
                
                            }
                
                            /*Collision Personnage sur le bas*/
                            if(dx==0 && dy >= 0){
                
                                if(c=='#'){ return 3;}
                
                            }
                
                            /*Collision Personnage sur la gauche*/
                            else if(dx<=0 && dy == 0){
                
                                if(c=='#'){ return 2;}
                
                            }
                             /*Collision Personnage sur la droite*/
                            else if(dx>=0 && dy == 0){
                
                                if(c=='#'){ return 4;}
                
                            }
                
                            /*Dans une collision*/
                            else{
                
                                if(c=='#'){ return 5;}
                
                            }
                		}
                	}
                
                    /*En cas de non collision*/
                	return 0;
                }

                • Partager sur Facebook
                • Partager sur Twitter

                Gestion Collision et Deplacement de Mario

                × 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