PDA

Voir la version complète : [PSP][Tutorial] Jour 12: Creer une annimation pour vos sprite (methode 1)


pjeff
31/07/2008, 12h17
Jour 12: Créer une animation pour vos sprites (méthode 1)
par PJEFF



Bonjour. Bienvenue dans la suite des tutoriels consacrés au développement sur PSP.

Pour des raisons évidentes j’ai repris les tutoriels déjà existants pour les adapter à ce qui suivra.
Je ne vais commenter que le code ajouté, pour le reste référez-vous aux anciens tutoriels.




Petite question voulez vous encore d'autre cours ?? un peu plus avancés ?

Ce que nous allons voir aujourd'hui c'est comment faire un système d'animation très simple pour vos sprites ou vos personnages.
Attention !!! ce système ne peut pas convenir a tout le monde ou à tout les type de jeux, c'est pourquoi nous verrons plus tard 1 ou 2 autres méthodes complémentaires.


Note : dans ce cours qui n'est pas compliqué en soi, il vous faudra manipuler les pointeurs donc n'hésitez pas à réviser les base :)
Car ça reste un élément indispensable (les pointeurs bien sur )


A NOTER QUE SUPER MARIO TOY A ETE FAIT SUR CETTE BASE LA (pour vous donner une indication des possibilités)



1) Déclaration générale

Donc là dans un premier temps on va créer des structures pour gérer tout ça, tout va partir de là :)


/**
* \struct ANIMATION
* \brief Utilisé pour l'animation des objets
*
*/
typedef struct {
int position; /*!< position de l'animation (elle et diviser par ligne) */
int anim_marche; /*!<pour l'animation indique la position actuel de l'animation */
int temp_anim_marche;/*!<variable interne pour l'animation s'incrémente jusqu'à ce qu'elle soit égale à Vitesse */
int animation;/*!<Nombre d'images en largeur pour une animation */
int anim_X;/*!< Largeur X de la taille du sprite pour l'animation)*/
int Hauteur;/*!< Hauteur Y de la taille du sprite pour l'animation)*/
int Vitesse;/*!<Vitesse de l'animation (de la première animation à la dernière)*/
bool flipv, fliph;/*!< Pour indiquer si l'image est inversée horizontalement ou verticalement*/
bool Active;/*!< Pour indiquer si l'image est active si oui alors on peut l'animer*/

} ANIMATION;




2eme structure utile, notez que cette fonction est presque vide mais je vous invite à rajouter certains éléments suivant vos besoin, comme vx et vy pour la vitesse du personnage, y compris le nécessaire pour vos collisions entre sprites nécessaire et PROPRE a chaque sprite !!

Je ne sais pas si je me fait bien comprendre.


/**
* \struct OBJETS
* \brief Utilisé pour l'animation des objets ainsi que pour les collision
*
* OBJETS est une structure pour la gestion des objets pour l'animation et les collisions
*/
typedef struct OBJETS{
//sprite du player
OSL_IMAGE *sprite ;/*!< Sprite a utiliser pour l'objet */

ANIMATION Animation;/*!< pour pouvoir utiliser la structure pour l'animation*/

} OBJETS;






2) Les fonctions


A partir de là on a planté le décors donc on va créer notre première fonction pour créer nos sprites.

/*-------------------------------------------------------------------------*/
/**
@brief Création d'un objet
@param Image Image à charger pour créer l'animation
@param PossitionDepard Position de départ sur la planche de sprite pour l'animation
@param animation Nombre de frame pour l'animation
@param anim_X Largeur du sprite pour une Frame
@param Hauteur Hauteur du sprite pour une Frame
@param Vitesse Vitesse pour l'animation(De la première frame a la dernière)
@return Retourne un Objet créé et donc utilisable

Cette fonction permet de créer un objet pour pouvoir l'utiliser pour plusieurs choses :
(Simple animation, Personnage, Collision...)
*/
/*--------------------------------------------------------------------------*/
OBJETS *DevsCreerSprite(OSL_IMAGE *Image,int PossitionDepard,int animation ,int anim_X,int Hauteur,int Vitesse )
{
//Allocation de la mémoire pour notre Nouvelle Objet
OBJETS *obj;
obj = (OBJETS*)malloc(sizeof(OBJETS));
//si il y a un problème de mémoire alors on arrête tout et on libère la mémoire
if (!obj){
if (obj) free(obj);
return NULL;
}
memset(obj, 0, sizeof(OBJETS));

//ici rien de méchant on initialise l'objet par rapport au paramètre mis en haut de la fonction
obj->sprite = Image;
obj->Animation.position = PossitionDepard;
obj->Animation.animation = animation - 1;
obj->Animation.anim_X = anim_X;
obj->Animation.Vitesse = Vitesse;
obj->Animation.Hauteur = Hauteur;

//Initialisation de l'animation à la première animation par défaut (en haut a gauche du sprites)
oslSetImageTileSize(obj->sprite,0,obj->Animation.position,obj->Animation.anim_X,obj->Animation.Hauteur); // image de depard

return obj;
}





Maintenant on va donner la possibilité d'arrêter ou de reprendre l'animation.
Là je pense que les commentaires du code seront plus que suffisants :)

/*-------------------------------------------------------------------------*/
/**
@brief Rendre active une animation
@param Obj Objet pointer

Cette fonction permet de mettre actif l'animation de l'objet
*/
/*--------------------------------------------------------------------------*/
void DevsAnimationPlay(OBJETS* Obj){
Obj->Animation.Active = 1;
}

/*-------------------------------------------------------------------------*/
/**
@brief Mettre en Pause une animation
@param Obj Objet pointé

Cette fonction permet de mettre en pause l'animation de l'objet
*/
/*--------------------------------------------------------------------------*/
void DevsAnimationStop(OBJETS* Obj){
Obj->Animation.Active = 0;
}




Maintenant on va faire le changement d'animation automatiquement ;)
simple et efficace.


/*-------------------------------------------------------------------------*/
/**
@brief Animation d'un Objet
@param Obj Objet a Animer

Cette fonction permet d'animer un objet frame par frame en tenant compte de la vitesse et de tous les paramètres
*/
/*--------------------------------------------------------------------------*/
void DevsAnimation_sprite(OBJETS* Obj)
{
if (Obj->Animation.animation >= 1)
{


if (Obj->Animation.temp_anim_marche == Obj->Animation.Vitesse)
{
//relance l'anim car elle et finit
if (Obj->Animation.anim_marche == Obj->Animation.animation + 1) Obj->Animation.anim_marche = 0;

//relance la tempo
Obj->Animation.temp_anim_marche = 0;

if (Obj->Animation.anim_marche == 0)
{
oslSetImageTileSize(Obj->sprite,0,Obj->Animation.position,Obj->Animation.anim_X,Obj->Animation.Hauteur);
}else
{
//affiche la position adéquate
oslSetImageTileSize(Obj->sprite,(Obj->Animation.anim_marche * Obj->Animation.anim_X),Obj->Animation.position,Obj->Animation.anim_X,Obj->Animation.Hauteur);
}

//prochaine étape de l'anim
Obj->Animation.anim_marche++;//possition de l'annimation changer

}
//tempo de l'anim
Obj->Animation.temp_anim_marche++;

}
}





Maintenant on va donner la possibilité de supprimer l'objet (l'animation)

/*-------------------------------------------------------------------------*/
/**
@brief Supprime un Objet
@param Obj Objet pointer


Cette fonction permet de supprimer un objet (de libérer de la mémoire)
*/
/*--------------------------------------------------------------------------*/

void DevsDeleteAnnimation(OBJETS* Obj) {
oslDeleteImage(Obj->sprite);
free(Obj);
}





Une autre petite fonction utile : on va permettre de faire une pause sur une Frame en particulier (ça peut être utile en cas d'animation pour une intro ou autre ....)

/*-------------------------------------------------------------------------*/
/**
@brief Pause sur une Frame très précise
@param Obj Objet pointer
@param Poss Position de l'objet (Série d'animation au niveau de la hauteur)
@param animation Numéro de la frame sur laquelle on doit faire une pause

Cette fonction permet de faire une pause sur une frame bien précise
*/
/*--------------------------------------------------------------------------*/
void DevsSetAnimationFramePause(OBJETS* Obj,int Poss,int animation)
{
Obj->Animation.position = Poss;
Obj->Animation.temp_anim_marche = 0;
Obj->Animation.anim_marche = animation;

Obj->Animation.Active = 0;
oslSetImageTileSize(Obj->sprite,(Obj->Animation.anim_marche * Obj->Animation.anim_X),Obj->Animation.position,Obj->Animation.anim_X,Obj->Animation.Hauteur); // image de depard

}




Maintenant on va voir comment faire un Reset de l'animation en cours.
On veut tout simplement remettre l'animation sur la première frame assez rapidement.

/*-------------------------------------------------------------------------*/
/**
@brief Faire un Reset de l'animation en COURS
@param Obj Objet pointé

Cette fonction permet de faire un reset de l'animation en cours (Frame 0)
*/
/*--------------------------------------------------------------------------*/
void DevsResetPossition(OBJETS* Obj)
{
Obj->Animation.temp_anim_marche = 0;
Obj->Animation.anim_marche = 0;
oslSetImageTileSize(Obj->sprite,0,Obj->Animation.position,Obj->Animation.anim_X,Obj->Animation.Hauteur);
}




Maintenant et ça reste indispensable on va donner la possibilité de changer facilement la position de l'animation.
(petit rappel) une position = une ligne ex:
ligne 1 = animation marche gauche
ligne 2 = animation marche droite
ligne 3 = animation saut
......

/*-------------------------------------------------------------------------*/
/**
@brief Changement de position de l'objet
@param Obj Objet pointé
@param Poss Nouvelle Position de l'objet (position = ligne concernée)

Cette fonction Permet de changer la position de l'objet (gauche, droite , saute, tir ....)
*/
/*--------------------------------------------------------------------------*/
void DevsChangePossition(OBJETS* Obj, int Poss)
{
if (Poss != Obj->Animation.position)
{
Obj->Animation.position = Poss;
Obj->Animation.temp_anim_marche = Obj->Animation.Vitesse ;
Obj->Animation.anim_marche = 1;
oslSetImageTileSize(Obj->sprite,0,Obj->Animation.position,Obj->Animation.anim_X,Obj->Animation.Hauteur);

}
else if(Poss == Obj->Animation.position && Obj->Animation.anim_marche == 0){
Obj->Animation.temp_anim_marche = Obj->Animation.Vitesse ;
Obj->Animation.anim_marche = 1;
oslSetImageTileSize(Obj->sprite,(Obj->Animation.anim_marche * Obj->Animation.anim_X),Obj->Animation.position,Obj->Animation.anim_X,Obj->Animation.Hauteur);
}

}





Maintenant on va donner la possibilité de faire un effet miroir d'un sprite, idéal quand on ne veut pas faire plusieurs fois la même animation pour gauche et droite par exemple qui en réalité sont la même image mais inversée.
(hé oui faut penser à tout)

/*
=================
ANNIMATION::DevsSetFliphOn

A REVOIR
Active le flip Horizontal du sprite pour l'animation en cours (idéal pour la gauche et la droite qui sont en fait la même chose mais en miroir)
=================
*/
void DevsSetFliphOn(OBJETS* Obj)
{
if (Obj->Animation.fliph == 0)Obj->Animation.fliph = 1;
}

/*-------------------------------------------------------------------------*/
/**
@brief Annule l'effet miroir d'une animation
@param Obj Objet pointé

Cette fonction permet d'annuler l'effet miroir d'une animation
*/
/*--------------------------------------------------------------------------*/
void DevsSetFliphOff(OBJETS* Obj)
{
if (Obj->Animation.fliph == 1)Obj->Animation.fliph = 0;
}



3) Le système d'affichage de l'animation


Maintenant que on a créé plusieurs fonctions, il faut pouvoir afficher le sprite ou l'animation ça pourrait être pratique.
Dans l'optique où l'on peut faire un effet miroir à l'animation j'ai du reprendre une fonction de brunni et la modifier pour l'adapter au besoin.


/*-------------------------------------------------------------------------*/
/**
@brief effet de miroir d'une image
@param img sprite a inverser
@param flipType flipType 0 = filp horizontal / flipType = 1 flip vertical

dessine une image et fait un effet de miroir Horizontal ou vertical
*/
/*--------------------------------------------------------------------------*/
void DevsDrawImageSimpleFlip(OSL_IMAGE *img, int flipType) {
OSL_UVFLOAT_VERTEX *vertices;
int x = img->x, y = img->y;

oslSetTexture(img);
if (oslImageGetAutoStrip(img)) {
if (oslVerifyStripBlit(img))
return;
}

vertices = (OSL_UVFLOAT_VERTEX*)sceGuGetMemory(2 * sizeof(OSL_UVFLOAT_VERTEX));

if (!flipType){
vertices[0].u = img->offsetX1;
vertices[0].v = img->offsetY0;
vertices[0].x = x;
vertices[0].y = y;
vertices[0].z = 0;

vertices[1].u = img->offsetX0;
vertices[1].v = img->offsetY1;
vertices[1].x = x + img->stretchX;
vertices[1].y = y + img->stretchY;
vertices[1].z = 0;
}
else {

vertices[0].u = img->offsetX0;
vertices[0].v = img->offsetY1;
vertices[0].x = x;
vertices[0].y = y;
vertices[0].z = 0;

vertices[1].u = img->offsetX1;
vertices[1].v = img->offsetY0;
vertices[1].x = x + img->stretchX;
vertices[1].y = y + img->stretchY;
vertices[1].z = 0;


}

sceGuDrawArray(GU_SPRITES,GU_TEXTURE_32BITF|GU_VER TEX_16BIT|GU_TRANSFORM_2D,2,0,vertices);
}




Il nous reste plus que la fonction pour afficher le sprite et c'est terminé pour le moment :)


*-------------------------------------------------------------------------*/
/**
@brief Animation d'un Objet
@param Obj Objet à Animer ou à Dessiner

Cette fonction permet de dessiner le sprite et de l'animer si il est actif
*/
/*--------------------------------------------------------------------------*/
void DevsDrawImageAnime(OBJETS* Obj)
{
//verifie si il y a un effet miroir ou non :)
if (Obj->Animation.fliph)DevsDrawImageSimpleFlip(Obj->sprite,0);
else if (Obj->Animation.flipv)DevsDrawImageSimpleFlip(Obj->sprite,1);
//rien du tout alors on affiche le sprite normalement
else {oslDrawImage(Obj->sprite);}

//on anime le sprite uniquement si il est pas en "Pause"
if (Obj->Animation.Active)DevsAnimation_sprite(Obj);
}









[B]Exemple concret

on va animer quelques sprites :


http://projetpsp.free.fr/anim_playeradvance/bac.png

http://projetpsp.free.fr/anim_playeradvance/fume.png

http://projetpsp.free.fr/anim_playeradvance/zelda.png

http://projetpsp.free.fr/anim_playeradvance/zou.png




#include <oslib/oslib.h>
#include "annimation.h"

PSP_MODULE_INFO("Devdlib", 0, 1, 1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);

//declaration
OBJETS *zelda, *fumee, *zou, *bac ;
OSL_FONT *maFonte;


//define pour les annimations
//position pour les personnages, plusieurs méthodes existent, ce n'est pas l'idéal mais bon.
#define ZELDA_HAUT 50*2
#define ZELDA_BAS 50*3
#define ZELDA_GAUCHE 50*1
#define ZELDA_DROITE 0

#define ZOU_HAUT 60*2
#define ZOU_BAS 60*3
#define ZOU_GAUCHE 60*1
#define ZOU_DROITE 0

//déclare une fonction qu'on va utiliser
void Touche();

int main(int argc, char* argv[])
{
//Initialise OSLib
oslInit(0);
oslInitGfx(OSL_PF_8888, 1);
oslInitConsole();

//Configure le joypad
oslSetKeyAutorepeatInit(40);
oslSetKeyAutorepeatInterval(10);

//chargement de ma police perso
maFonte = oslLoadFontFile("media/ArialBlack.oft");
//utilise une fonte
oslSetFont(maFonte);

//initialisation des objets animés
zelda = DevsCreerSprite(oslLoadImageFile("media/zelda.png", OSL_IN_RAM, OSL_PF_5551),ZELDA_BAS, 3 ,50,50,12);
fumee = DevsCreerSprite(oslLoadImageFile("media/fume.png", OSL_IN_RAM, OSL_PF_5551),0, 4 ,76,72,30);
zou = DevsCreerSprite(oslLoadImageFile("media/zou.png", OSL_IN_RAM, OSL_PF_5551),ZOU_BAS, 3 ,56,60,15);
bac = DevsCreerSprite(oslLoadImageFile("media/bac.png", OSL_IN_RAM, OSL_PF_5551),0, 4 ,32,32,25);

//ici on les animes tous
DevsAnimationPlay(bac);
DevsAnimationPlay(zelda);
DevsAnimationPlay(zou);
DevsAnimationPlay(fumee);


//Position de départ en X et Y de tous les sprites
zelda->sprite->x = 50;
zelda->sprite->y = 50;

fumee->sprite->x = 0;
fumee->sprite->y = 272 - fumee->sprite->stretchY;

zou->sprite->x = 200;
zou->sprite->y = 50;

bac->sprite->x = 350;
bac->sprite->y = 200;



while (!osl_quit)
{
//Début du dessin
oslStartDrawing();
oslCls();

Touche();

oslPrintf_xy(2,2,"UTILISE LES FLECHES");

//dessine les objets
DevsDrawImageAnime(bac);
DevsDrawImageAnime(zelda);
DevsDrawImageAnime(zou);
DevsDrawImageAnime(fumee);


oslEndDrawing();
oslSyncFrame();
}

//efface tout avant de quitter
oslEndGfx();
oslQuit();
return 0;
}


void Touche()
{
oslReadKeys();

//ici on presse la touche du haut
if (osl_keys->held.up){

//donc on change la position du personnage (à noter que la fonction elle même vérifie d'abord que
//l'ancienne position était la même ou non, si elle est la même alors on l'anime simplement, autrement on la change
DevsChangePossition(zelda,ZELDA_HAUT);
//on s'assure que l'on est toujours en lecture de l'animation
DevsAnimationPlay(zelda);

//que dire à part la même chose ..
DevsChangePossition(zou,ZOU_HAUT);
DevsAnimationPlay(zou);

}

if (osl_keys->held.down){
DevsChangePossition(zelda,ZELDA_BAS);
DevsAnimationPlay(zelda);

DevsChangePossition(zou,ZOU_BAS);
DevsAnimationPlay(zou);
}

if (osl_keys->held.left){
DevsChangePossition(zelda,ZELDA_GAUCHE);
DevsAnimationPlay(zelda);

DevsChangePossition(zou,ZOU_GAUCHE);
DevsAnimationPlay(zou);

}

if (osl_keys->held.right){
DevsChangePossition(zelda,ZELDA_DROITE);
DevsAnimationPlay(zelda);

DevsChangePossition(zou,ZOU_DROITE);
DevsAnimationPlay(zou);
}

//si aucune touche alors on remet l'animation à 0 et on arrête l'animation
if (!osl_keys->held.value){
DevsAnimationStop(zelda);
DevsResetPossition(zelda);
DevsAnimationStop(zou);
DevsResetPossition(zou);
}


}






Voici le code source de ce que l'on a vu dans ce cours
annimation.rar (http://projetpsp.free.fr/anim_playeradvance/annimation.rar)



Je ne sais pas si j'ai été suffisament clair dans ce tutorial, si besoin je rajouterais des précisions.


Merci d'editer mon post pour corriger les fautes d'orthographe :)

Yodajr
31/07/2008, 13h51
Merci pour le tuto :)

Je commence à corriger...
Mais un conseil : si tu as Firefox, installe le plugin "Dictionnaire HunSpell en français (réforme 1990)"
Il ne pourra pas corriger tes fautes de grammaire, mais soulignera en rouge tes fautes d'orthographe et te fera une liste de propositions avec un clic droit (comme dans word)