Jour 11 - Fonctions vidéo ← Précédent

Jours 12 - Démos rapides

Cette partie du tutorial contient des petits bouts de codes, nouveaux ou basés sur certains des autres exemples , qui peuvent vous aider dans le développement DS...

Suivre le Stylet

Ok, j’ai fini par faire un exemple stupide avec un vaisseau qui suit le stylet :-P. Le code est dans les exemples de PAlib, dans Demos/FollowStylus/. Cet exemple montre un sprite qui suit le stylet en se tournant vers lui...

Je vais seulement donner et commenter la différence avec l’autre code Exemple de Trajectoire :

s32 x = (128) << 8; // position en x du vaisseau en point fixe 8bit
s32 y = (96) << 8; // Y
u16 angle = 0; // la direction dans laquelle on se déplace !
 
while(1)
{
	angle = PA_GetAngle(x>>8, y>>8, Stylus.X, Stylus.Y);
	PA_SetRotsetNoZoom(0, 0, angle); // Tourne le vaisseau dans la bonne direction
		
	if (Stylus.Held){ // Avance
		x += PA_Cos(angle);
		y -= PA_Sin(angle);
	}
 
	PA_OutputText(1, 5, 10, "Angle : %d  ", angle);
	
	PA_SetSpriteXY(0, 0, (x>>8)-16, (y>>8)-16); // Sprite position converted to normal...
		
	PA_WaitForVBL();
}
  • Pour commencer, x et y sont maintenant les coordonnées du centre du sprite, et non pas le coin supérieur gauche du sprite. Pourquoi? Car ça donne un meilleur résultat, vous verrez...
  • angle = PA_GetAngle(x»8, y»8, Stylus.X, Stylus.Y); Là, c’est complètement différent de l’ancien code d’angle. Cette fois, on récupère l’angle en utilisant le centre du sprite et le stylet (d’où le »8, et en utilisant x et y pour le centre du sprite.).
  • if (Stylus.Held){ a été changé afin qu’on bouge quand le stylet touche l’écran.

Et ça y est ! C’était une démo très simple, je l’admets, mais c’est parce que c’est la première ici :-)

Lancer et faire rebondir des Frisbees

Ce sera un tutorial en plusieurs étapes... Le but final sera d’avoir une jolie démo avec 10 frisbee volant, lancés par le stylet, et se heurtant... Nous ferons cela en 3 étapes simples:

  • Première étape, afficher un frisbee qui vole sur les deux écrans et lancé par le stylet
  • Ensuite, modifier le code pour avoir 10 frisbees, en utilisant un tableau de structure
  • Enfin, ajouter les collisions dans le code !

Part 1 - 1 Frisbee

Voici maintenant un beau petit exemple : il montre comment utiliser le code du stylet pour attraper un frisbee, le lancer, et le faire rebondir sur les murs (sur les 2 ecrans). Et aussi le faire tourner sur lui-meme, juste parce que j’en avais envie ...

Vous trouverez cet exemple dans Demos/Frisbee dans la prochaine version de la PAlib (il y avait une vieille version dépassée sans la gestion du stylet)

Je vais donner un gros morceau de code :

 
#define FRISBEE 10 // Nombre de Sprite...
#define SCREENHOLE 48 // Taille entre les écrans... C'est la taille la plus adaptée
 
 
typedef struct{
	s16 x, y; // This is for the frisbee's position
	s16 vx, vy; // Vitesse du Frisbee
	s16 angle; // To make the frisbee turn on itself
}frisinfos;
 
frisinfos frisbee;  // Frisbee structure variable
 
 
 
int main(void)
{
 
// Initialise la librairie...
PA_Init();
PA_InitVBL(); 
 
PA_InitText(1, 0);
 
// Charge les palettes pour les sprites sur les deux écrans
PA_DualLoadSpritePal(0, (void*)sprite0_Pal);
 
// Créer le sprite sur les deux écrans...
PA_DualCreateSprite(FRISBEE, (void*)frisbee_Sprite, OBJ_SIZE_32X32, 1, 0, 96, 300); // Bas de l'écran
PA_DualSetSpriteRotEnable(FRISBEE, 0); // Enable rotation/zoom, rotset 0
 
// Sprite initial position...
frisbee.x = 96+16; 
frisbee.y = 300+16; // on the bottom screen
 
// Speed of frisbee in both ways
frisbee.vx = 0;
frisbee.vy = 0;
 
while(1)
{
	// Bouge avec le stylet, ou autre...
	if (PA_MoveSprite(FRISBEE)){
		frisbee.x = PA_MovedSprite.X;
		frisbee.y = PA_MovedSprite.Y + 192 + SCREENHOLE;
		frisbee.vx = PA_MovedSprite.Vx;		frisbee.vy = PA_MovedSprite.Vy; 
	}
	else{
		// Maintenant la position fixe du frisbee sera changée par rapport à la vitesse...
		frisbee.x += frisbee.vx;
		frisbee.y += frisbee.vy;
		
		// Si on touche un bord, on inverse la vitesse
		if ((frisbee.x -16 <= 0) && (frisbee.vx < 0)) frisbee.vx = -frisbee.vx; 
		else if ((frisbee.x + 16 >= 256)&&(frisbee.vx > 0)) frisbee.vx = - frisbee.vx;
	
		// La même chose pour les limites verticales...
		if ((frisbee.y -16 <= 0) && (frisbee.vy < 0)) frisbee.vy = -frisbee.vy;
		else if ((frisbee.y + 16 >= 192 + 192 + SCREENHOLE)&& (frisbee.vy > 0)) frisbee.vy = - frisbee.vy;		
		// The bottom limit is at the bottom of the bottom screen, so that would be 2 screen heights, plus the space in between...
		PA_DualSetSpriteXY(FRISBEE, frisbee.x-16, frisbee.y-16);
	}
		
	PA_OutputText(1, 2, 10, "SpeedX : %d    ", frisbee.vx);
	PA_OutputText(1, 2, 11, "SpeedY : %d    ", frisbee.vy);
		
	frisbee.angle+=4; // Fait tourner le frisbee...
	PA_DualSetRotsetNoZoom(0, frisbee.angle);
 
	PA_WaitForVBL();  // Synch to the framerate...
	}

Et maintenant, les commentaires :

#define FRISBEE 10 // Nombre de Sprite...
#define SCREENHOLE 48 // Taille entre les écrans... C'est la taille la plus adaptée

sont justes 2 definitions qui nous simplifierons la vie. FRISBEE est le nombre de sprites de frisbee, et SCREENHOLE l’espace entre les 2 ecrans, en pixels.

// Load the palettes for the sprites on both screens
PA_DualLoadSpritePal(0, (void*)sprite0_Pal);
 
// Create the sprite on both screens...
PA_DualCreateSprite(FRISBEE, (void*)frisbee_Sprite, OBJ_SIZE_32X32, 1, 0, 96, 300); // Bas de l'écran
PA_DualSetSpriteRotEnable(FRISBEE, 0); // Enable rotation/zoom, rotset 0

Ici on charge la palette, on cree le sprite sur les 2 ecrans (d’ou le prefixe Dual), et on active les rotations. Vous avez remarque la coordonnee en Y du sprite ? 300... C’est pour l’ecran du bas. En “Dual mode”, PAlib considere la DS comme ayant un seul ecran de 384+SCREENHOLE pixels, dans notre cas 48 pixels (ce qui donne le meilleur resultat visuel sur DS, mais c’est horrible sur un emulateur...)

Ensuite on initialise les valeurs de la structure frisbee :

frisbee.x = 96+16; 
frisbee.y = 300+16; // on the bottom screen
 
frisbee.vx = 0;
frisbee.vy = 0;

La position est initialisée à 96, 300. +16 car on va utiliser le centre du sprite...et c’est un sprite de 32×32. La vitesse est initialisée à 0. On utilise pas de point fixe ici, mais on aurait pu...

Ensuite vient une des parties importantes du code :

if (PA_MoveSprite(FRISBEE)){
	frisbee.x = PA_MovedSprite.X;
	frisbee.y = PA_MovedSprite.Y + 192 + SCREENHOLE;
	frisbee.vx = PA_MovedSprite.Vx;		frisbee.vy = PA_MovedSprite.Vy; 
}

Cette partie du code a de multiples fonctions :

  • PA_MoveSprite(sprite number) est une fonction importante de la PAlib que l’on a déja vu. On vérifie si le stylet touche le sprite, et on le bouge en fonction de la position du stylet. Si le stylet bouge, on retournera 1. Si c’est le cas, on a beaucoup de choses a faire :
  • Sauvegarder les nouvelles coordonnées en X et Y du frisbee... C’est fait avec frisbee.x = PA_MovedSprite.X;.
    • Pourquoi PA_MovedSprite.X ? Parce que PA_MovedSprite.X retourne le point central du sprite déplacé
    • Pourquoi PA_MovedSprite.Y + 192 + SCREENHOLE; ?? Parce que la fonction MoveSprite retourne la position d’un sprite pour l’écran du bas, donc Y entre 0 et 191... MAis nous considérons les 2 écrans comme un seul, on doit donc la convertir en une position correcte... On ajoute donc 192 pour l’écran du haut, et SCREENHOLE pour l’espace entre les 2 écrans...
  • Ensuite on mémorise la vitesse du stylet, horizontalement et verticalement (respectivement vx et vy).

A la fin, il faut se rappeler de la structure PA_MovedSprite, qui garde les informations sur la position du sprite, en plus de sa vitesse de déplacement actuelle... Comme vous pouvez le voir, il y a plus de PA_MoveSprite que de simples déplacements de sprites.

Mais si le sprite n’avait pas été touché, on commence une boucle telle que la suivante :

else{
	// Maintenant la position fixe du frisbee sera changée par rapport à la vitesse
	frisbee.x += frisbee.vx;
	frisbee.y += frisbee.vy;

Ce code s’assure qu’on l’on déplace suivant la vitesse actuelle, rien de particulier à noter.

// Si on touche un bord, on inverse la vitesse
if ((frisbee.x -16 <= 0) && (frisbee.vx < 0)) frisbee.vx = -frisbee.vx; 
else if ((frisbee.x + 16 >= 256)&&(frisbee.vx > 0)) frisbee.vx = - frisbee.vx;
	
// La même chose pour les limites verticales
if ((frisbee.y -16 <= 0) && (frisbee.vy < 0)) frisbee.vy = -frisbee.vy;
else if ((frisbee.y + 16 >= 192 + 192 + SCREENHOLE)&& (frisbee.vy > 0)) frisbee.vy = - frisbee.vy

Voila une partie plus importante : elle vérifie que le frisbee ne sort pas de l’écran ! Si c’est le cas, elle change la vitesse du frisbee pour qu’il aille dans la bonne direction... Pour la position en Y, notez qu’on utilise la somme 192+192+SCREENHOLE, parce qu’elle équivaut à la hauteur totale de l’écran en prenant en compte l’espace entre les 2...

	PA_DualSetSpriteXY(FRISBEE, frisbee.x-16, frisbee.y-16);
}

Fin de la boucle du else : On place le sprite à la bonne position. Remarquez le préfixe Dual , pour utiliser les 2 écrans d’un coup. On met -16 parce que x et y sont la position du centre, et que l’on place les sprites par rapport au coin en haut à gauche.

La fin du code est assez triviale... D’abord on montre la vitesse sur l’écran du haut, juste pour vérifier. Ensuite on fait tourner le frisbee avec :

frisbee.angle += 4; // Fait tourner le frisbee
PA_DualSetRotsetNoZoom(0, frisbee.angle);

Il tourne à 4 degrés PAlib par frame, ce qui donne un assez bon rendu... j’ai utilisé DualSetRotset pour le faire tourner sur les 2 écrans...

Et c’est tout ! Je vous conseille de compiler et de tester ce code (il marche même sur Dualis r11, il y a juste un problème de couleurs), et vous verrez que c’est un exemple assez sympa.

Vous savez quoi ? Pour le prochaine exemple, je vais faire exactement la même chose, mais avec 10 frisbees :-P Vous verrez qu’il n’y a que 2-3 lignes à ajouter, en utilisant un tableau de structures...

Part 2 - 10 Frisbees

Comme je l’ai déjà dit, ca devrait être assez simple... Je vais mettre la plupart du code mais je ne reprendrai pas sur les choses que nous avons déjà vues.

typedef struct{
	s16 x, y; // This is for the frisbee's position
	s16 vx, vy; // Frisbee speed
	s16 angle; // To make the frisbee turn on itself
}frisinfos;
 
frisinfos frisbee[10];  // 10 Frisbees !!
 
 
int main(void)
{
 
// Initialise the lib...
PA_Init();
PA_InitVBL(); 
 
PA_InitText(1, 0);
 
// Load the palettes for the sprites on both screens
PA_DualLoadSpritePal(0, (void*)sprite0_Pal);
 
s32 i; // will be used in for loops to cycle through the frisbees...
 
PA_InitRand(); // Init the random stuff...
 
for (i = 0; i < 10; i++){
	// Sprite initial position...
	frisbee[i].x = (PA_Rand()%256)-16; // random position on the screen
	frisbee[i].y = 192+SCREENHOLE + (PA_Rand()%192)-16; // random position on the bottom screen; 
	
	// Speed of frisbee in both ways
	frisbee[i].vx = 0;
	frisbee[i].vy = 0;
	
	frisbee[i].angle = 0;	
	
	// Create the sprite on both screens...
	PA_DualCreateSprite(FRISBEE+i, (void*)frisbee_Sprite, OBJ_SIZE_32X32, 1, 0, frisbee[i].x-16, frisbee[i].y-16); 
	PA_DualSetSpriteRotEnable(FRISBEE+i, i); // Enable rotation/zoom, rotset 0
}
 
	while(1)
	{
		for (i = 0; i < 10; i++){
			// Move with the stylus, or move on...
			if (PA_MoveSprite(FRISBEE+i)){
				frisbee[i].x = PA_MovedSprite.X;
				frisbee[i].y = PA_MovedSprite.Y + 192 + SCREENHOLE;
				frisbee[i].vx = PA_MovedSprite.Vx;		frisbee[i].vy = PA_MovedSprite.Vy; 
			}
			else{
				// Now, the frisbee's fixed point position will be updated according to the speed...
				frisbee[i].x += frisbee[i].vx;
				frisbee[i].y += frisbee[i].vy;
			
				// If the sprite touches the left or right border, flip the horizontal speed
				if ((frisbee[i].x - 16 <= 0) && (frisbee[i].vx < 0)) frisbee[i].vx = -frisbee[i].vx; 
				else if ((frisbee[i].x + 16 >= 256)&&(frisbee[i].vx > 0)) frisbee[i].vx = - frisbee[i].vx;
		
				// Same thing, for top and bottom limits...
				if ((frisbee[i].y - 16 <= 0) && (frisbee[i].vy < 0)) frisbee[i].vy = -frisbee[i].vy;
				else if ((frisbee[i].y + 16 >= 192 + 192 + SCREENHOLE)&& (frisbee[i].vy > 0)) frisbee[i].vy = - frisbee[i].vy;		
				// The bottom limit is at the bottom of the bottom screen, so that would be 2 screen heights, plus the space in between...
				PA_DualSetSpriteXY(FRISBEE+i, frisbee[i].x-16, frisbee[i].y-16);
		
			}
			frisbee[i].angle+=4; // Make the frisbee turn...
			PA_DualSetRotsetNoZoom(i, frisbee[i].angle);
		}

Comme vous pouvez le voir, il y a quelques différences...

  • Tout d’abord : frisinfos frisbee[10]; /, c’est devenu un tableau !! Yahoo ! Sa taille est de 10, pour 10 frisbees...
  • Ensuite s32 i;, qui sera utilisé pour les boucles...
  • Et aussi PA_InitRand();, ca vous permettra au moins d’avoir vos frisbees à des positions différentes chaque fois que vous allumerez votre DS...

Le code d’initialisation des frisbees a changé un peu aussi :

for (i = 0; i < 10; i++){
	// Position initiale du sprite
	frisbee[i].x = (PA_Rand()%256)-16; //Une ordonnée aléatoire sur l'écran
	frisbee[i].y = 192+SCREENHOLE + (PA_Rand()%192)-16; // Une position aléatoire sur l'écran du bas
	
	// vitesses des frisbees dans les 2 sens
	frisbee[i].vx = 0;
	frisbee[i].vy = 0;
	
	frisbee[i].angle = 0;	
	
	// Créer le sprite sur les écrans
	PA_DualCreateSprite(FRISBEE+i, (void*)frisbee_Sprite, OBJ_SIZE_32X32, 1, 0, frisbee[i].x-16, frisbee[i].y-16); 
	PA_DualSetSpriteRotEnable(FRISBEE+i, i); // active la rotation/zoom, rotset 0
}
  • On génère une position aléatoire sur l’écran et on y place le sprite...
  • Remarquez FRISBEE+i au lieu du numéro de sprite ? On donne donc au 1er frisbee le numéro FRISBEE (10), et ensuite les numéros qui suivent : FRISBEE + 1 (11) etc... Sympa non ?
  • pareil pour les rotset, ils ont chacun un numéro (0-9)
  • au lieu de frisbee.x, on utilise frisbee[i].x, puisque c’est un tableau... On accède ainsi au frisbee (i). Vous verrez à peu près la même chose par la suite...

Enfin le dernier changement concerne la boucle infinie...

  • for (i = 0; i < 10; i++){qu’on a rajouté pour s’occuper de chaque sprite ! Hé oui c’est aussi simple que ça.
  • le reste est à peu près pareil, on remplace simplement frisbee par frisbee[i], pour s’occuper de chaque frisbee l’un après l’autre...

C’est tout ce qu’il yavait à savoir dans cette partie ! Qu’est-ce qu’on fait ensuite ? 10 frisbees avec des collisions !

Part 3 - 10 La collision des Frisbees

C’est encore une simple modification de l’exemple Frisbee2, mais en rajoutant un système de collision pour ne pas que les frisbees se passent les uns par dessus les autres. Il s’agit du même code avec quelques lignes en plus, je n’ai posté que les nouvelles lignes... J’ai laissé DualSetSpriteXY(...) pour que vous voyiez où placer le code.

	PA_DualSetSpriteXY(FRISBEE+i, frisbee[i].x-16, frisbee[i].y-16);
}
			
u8 j;
for (j = 0; j < i; j++){ // Test collisions for all frisbees with a smaller number...
	if (PA_Distance(frisbee[i].x, frisbee[i].y, frisbee[j].x, frisbee[j].y) < 32*32) {
		frisbee[i].vx = (frisbee[i].x - frisbee[j].x)/6;
		frisbee[i].vy = (frisbee[i].y - frisbee[j].y)/6;
		frisbee[j].vx = -frisbee[i].vx;
		frisbee[j].vy = -frisbee[i].vy;				
	}
}

Hé voilà, c’est tout ce dont on a besoin pour que les frisbees se cognent les uns dans les autres !

La première chose que l’on avait à faire était de vérifier que chaque frisbee n’en rencontrait pas un autre, grâce à cette boucle : for (j = 0; j < i; j++). Que remarquez vous de spécial dans cette boucle ? Si c’avait été comme la 1ère boucle, on aurait fait de 0 à 10, ici on fait de 0 à i, pourquoi ?

  • On est déjà dans une boucle for, celle qui déplace les frisbees, de 0 à 9 compris. Si on rajoute la même boucle à l’intérieur pour vérifier les collisions ded chaque frisbee, on se heurte à 2 problèmes :
    • On testerais chaque frisbee 2 fois : 0 avec 1, puis 1 avec 0, par exemple. C’est une perte de temps énorme.
    • Ca testerais la collision avec les mêmes nombres : 0 avec 0 par exmeple ! Et comme c’est le même frisbee il y aurait collision, et ca bugguerais !
  • Pour éviter ca, on ne teste les collisions qu’avec les frisbees qui ont déjà été déplacés. Ca ne parraît peut-être pas intuitif, voyons voir le comportement du programme :
    • Frisbee 0 : aucun test n’est fait (si j < i, alors que i vaut 0, la boucle s’arrête directement)
    • Frisbee 1 : ne teste qu’avec le frisbee 0 (le seul à être inférieur à 1)
    • Frisbee 2 : teste avec le 0 et le 1
    • Frisbee 3 : teste avec 0 , 1 et 2
    • etc...
  • Et ça fonctionne à merveille !

Ensute le test de collision, venant des tutoriels de collisions circulaires (dans le truc de Math Dev) : if (PA_Distance(frisbee[i].x, frisbee[i].y, frisbee[j].x, frisbee[j].y) < 32*32), vérifie la distance entre 2 frisbees... Si la distance trop petite entre les 2, il y a collision, et donc on modifie les vitesses des 2 frisbees en collision Pour le 1er :

frisbee[i].vx = (frisbee[i].x - frisbee[j].x)/6;
frisbee[i].vy = (frisbee[i].y - frisbee[j].y)/6;

On déduit la nouvelle vitesse de la différence de position du frisbee. Ainsi ils iront chacun d’un côté différent. J’ai divisé ca par 6 pour avoir une vitesse correcte, sinon c’était beaucoup trop rapide. Vous pouvez bien sûr changer le 6 pour avoir une vitesse différente lors de la collision.

frisbee[j].vx = -frisbee[i].vx;
frisbee[j].vy = -frisbee[i].vy;

Ce code donne la vitesse opposée à l’autre frisbee, rien de compliqué.

Et voilà, tout simple non ? Cependant il y a une faille dans ce système... Elle ne vient pas du code de detection de collision, mais du nouveau code de vitesse. En effet il ne prend pas en compte la vitesse à laquelle allait les frisbees qui se cognaient, ils repartent toujours à la même vitesse.

Si vous avez besoin d’une correction de vitesse plus précise, vous devriez regarder le prochai nexemple, il prend en compte la vitesse d’une manière plutôt efficace.

Taper le palet

Voila une nouvelle démo ! Celle ci est dérivée du code de collision circulaire, ainsi que de l’exemple des frisbees. Qu’est ce aue ça fait ? Vous avez un palet au milieu de l’écran (juste un cercle bleu, mais si quelqu’un veut changer les graphismes...), et vous avez votre ‘raquette’ (un autre cercle bleu). Quand vous tapez le palet avec la raquette, ca envoit le palet rebondir partout. Plus fort vous tapez, plus loin il ira.

Voila le code, vous verrez qu’il n’y a quasiment rien de nouveau. La plupart est juste du code recyclé.

typedef struct{
	s16 x, y; // position
	s16 vx, vy; // speed
}puckinfos;
 
puckinfos puck;
 
#define SCREENHOLE 48
 
int main(void){
 
	PA_Init();
	PA_InitVBL();
	
	PA_InitText(1,0); // On the top screen
 
	PA_DualLoadSpritePal(0, (void*)sprite0_Pal);
	
	// This'll be the movable sprite...
	PA_CreateSprite(0, 0,(void*)circle_Sprite, OBJ_SIZE_32X32,1, 0, 16, 16); 	
	s32 x = 16; s32 y = 16; // Sprite's center position
	
	// This will be the hit circle
	PA_DualCreateSprite(1,(void*)circle_Sprite, OBJ_SIZE_32X32,1, 0, 128-16, 96-16); 
	puck.x = 128; puck.y = 96+192+SCREENHOLE; // central position on bottom screen
	puck.vx = 0; puck.vy = 0; // No speed
	
	
	while(1)
	{
		if (PA_MoveSprite(0)){
			x = PA_MovedSprite.X;
			y = PA_MovedSprite.Y;
		}
		
		// Collision ?
		if (PA_Distance(x, y, puck.x, puck.y-192-SCREENHOLE) < 32*32) {
			// Collision, so we'l change the pucks speed to move it out of our 'raquette'
			u16 angle = PA_GetAngle(x, y, puck.x, puck.y-192-SCREENHOLE); // New direction angle
			u16 speed = (32*32-PA_Distance(x, y, puck.x, puck.y-192-SCREENHOLE))/32; // The closer they are, the harder the hit was...
			puck.vx = (PA_Cos(angle)*speed)>>8;
			puck.vy = -(PA_Sin(angle)*speed)>>8;
		}
		
		puck.x += puck.vx;
		puck.y += puck.vy;
	
		// If the sprite touches the left or right border, flip the horizontal speed
		if ((puck.x -16 <= 0) && (puck.vx < 0)) puck.vx = -puck.vx; 
		else if ((puck.x + 16 >= 256)&&(puck.vx > 0)) puck.vx = - puck.vx;
 
		// Same thing, for top and bottom limits...
		if ((puck.y -16 <= 0) && (puck.vy < 0)) puck.vy = -puck.vy;
		else if ((puck.y + 16 >= 192 + 192 + SCREENHOLE)&& (puck.vy > 0)) puck.vy = - puck.vy;		
		// The bottom limit is at the bottom of the bottom screen, so that would be 2 screen heights, plus the space in between...
		PA_DualSetSpriteXY(1, puck.x-16, puck.y-16);
		
		
 
		PA_WaitForVBL();
	}
	return 0;
}

Et les commentaires...

typedef struct{
	s16 x, y; // position
	s16 vx, vy; // speed
}puckinfos;
 
puckinfos puck;

Définition classique que nous avons utilisé encore et encore, pour stocker la position du palet et sa vitesse. Dans cet exemple, nous n’avons pas utilisé de point fixe, mais nous aurions pu.

Puis la palette est chargée, le premier sprite aussi, et ensuite

PA_DualCreateSprite(1,(void*)circle_Sprite, OBJ_SIZE_32X32,1, 0, 128-16, 96-16); 
puck.x = 128; puck.y = 96+192+SCREENHOLE; // central position on bottom screen
puck.vx = 0; puck.vy = 0; // No speed

Ceci charge le palet pour les deux écrans, et initialise sa position actuelle et sa vitesse.

Puis vient le classique

if (PA_MoveSprite(0)){
	x = PA_MovedSprite.X;
	y = PA_MovedSprite.Y;
}

juste pour stocker la position de la raquette (le point central).

La partie suivante est le seul code nouveau... C’est ce qui est utilisé pour taper le palet avec une vitesse différente suivant la manière dont vous le tapez, et l’envoie dans la bonne direction.

if (PA_Distance(x, y, puck.x, puck.y-192-SCREENHOLE) < 32*32) {
	// Collision, so we'l change the pucks speed to move it out of our 'raquette'
	u16 angle = PA_GetAngle(x, y, puck.x, puck.y-192-SCREENHOLE); // New direction angle
	u16 speed = (32*32-PA_Distance(x, y, puck.x, puck.y-192-SCREENHOLE))/32; // The closer they are, the harder the hit was...
	puck.vx = (PA_Cos(angle)*speed)>>8;
	puck.vy = -(PA_Sin(angle)*speed)>>8;
}
  • if (PA_Distance(x, y, puck.x, puck.y-192-SCREENHOLE) < 32*32) Teste s’il y a une collision, comme on l’a dit avant... 32 est la distance entre les 2 cercles (doit etre changé si vous changez la taille d’un des cercles), et 32×32 car on utilise la distance au carré, pour des questions de vitesse.
  • On a utilisé puck.y - 192 - SCREENHOLE car la coordonnée est pour deux écrans de hauteur pour le palet, alors que la raquette est seulement sur l’écran du bas. Il faut donc soustraire la taille de l’écran du haut (192) et de l’espace entre les écrans (SCREENHOLE)
  • u16 angle = PA_GetAngle(x, y, puck.x, puck.y-192-SCREENHOLE); est là pour récupérer l’angle formé entre le centre du palet et celui de la raquette. Pourquoi faire ? Vous pouvez le dessiner sur un papier, et vous verrez que cet angle est la direction dans laquelle le palet devrait partir !! Et oui ! Je n’ai pas le temps de faire un autre dessin pour ca, mais si quelqu’un en fait un, je l’ajouterais ici.
  • u16 speed = (32*32-PA_Distance(x, y, puck.x, puck.y-192-SCREENHOLE))/32; est la deuxième ligne très importante, car elle va déterminer la vitesse du palet ! Comment ca marche ? Plus vous êtes prêt du palet quand ca tape, plus vite vous bougiez... pourquoi ? Car si vous touchez le palet de seulement 1 pixel, ca signifie que vous bougiez vraiment très lentement. Mais si vous touchez le palet de quelque chose comme 16 pixels ? Ca signifie qu’avant que la frame soit mise a jour, vous avez eu le temps de bouger d’au moins 16 pixels, ce qui est 16 fois plus rapide que le coup à 1 pixel.
    • 32×32 est le carré de la distance nécessaire pour taper. Si vous avez eu une collision de seulement 0 pixels, vous n’avez pas tapé le palet, vu que votre vitesse n’était pas assez élevée.
    • Ensuite la distance (PA_Distance(x, y, puck.x, puck.y-192-SCREENHOLE)) est recalculée, et soustraite à la distance au carré. Ceci donnera une vitesse plus élevée quand vous bougez vite.
    • Et enfin, cette vitesse est divisée par 32... Pourquoi ? Parce que c’était juste par essai et erreur lol... Vous pouvez changer 32 pour avoir des vitesses plus ou moins élevées, et ainsi l’adapter à vos besoins.
  • puck.vx = (PA_Cos(angle)*speed)»8; détermine la vitesse horizontale (vx) du palet, avec le nouveau coup. Cette vitesse est trouvée en utilisant le Cos de l’angle et la vitesse globale qu’il y aura...C’est comme e tutorial sur la trajectoire, mais avec la vitesse en plus...
    • Mais dans ce cas, pourquoi y a t’il un »8 ??? Parce que, comme on l’a dit au début, je n’ai pas utilisé les mathématiques en point fixe dans cet exemple. Et PA_Cos retourne un nombre en point fixe, donc j’ai du le convertir en un nombre normal.
    • Alors pourquoi le »8 est t’il après la vitesse, et pas juste après le Cos ?? Car s’il était après le PA_Cos, vu que c’est un entier, le résultat serait toujours 0 ! Quand il est fait après la multiplication avec la vitesse, la valeur totale avant la division est supérieure à 256, donc diviser par 256 (ce qui est la même chose que »8) ne retournera pas toujours 0, mais plutot la vitesse correcte en pixels, arrondie à la valeur inférieure (une vraie vitesse de 2.5 vous donnera 2, etc...).
  • La même chose s’applique pour la vitesse verticale, avec un signe négatif car c’est un Sin (and Sin is bad, negative, lol... you must not Sin ! ;-)) (NDT : petite blague intraduisible, et peut etre aussi moyen mnémotechnique, Sin = pêché, donc pas bien, négatif...).

Et voila ! Je ne commenterais pas le reste du code, vu que c’est juste un copier/coller du code du frisbee (j’ai juste remplacé ‘frisbee’ par ‘palet’ (NDT: ‘puck’ en anglais)).

J’espère que c’était assez clair ! Enjoy :-)

Prochain → Jour 13 - Démarrer un jeu de plate-forme

 
day12.txt · Dernière modification: 28/10/2007 17:43 par 82.244.105.170
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki