PDA

Voir la version complète : [PSP][Tutorial] OSlib - Jour 6 : Et tou ondoule...


Yodajr
10/04/2006, 02h56
Jour 6 : Animation des sprites

Aujourd'hui on commence les choses sérieuses B)
Car au dela de la juste mise en pratique des fonctions d'oslib, nous allons beaucoup plus parler à partir de maintenant de programmation de jeu vidéo à proprement parler.
Aussi, je m'en vais vous expliquer MA facon de faire, qui n'est certainement pas la meilleure, mais voila quoi, ca fonctionne plutot bien :p
Je veux juste que vous compreniez que ce n'est pas la seule et unique facon de faire ;)

Trève de blabla, let's go !


1) Le Spriteset

Vu que nous voulons animer notre sprite, il faut faire comme pour les dessins animés : dessiner toutes les étapes de l'animation (ou en tout cas un certain nombre en fonction du degré de fluidité voulu : le nombre d'étape d'animation d'un mario marchant est risible face à celui d'un aladdin MD)
Pour ce tuto, nous aurons besoin de 4 séquences d'animation : perso marchant vers la droite, vers la gauche, vers le haut et enfin vers le bas.
Ainsi que les 4 positions statiques.

Une fois les dessins (ou les rips) effectués, il faut établir les bases du spriteset :
Cherchez dans toutes les étapes d'anims celle qui est la plus large en pixels ainsi que celle qui est la plus haute : vous aurez ainsi les dimensions du cadre référence.

Par exemple, pour ce tuto, j'ai rippé l'anim de marche du héros de chrono trigger (vous aviez reconnu ? trop forts ! :D).
Et dans le tas, j'ai trouvé l'étape la plus large (22 pixels de large) : http://hothmoon.free.fr/psp/dev/jour06_img1.png ainsi que la plus haute (35 pixels de haut) : http://hothmoon.free.fr/psp/dev/jour06_img2.png
J'obtiens donc pour mon cadre référence 22x35.
Ce qui veux dire que tous mes sprites constituant les differentes animations doivent dorénavant tous mesurer 22x35 : à vous d'agrandir, mais pas en resizant le sprite, juste en ajoutant du vide aux bons endroits, tout en gardant un même repère fixe (le sol par exemple) :
http://hothmoon.free.fr/psp/dev/jour06_img3.png deviens : http://hothmoon.free.fr/psp/dev/jour06_img4.png
(j'ai agrandi un poil afin que vous puissiez voir les changements apportés, changements que j'ai coloré en vert pour l'exemple)

Une fois que j'ai tous mes sprites à la bonne taille, je les assemble en une seule et même image : le spriteset.
Pour faciliter le code plus tard, je les dispose sur autant de lignes que de directions, chaque colonne représentant une étape d'animation.
Mon spriteset ressemble alors à cela :

http://hothmoon.free.fr/psp/dev/jour06_img5.png

C'est beau hein ? :D
Donc mon spriteset à moi possède 4 directions, une étape statique et 6 étapes de marche, retenez cela pour mieux comprendre le code qui suit :)


2) Le code

//La librairie principale OSLib
#include <oslib/oslib.h>

//les callbacks
PSP_MODULE_INFO("OSLib Sample", 0, 1, 1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);

//definition des pointeurs vers nos images
OSL_IMAGE *chrono;

//defines
#define BAS 0
#define HAUT 35
#define DROITE 70
#define GAUCHE 105

//variables
int chrono_position;
int anim_marche;
int temp_anim_marche;

//fonctions
void Touches();
void AnimMarche();

int main()
{
//Initialisation de la librairie
oslInit(0);

//Initialisation du mode graphique
oslInitGfx(OSL_PF_8888, 1);

//definition de la transparence
oslSetTransparentColor(RGB(255,0,255));

//chargement de nos images (oui, le "loading" :p)
chrono = oslLoadImageFile("chrono_sheet.png", OSL_IN_RAM, OSL_PF_5551);

//plus de transparence
oslDisableTransparentColor();

//vérification
if (!chrono)
oslDebug("Verifiez que tous les fichiers sont bien copiés dans le répertoire du jeu.");

//place chrono vers le centre de l'écran et dans sa position de départ
chrono->x = 240;
chrono->y = 130;
chrono_position = BAS;

//boucle principale
while (!osl_quit)
{
//Permet de dessiner
oslStartDrawing();

//check des touches
Touches();

//dessine un dégradé
oslDrawGradientRect(0,0,480,272,RGB(0,128,0), RGB(0,128,0), RGB(128,255,128), RGB(128,255,128));

//dessine notre sprite
oslDrawImage(chrono);

//Fin du dessin
oslEndDrawing();

//Synchronise l'écran
oslSyncFrame();
}

//on quitte l'application
oslEndGfx();
oslQuit();
return 0;
}


void Touches()
{
//Lit les touches
oslReadKeys();

if (osl_keys->held.down)
{
//deplace le sprite
chrono->y += 2;

//affiche la bonne ligne dans le spriteset
chrono_position = BAS;

//on anime le sprite
AnimMarche();
}
if (osl_keys->held.up)
{
chrono->y -= 2;
chrono_position = HAUT;
AnimMarche();
}
if (osl_keys->held.left)
{
chrono->x -= 2;
chrono_position = GAUCHE;
AnimMarche();
}
if (osl_keys->held.right)
{
chrono->x += 2;
chrono_position = DROITE;
AnimMarche();
}

//si aucune touche n'est appuyée
if (!osl_keys->held.value)
{
//reinitialisation des variables, pour la prochaine fois
anim_marche = 0;
temp_anim_marche = 0;

//affiche chrono dans sa derniere position
oslSetImageTileSize(chrono,0,chrono_position,22,35 );
}
}


void AnimMarche()
{
//tempo de l'anim
temp_anim_marche++;
if (temp_anim_marche == 6)
{
//relance la tempo
temp_anim_marche = 0;

//prochaine étape de l'anim
anim_marche++;

//affiche la position adequate
oslSetImageTileSize(chrono,(anim_marche * 22),chrono_position,22,35);

//relance l'anim
if (anim_marche == 6) anim_marche = 0;
}
}


3) Explications

#define BAS 0
#define HAUT 35
#define DROITE 70
#define GAUCHE 105
Ici je met en define 4 nombres. Mais pourquoi ceux là ? me demandrez vous... et pourquoi pas ? vous répondrais je :rolleyes:
Non sérieusement, il s'agit du Y correspondant à chacune des 4 directions de mon spriteset. Vous vous rappelez que mon cadre référence fait 35 de haut ? donc le Y de la 1ere direction est à 0, le 2ème à 35, etc...
Donc quand je dirai : positionne toi à GAUCHE, il saura que ca correspond au nombre 105, qui lui même correpond à la dernière direction de mon spriteset ;)

chrono_position = BAS;
Bon ben voila, on y est, intero surprise, après cette commande, à quoi est égal la variable chrono_position ?
Ceux qui ont répondu 67 vont au coin, et ceux qui ont répondu 0 ont le droit de continuer :P
(au fait, retenez qu'au début chrono_position = 0, ca facilitera une explication plus loin)

oslDrawGradientRect(0,0,480,272,RGB(0,128,0), RGB(0,128,0), RGB(128,255,128), RGB(128,255,128));
Dessine un beau dégradé à 4 points. Comme mes 2 premiers et mes 2 derniers points sont identiques, ici ce n'est qu'un dégradé à 2 points (vert foncé vers vert clair)

if (!osl_keys->held.value)
La valeur "value" de la structure osl_keys passe à 1 quand une touche est pressée ou maintenue (pressed ou held).
Donc il me suffit de tester si elle est à 0 pour savoir si le joueur n'appuie sur aucune touche ;)

oslSetImageTileSize(chrono,0,chrono_position,22,35 );
Bon, voici la commande d'oslib la plus importante à comprendre pour ce tuto.
En fait cette fonction permet d'afficher le morceau que l'on veut d'une image, prenons pour mieux comprendre la formulation générique :
oslSetImageTileSize(image,x,y,largeur,hauteur);

Vous vous rappelez notre spriteset ? sans cette commande, il s'afficherai entierement sur l'écran, ce serai pas cool admettez :P
Donc ici, on dit à la PSP : "affiche moi l'image certe, mais seulement le morceau qui commence au point x,y et qui mesure largeur sur hauteur"
Une fois ceci fait, à chaque appel de oslDrawImage(image), seul ce fameux morceau sera affiché.
Compris ?

Donc ici, je dit à la PSP : "affiche moi le spriteset de chrono, mais seulement le morceau qui commence à 0,chrono_position et qui fait 22 pixels de large sur 35 pixels de haut"
Un peu confus toujours ? bon, considérons que c'est la premiere execution et que chrono_position = 0 (bravo à ceux qui s'en sont rappelé, un bon point), la commande deviens :
"affiche moi le spriteset de chrono, mais seulement entre les points 0,0 et 22,35"
C'est plus clair là, non ? elles ne vous disent rien ces coordonnées ?
Hé oui, il s'agit de ma toute premiere étape, le 1er sprite du spriteset.
Ce sprite correspond donc bien à la position BAS.

Vous comprenez un peu mieux la ligne du code ?
Remplacez mentalement la valeur de chrono_position par la valeur correspondant à la position GAUCHE (105), vous pouvez savoir quelle sprite va s'afficher : le dernier de la 1ere colonne (0,105 - 22,140)

Comprennez bien, car la même ligne reviens, mais en un peu plus compliqué :P

temp_anim_marche++;
if (temp_anim_marche == 6)
{
temp_anim_marche = 0;
....
Ici je temporise un peu, sinon nous n'aurions pas le temps de voir les differentes étapes de l'animation. Il faut juste ne pas oublier de repasser la variable temp à 0 :)

oslSetImageTileSize(chrono,(anim_marche * 22),chrono_position,22,35);
Oui voila, je vous avais prévenus :lol:
Mais c'est pas si compliqué :
Observez bien le code : à chaque fois que la tempo est atteinte, on incrémente la variable anim_marche.
Considérons que nous sommes au début et qu'elle était à 0, donc elle est maintenant égale à 1.
Considérons également que chrono_position est toujours égal à 0 (BAS)
Remplacons maintenant toutes les variables par leurs valeurs, nous obtenons :
oslSetImageTileSize(chrono,22,0,22,35);
Ce qui correspond à demander à la PSP d'afficher le 2ème sprite de la 1ere ligne de mon spriteset, c'est à dire la 1ere étape de l'anim de marche vers l'avant !
On peut ainsi continuer mentalement d'incrementer anim_marche pour voir que l'on pointe à chaque fois sur l'étape suivante !
Même chose en changeant la valeur de chrono_position !

Regardez donc le code dans sa globalité : quand le joueur maintient la direction droite appuyée, on déplace le sprite vers la droite, puis on dit que chrono_position est maintenant égal à DROITE ( 3ème ligne d'anim du spriteset) et on lance l'anim.
Tant qu'il maintient la touche, la fonction AnimMarche est lancée à chaque frame, la variable anim_marche est incrémentée toutes les 6 frames, ce qui correspond à une nouvelle position du sprite à afficher. Et enfin quand la derniere étape est atteinte, on revient au début de l'anim et c'est reparti (if (anim_marche == 6) anim_marche = 0;)

Vous n'avez toujours rien compris ? c'est pas grave, remarquez les valeurs numériques de la commande, elles ne vous disent rien ?
Oui c'est ca, il s'agit des valeurs de mon cadre de référence (tout au début). Vous pouvez donc vous contentez de copier/coller le code et de remplacer les valeurs par les votres ;)

Pour les pros : On peut également utiliser oslSetImageTile(image,x0,y0,x1,y1) qui permet d'afficher un morceau de l'image de facon plus précise ;)
Il est possible aussi qu'au lieu d'avoir dans le spriteset les 2 directions droite et gauche, n'en avoir qu'une seule et effectuer un "miroir" sur le sprite au bon moment (oslMirrorImageH(image))


4) Screen et eboot

Voila, vous devez obtenir un truc comme ça :

http://hothmoon.free.fr/psp/dev/jour06_screen.png

Téléchargez l'eboot compilé pour 1.5 ici (http://hothmoon.free.fr/psp/dev/jour06_eboot.zip)

Tembargo
10/04/2006, 03h00
...ça donne envie :(

Beau travaille alala

lunatic
05/07/2006, 11h09
Bien je crois que je vais tenter de faire ces tutaux ...
Ils m'ont l'air très bien fait ma fois :w00t:
Un grand merci Yodajr pour son fabuleux travail !!!

Yodajr
06/07/2006, 01h20
Merci, faut que je les finisse d'ailleurs, il m'en reste encore 2 :)

gee_love
06/07/2006, 02h06
mazettte...même moi je comprend... ils sont toujours très bien expliqués tes tutos Yoda :) bonne continuation !

Evil_Gouky
07/08/2006, 09h37
Bonjour Yodajr,

J'aurais une question concernant les sprites !!!

y a t'il une taille maximum pour un sprite Hauteur / Largeur ?

Car j'ai testé avec 10 srpites de 36*36 soit un set de 360L sur 36H ça c'est ok mais, avec 10 sprites de 100*100 soit une set de 1000L sur 100H cela ne fonctionne pas j'obtient bien un carré de 100*100 pour l'annimation mais je ne vois pas mon sprite ??? juste un carré mauve (255, 0, 255) celui pour la transparence.

je n'ai pas vu dans la doc de OSLib une limitation ?

Merci d'avance pour la réponse...
@+

Yodajr
10/08/2006, 01h30
Oui y'a une limitation au niveau de la PSP, tu ne peux pas afficher d'images avec une résolution supérieure à 512x512 (j'en parle dans le tuto 4 ;) )

Poison
15/04/2007, 19h22
Encore un super tuto Yodajr, j'ai juste une petit question, est-on oubligé d'utiliser des variables globales ?
Ne peut-on pas prendre des pointeurs ?

En tout cas merci pour le tuto

Yodajr
15/04/2007, 20h58
Pas trop compris la question :)

Poison
15/04/2007, 21h43
:p par rapport au deux fonctions void, tu utilise les variables globale :

//variables
int chrono_position;
int anim_marche;
int temp_anim_marche;

Au lieu d'utiliser des variables globale, peut-on utiliser des pointeurs sur variable ?

Ceci dit, ce n'est juste qu'une question pour savoir si c'était possible ou pas , rien de plus ? :p

Yodajr
15/04/2007, 21h54
Heu, oui probablement, bien que je n'en voit pas l'interet :)

Brunni
15/04/2007, 23h30
Je n'en vois pas non plus l'intérêt. Ceci dit tu peux utiliser ce que tu veux: comme par exemple des variables globales mais aussi locales, ou stocker ces paramètres dans des structures.
Tu peux même utiliser des registres si tu veux :p
move $t0, 0
boucle:
addi $t0, $t0, 1
bne $t0, 6, _noReset
move $t0, 0
_noReset:
[...]
j boucle

:ph34r:

Mokhet
31/12/2007, 01h49
Bonjour, tout d'abord formidables tutoriaux Yodajr et un grand merci à Brunni et son OSLib sans qui je ne serais arrivé à rien sur ma PSP.

Sinon, au delà du fait que ça me force à me remettre au C que j'ai abandonné après un survol d'horreur y'a plus de 15 ans déjà :D pardon pour le probable manque de précision dans les termes :wub: , j'aurais deux ou trois questions si c'était pas trop abusé.

Afin de reprendre le langage en main, je modifie les tutos de Yodajr (encore bravo, quoi je me répète ? :) Jusque là, tout va bien.

J'ai donc ajouté ceci au début de la fonction AnimMarch()
void AnimMarche()
{
// ne pas dépasser les bords
if ( chrono->x < 0 ) chrono->x = 0;
else if ( chrono->x > 479 - chrono->stretchX ) chrono->x = 479 - chrono->stretchX;
if ( chrono->y < 0 ) chrono->y = 0;
else if ( chrono->y > 271 - chrono->stretchY ) chrono->y = 271 - chrono->stretchY;

Ça me va très bien, ça fait ce que je veux (ne pas laisser chrono sortir de l'écran) mais voila enfin la question.

Est-ce qu'il existe une propriété dans la structure OSL_IMAGE qui me permettrait de connaitre la "width" et la "height" originale de l'image, la taille de lecture je dirais. J'ai fouiné dans la doc et j'ai rien trouvé mis à part sizeX et sizeY qui ne me semble pas être ce que je cherche. Mis à part que pour l'utilisation que j'en fais elle me vont très bien, mais j'aurais bien aimé savoir si de telles propriétés existaient.

Et sinon, j'ai une autre question qui concerne plus spécialement Brunni mais bon, allons y, ça peut éventuellement intéressé d'autres personnes.

L'utilisation du logo OSLib est-elle autorisée ? J'aimerais l'afficher au démarrage de mon programme et dans la rubriques des "Remerciements".

Merci de m'avoir lu :)

Yodajr
02/01/2008, 23h13
Est-ce qu'il existe une propriété dans la structure OSL_IMAGE qui me permettrait de connaitre la "width" et la "height" originale de l'image, la taille de lecture je dirais. J'ai fouiné dans la doc et j'ai rien trouvé mis à part sizeX et sizeY qui ne me semble pas être ce que je cherche. Mis à part que pour l'utilisation que j'en fais elle me vont très bien, mais j'aurais bien aimé savoir si de telles propriétés existaient.
Si si c'est sizeX et sizeY qui contiennent la taille de l'image, pourquoi penses tu le contraire ?

L'utilisation du logo OSLib est-elle autorisée ? J'aimerais l'afficher au démarrage de mon programme et dans la rubriques des "Remerciements".
Bien sûr, en plus il est magnifique ce splash screen :wub:

Pour la v1 (et surement la v2 mais pas testé) d'oslib c'est comme ça :

oslShowSplashScreen(1);
Avec les fichiers "texte.png" et "etoile.png" dans un répertoire "logo" à placer dans le répertoire du jeu sur la mémory stick ;)

Mokhet
05/01/2008, 16h19
Si si c'est sizeX et sizeY qui contiennent la taille de l'image, pourquoi penses tu le contraire ?

Euh parce que je suis un peu bête, que je me mélange, et que je devrais expérimenter et lire la doc plusieurs fois pendant plusieurs mois avant de demander de l'aide. :whst:

Sinon, surtout parce que j'avais zappé le fait que l'image complète possédait une taille sizeX et sizeY évidemment plus grande que le sprite affiché.

Donc dans le cas présent, et sous réserve que je trouve encore un nouveau truc dans la doc et sous contrôle des experts, ce ne serait pas sizeX ni sizeY mais stretchX et stretchY.