Déplacement et animation de personnages.

Déplacement et animation de personnages.

Voir version :

Dépendances (dans l'archive) :
charset.bmp

Télécharger :

#include <sdl/sdl.h>

#ifdef WIN32
#pragma comment(lib,"sdl.lib")
#pragma comment(lib,"sdlmain.lib")
#endif

// -------- Charset 

typedef struct 
{
    SDL_Surface* charset;
    int nbx,nby;
    int w,h;
} Charset;

int Charger(Charset* c,const char* fic,int nbx,int nby,Uint8 keycolorR,Uint8 keycolorG,Uint8 keycolorB)
{
    SDL_Surface* tmp = SDL_LoadBMP(fic);
    if (c==NULL)
        return -1;
    c->charset = SDL_DisplayFormat(tmp);
    SDL_FreeSurface(tmp);
    SDL_SetColorKey(c->charset,SDL_SRCCOLORKEY,SDL_MapRGB(c->charset->format,keycolorR,keycolorG,keycolorB));
    c->nbx = nbx;
    c->nby = nby;
    c->w = c->charset->w/nbx;
    c->h = c->charset->h/nby;
    return 0;
}

void FreeCharset(Charset* c)
{
    SDL_FreeSurface(c->charset);
}

void CharsetGetSrcRect(Charset* C,SDL_Rect* src,int frame)
{
    src->w = (Uint16)C->w;
    src->h = (Uint16)C->h;
    src->x = (Sint16)((frame%C->nbx)*C->w);
    src->y = (Sint16)((frame/C->nbx)*C->h);
}

// --------- Animation

typedef struct
{
    int framedepart;
    int nbframes;
    int delay;
} Anim;

void SetAnim(Anim* A,int framedepart,int nbframes,int delay)
{
    A->framedepart = framedepart;
    A->nbframes = nbframes;
    A->delay = delay;
}

int AnimGetFrame(Anim* A)
{
    return A->framedepart + (SDL_GetTicks()/A->delay)%A->nbframes;
}

// --------------------- Sprite

#define SENS_HAUT 0
#define SENS_DROITE 1
#define SENS_BAS 2
#define SENS_GAUCHE 3

#define STAT_ARRET 0
#define STAT_MARCHE 1

typedef struct
{
    Charset* C;
    Anim A;
    SDL_Rect pos;
    int sens;
    int status;
} Sprite;

void SetSprite(Sprite* S,int x,int y,Charset* C,int framedepart)
{
    S->pos.x = (Sint16)x;
    S->pos.y = (Sint16)y;
    S->C = C;
    SetAnim(&S->A,framedepart,1,1);
    S->sens = SENS_HAUT;
    S->status = STAT_ARRET;
}

void RenderSprite(SDL_Surface* screen,Sprite* S)
{
    SDL_Rect src;
    int frame = AnimGetFrame(&S->A);
    CharsetGetSrcRect(S->C,&src,frame);
    SDL_BlitSurface(S->C->charset,&src,screen,&S->pos);
}

// ----------------- Fonctions

void Evolue(Sprite SP[2],Uint8 * keys,int startframeperso[2])
{
    int i,j;
    int control[2][4] = {{SDLK_UP,SDLK_RIGHT,SDLK_DOWN,SDLK_LEFT},{SDLK_i,SDLK_l,SDLK_k,SDLK_j}};
    Sint16 vec[4][2] = {{0,-1},{1,0},{0,1},{-1,0}};
    Sint16 speed = 2;
    int speedanim = 120;
    for(i=0;i<2;i++)
    {
        int move = 0;
        for(j=0;j<4;j++)
        {
            if (keys[control[i][j]])
            {
                move = 1;
                if (SP[i].status == STAT_ARRET || SP[i].sens != j)
                    SetAnim(&SP[i].A,startframeperso[i] + SP[i].C->nbx*j,3,speedanim);
                SP[i].sens = j;
                SP[i].pos.x+=speed*vec[j][0];
                SP[i].pos.y+=speed*vec[j][1];
                SP[i].status = STAT_MARCHE;
            }
        }
        if (SP[i].status == STAT_MARCHE && !move)
        {
            SetAnim(&SP[i].A,startframeperso[i] + SP[i].C->nbx*SP[i].sens + 1,1,1);
            SP[i].status = STAT_ARRET;
        }
    }
}

int main(int argc,char** argv)
{
    SDL_Surface* screen;
    Charset C;
    Sprite SP[2];
    Uint8 * keys;
    int numkeys;
    Uint32 timer,elapsed;
    int startframeperso[2] = {0,6};
    SDL_Init(SDL_INIT_VIDEO);
    screen=SDL_SetVideoMode(400,300,32,SDL_SWSURFACE|SDL_DOUBLEBUF);  
    if (Charger(&C,"charset.bmp",12,8,32,156,0)!=0)
    {
        SDL_Quit();
        printf("Echec chargement charset.bmp\n");
        return -1;
    }
    SetSprite(&SP[0],100,100,&C,startframeperso[0]+1);
    SetSprite(&SP[1],300,100,&C,startframeperso[1]+1);
    do 
    {
        timer = SDL_GetTicks();
        SDL_FillRect(screen,NULL,0);
        SDL_PumpEvents();
        keys = SDL_GetKeyState(&numkeys);
        Evolue(SP,keys,startframeperso);
        RenderSprite(screen,&SP[0]);
        RenderSprite(screen,&SP[1]);
        SDL_Flip(screen);
        elapsed = SDL_GetTicks() - timer;
        if (elapsed<20)
            SDL_Delay(20-elapsed);
    } while (!keys[SDLK_ESCAPE]);
    FreeCharset(&C);
    SDL_Quit();
    return 0;
}





Commentaires


	Un petit exemple, qui peut paraitre compliqué, mais qui permettra d'être évolutif.

	Lancez le programme. Déplacez un personnage, l'autre avec les touches i,j,k,l.
	On a bien l'impression qu'ils marchent, ils regardent ou ils vont, et on peut les déplacer en même temps.

	Regardons la seule image disponbible : charset.bmp. Plusieurs images, de la même taille, qui donnent des animations si on colle la
	bonne partie au bon moment.

	Je dirai que la vignette 0 est celle en haut à gauche, ensuite on suit le sens de lecture conventionnel.
	Ainsi la vignette de la 2e ligne, premiere colonne aura l'indice 12, et la toute dernière en bas à droite aura l'indice 95.

	Si on regarde juste le main :
	Initialisation. On charge le charset : je lui donne le nom du fichier, et combien d'images j'ai en largeur, hauteur, et la keycolor : ici 32,156,0, le vert du fond.
	On initialise 2 sprites : on donne la position de départ (100,100) pour l'un (300,100) pour l'autre, l'adresse du charset associé, et l'image (vignette) de la première animation.
	Ce sera la vignette 1 pour l'un (le Ryu de dos, les deux pieds au sol), et la vignette 7 pour l'autre (Le Blanka)

	Puis on part dans la boucle principale. Dans cette boucle, on a une fonction Evolue, et RenderSprite.

	La fonction RenderSprite illustre juste le sprite. Nous en reparlerons.

	La fonction Evolue va gérer les entrées au clavier : j'ai tout condensé dedans pour éviter le copier/coller.

	Charset :
	---------

	Ma structure charset va simplement contenir la SDL_Surface* du charset, ansi que le nombre de personnages en x et en y.
	Elle calculera la largueur et la hauteur constante d'une vignette : w,h

	La fonction CharsetGetSrcRect, je lui donne un numéro de vignette, elle me calcule le rectangle de la vignette que je passera en 2e paramètre à SDL_BlitSurface.

	Anim :
	------

	Cette structure est intéressante car c'est elle qui va contrôler l'animation.
	Vous lui donnez un nombre de départ, un nombre d'animations, et un delay, et, chaque fois que vous appelez AnimGetFrame, elle vous donnera l'animation courante :

	Typiquement, je lui donne :
	framedepart = 0
	nbframe = 3
	delay = 50

	Et bien elle va aller de 0 à 2, en changeant toutes les 50 millisecondes :  0..1..2..0..1..2..0..1.. .. . .. . ..
	Si je donne framedepart = 6, elle me donnera 6..7..8..6..7..8..6..7..8.. .....

	En réalité, c'est un chronomètre passif et non actif : c'est à dire que c'est au moment ou je l'appelle qu'elle va calculer le bon paramètre en fonction du time global donné par SDL.

	Cela va servir pour la marche : parce que j'aurai mes 3 animations pour la marche, mais aussi pour l'arrêt, ou je posera nbframe = 1, 
	ce qui donnera, si j'ai framedepart = 7 :  7..7..7..7..7  -> donc ça ne bouge pas.

	Sprite :
	--------

	Un sprite ici contient une position x,y (que je stocke ici en SDL_Rect pour simplifier), une séquence d'animation, et un lien vers le charset.

	Que fait la fonction Render ? Elle prend la frame en interrogeant l'animation, et affiche la bonne vignette en appelant le charset, tout simplement !!!

	Le sprite contient en outre un etat (en marche ou à l'arrêt), et un sens (haut, droite, bas, gauche) : rangé dans le même ordre que sur le charset.

	Evolue :
	--------

	Evolue faire un for pour les deux sprites.
	Et un deuxième pour tester les 4 directions.

	Si la touche correspondante est pressée, je déplace le sprite, et je change l'animation si je change de sens, ou si j'étais à l'arrêt.

	Si je ne déplace pas, alors je remet le sprite à l'arrêt avec une animation d'une seule frame, celle du milieu.


	Modifications :
	---------------

	Si vous voulez changer de personnage, jouez avec int startframeperso[2] = {0,6}; dans le main.
	Mettez bien une vignette en haut à gauche d'un bloc de personnages : 0,3,6,9,48,51,54,57 (sinon, ça va faire n'importe quoi)

	Vous pouvez jouer sur la vitesse de déplacement (speed), et la vitesse d'animation (speedanim) en jouant sur ces constantes :

	int speed = 2;
	int speedanim = 120;