Événements liés
  • Démo technique du samedi #6: 30 Juin 2007
Pages: [1] 2   Bas de page
Imprimer
Auteur Fil de discussion: [Demo] Des listes d'objets qui vous suivent  (Lu 10332 fois)
0 Membres et 1 Invité sur ce fil de discussion.
Mollusk Hors ligne
PAlib Guru et
Administrateur
*****
Messages: 3480


Voir le profil WWW
Ne vous posez pas de questions, codez !

« 01 Juillet 2007, 01:59:13 »

Il est venu le temps de faire une nouvelle démo. Smiley Cette semaine, on va aborder 2 points importants. Il existe plusieurs façons de réaliser chacun d'eux, et donc on peut tout à fait contester mon code moche et peu optimisé, mais c'est juste pour montrer une manière de faire. Smiley Les 2 éléments dont on va parler :
  • Gérer une liste d'objets en en retirant/rajoutant un peu tout le temps
  • Gérer la trajectoire de type 'tête chercheuse' en cherchant à utiliser le moins de temps CPU possible

Voici donc le résultat de la démo :

Le code source fait une centaine de lignes, et utilise des sprites normaux avec des rotsets pour les rotations. Rien de bien exceptionnel, rien qui ne nécessite une explication. Azn
Bon, il faut bien commencer quelque part, on va voir en premier la gestion des objets. Smiley

Code
(c):
typedef struct{
  s32 fx, fy, x, y, vitesse;
  s16 angle;
  s8 chgangle;
} tir_type;
 
tir_type tirs[100]; // 100 max
u8 ntir[100];
u8 nlibre[100];
u8 ntirs;
Voici pour la déclaration. En gros, pour chaque tir, on a une position en fixed point et en normal (pour ceux qui auraient envie de jouer avec des collisions sans se prendre la tête avec les fixed points), la vitesse, l'angle (direction, de 0 à 511 pour des questions de simplicité et de puissance de 2 Azn), et chgangle, qui est de combien l'angle peut changer par frame.
La variable ntirs correspond au nombre de tirs actifs. On a ensuite 3 tableaux :
  • tir_type tirs[100]; pour les infos des tirs
  • u8 ntir[100]; pour les tirs en cours. De ntir[0] à ntir[ntirs] on trouve les tirs activés avec le numéro correspondant dans le tableau tirs
  • u8 nlibre[100]; pour les slots dispos. Même principe que ntir, mais l'inverse, ici on a les slots non utilisés. Smiley

Donc, le principe est tout con. Au début on remplit le tableau des tirs dispos avec les nombres 0 à 99 (simple boucle for), puis quand on a besoin on pioche le dernier nombre de la liste, et on le retire de celle-ci. Smiley On l'ajoute alors à la liste des tirs, et on ajoute 1 au nombre de tirs :
Code
(c):
void AjouterTir(void){
 
  if(ntirs == 100) return; // On ne fait rien
  u8 n = nlibre[ntirs]; // Récupérer un slot disponible
  ntir[ntirs] = n;
 
  // Position aléatoire pour le nouveau tir...
  tirs[n].x = 8+(PA_Rand()%240);
  tirs[n].y = 8+(PA_Rand()%176);
  tirs[n].angle = PA_Rand()&511; // Direction au hasard
  tirs[n].vitesse = 2+(PA_Rand()&3); // Vitesse aléatoire de 2 à 5
  tirs[n].chgangle = 2+(PA_Rand()&7); // Vitesse de rotation aléatoire de 2 à 9/VBL
 
  tirs[n].fx = tirs[n].x<<8; tirs[n].fy = tirs[n].y<<8; // Fixed point...
  ntirs++;
}
Le reste du code est sans importance, c'est juste de la position/vitesse aléatoire. Smiley

Reste maintenant à gérer la destruction d'un tir... La boucle qui permet de faire bouger tous les tirs est presque toute simple :
Code
(c):
for(i = 0; i < ntirs; i++) i+= DeplacerTir(i);
Alors, pourquoi ce i += au lieu de juste exécuter la fonction ? En fait, le problème se pose quand on retire un tir : si on a 10 tirs, qu'on en retire un au milieu, on va avoir ntirs = 9, et donc la boucle va s'arrêter avant le dernier ! Pour pallier ce problème, j'ai choisi de faire mettre le dernier tir du tableau à la place du tir que l'on retire, et donc il faut "réexécuter" la fonction DeplacerTir pour le numéro que l'on vient de faire... Ainsi, dans le cas d'un retrait de tir, en fait la fonction renvoie -1. On passe de 5 à 4, par exemple, et comme on augmente de 1 à la boucle suivante, ça repasse à 5 et on refait le tir numéro 5. Smiley

Voici le code en question :
Code
(c):
if(PA_Distance(Stylus.X, Stylus.Y+192, tirs[n].x, tirs[n].y) < 8*8){ // Si touche, on le retire !
ntirs--;
  nlibre[ntirs] = n; // On remet dans le pool...
 
  ntir[i] = ntir[ntirs]; // On met à la place le dernier...
  PA_SetSpriteXY(0, n+1, 256, 192); // Cacher...
  PA_SetSpriteXY(1, n+1, 256, 192); // Cacher...
 
  return -1; // Refaire ce numéro
}
Rien de particulier donc, il suffit de déplacer le numéro du tir dans le tableau. Smiley


Maintenant qu'on a vu tout ça, il faut voir comment gérer l'aspect tête chercheuse... J'ai déjà montré la fonction qui initialise un tir, en gros tout est mis au pif (la position, la vitesse de déplacement, et la vitesse de rotation)... J'utilise une technique que j'ai incorporée à PAlib pour diriger le tir, mais je vais tout vous expliquer. Wink

Code
(c):
tirs[n].angle = PA_AdjustAngle(tirs[n].angle, tirs[n].chgangle, tirs[n].x, tirs[n].y, Stylus.X, Stylus.Y+192);
En fait, on donne à la fonction l'angle actuel, la vitesse de rotation, la position actuelle, et la cible, et l'angle sera ajusté de la vitesse de rotation dans un sens ou dans l'autre si besoin.
Le problème principal avec les angles, c'est qu'un angle fait intervenir des calculs relativement complexes pour le déterminer, ce qui est assez gênant dans des jeux avec plusieurs choses à tête chercheuse... Il est donc préférable d'avoir un algorithme plus optimisé, qui permettrait de ne pas avoir à recalculer l'angle systématiquement...

Code
(c):
extern inline u64 PA_Distance(s32 x1, s32 y1, s32 x2, s32 y2) {
  s64 h = x1 - x2;
  s64 v = y1 - y2;
  return(h*h + v*v);
}
 
u16 PA_AdjustAngle(u16 angle, s16 anglerot, s32 startx, s32 starty, s32 targetx, s32 targety) {
u64 distances[3];
 
startx = startx << 8; // Fixed point...
starty = starty << 8; // Fixed point...
targetx = targetx << 8; // Fixed point...
targety = targety << 8; // Fixed point...
 
     u16 tempangle = (angle - anglerot) & 511;
 
     // Calcul des distances en fonction des angles
     distances[0] = PA_Distance(startx + PA_Cos(tempangle), starty - PA_Sin(tempangle), targetx, targety);
     tempangle += anglerot;
 tempangle &= 511;
     distances[1] = PA_Distance(startx + PA_Cos(tempangle), starty - PA_Sin(tempangle), targetx, targety);
     tempangle += anglerot;
 tempangle &= 511;
     distances[2] = PA_Distance(startx + PA_Cos(tempangle), starty - PA_Sin(tempangle), targetx, targety);
 
     // On regarde si l'angle est optimal. Si ce n'est pas le cas,
     // on fait tourner toujours dans le même sens...
     if (distances[0] < distances[1])  angle -= anglerot;
     else if (distances[2] < distances[1])  angle += anglerot;
 
     return (angle&511);    
}

Premièrement, la fonction PA_Distance. Elle renvoie la distance... AU CARRÉ ! Pourquoi ? Parce qu'on ne va pas avoir besoin d'avoir la distance exacte pour faire marcher le tout, et si on peut éviter d'utiliser une fonction racine carrée un peu couteuse en temps CPU, ce n'est pas plus mal. Wink

Ensuite, la fonction PA_AjustAngle à proprement parler... Le principe sous-jacent est relativement simple. Le missile que l'on dirige va avancer dans la direction actuelle. Il peut tourner dans un sens ou dans l'autre, et on voudrait savoir dans quel sens aller... Pour déterminer ce dernier, il suffit en fait de savoir quelle direction lui permettra d'être le plus proche de la cible en cas de mouvement Cheesy  Il suffit donc de faire comme si on déplaçait le missile à une vitesse petite (pour ne pas dépasser le point cible Azn) dans 3 directions différentes :
  • Angle de base
  • Angle de base - rotation
  • Angle de base + rotation
On compare alors la distance entre ces 3 points et la cible, le point le plus proche est celui qui nous intéresse Langue C'est dans cette direction que le missile doit tourner. Cheesy

Cette méthode est particulièrement rapide et adaptée pour ajuster un angle, mais présente 2 défauts principaux dont il faut avoir conscience :
  • Si on est dans la direction parfaitement opposée à la direction optimale, que l'on tourne à droite ou à gauche, on aura la même distance, et donc ça risque de foirer. Azn
  • Si on définit un trop grand angle de rotation et qu'il faut tourner de quelques degrés à peine, ça n'ira pas non plus, car ça fonctionne sur un mode 'tout ou rien', sans intermédiaire... Pallier à ce problème est très simple, il suffit de faire exécuter 2 fois avec une vitesse de rotation 2 fois plus petite (ou 3, ou 4, etc.).

Voilà pour les infos. Cheesy Je pense que vous avez toutes les clés en main pour comprendre ce code relativement bête, mais pas si méchant. J'espère que ça pourra servir à quelqu'un. Azn

Bon Week-end à tous et à bientôt. Si vous avez des idées particulières de démos à faire, on est toujours preneurs. Wink
Journalisée

shell64 Hors ligne
Newbie
*
Messages: 4


Voir le profil
« Réponse #1 : 01 Juillet 2007, 04:59:57 »

très utile pour les programmeurs en herbe Azn
Journalisée
Pitt Hors ligne
Administrateur
*****
Messages: 575


Voir le profil WWW
« Réponse #2 : 02 Juillet 2007, 08:44:39 »

Ca donne un peu la gerbe mais c'est sympa Tongue
Quand au côté codage, pas grand chose de complexe mais ça peut être utile dans tout plein de choses Smiley
Journalisée
Toiletking Hors ligne
Mega Member
***
Messages: 1289


Voir le profil WWW
"Caca Boudin!"

« Réponse #3 : 25 Septembre 2007, 23:56:33 »

En quelques lignes, tu expliques un effet qui a fait galérer le développeur d'Uridium 2 sur Amiga ! Notamment pour la gestion des missiles à têtes chercheuses !

Si si, je me souviens d'une interview du programmeur dans un vieux joystick de l'époque. Azn

Op un petit screen pour les nostalgiques : Uridium 2
« Dernière édition: 26 Septembre 2007, 01:24:23 par Yus » Journalisée
Mollusk Hors ligne
PAlib Guru et
Administrateur
*****
Messages: 3480


Voir le profil WWW
Ne vous posez pas de questions, codez !

« Réponse #4 : 26 Septembre 2007, 08:01:34 »

En même temps, quand tu vois ça :
Citation de: Wikipedia
Les Amiga 1000, 500, 2000, 1500, 500+ et 600 étaient tous basés sur un microprocesseur Motorola 68000 à un peu plus de 7 Mhz.
Pas étonnant qu'ils aient eu plus de mal Wink

Et sinon, hors sujet total, mais en regardant le-dit Motorola 68000 sur Wikipedia... Documentation de fou, je trouve ça impressionnant de trouver autant d'informations sur une 'simple' encyclopédie en ligne Azn Tu as de quoi faire des émulateurs là je pense !
Journalisée

Toiletking Hors ligne
Mega Member
***
Messages: 1289


Voir le profil WWW
"Caca Boudin!"

« Réponse #5 : 26 Septembre 2007, 09:58:29 »

Il est ou Redbug? Wink
Journalisée
Pitt Hors ligne
Administrateur
*****
Messages: 575


Voir le profil WWW
« Réponse #6 : 26 Septembre 2007, 18:23:49 »

Qui fout gbatek sur wikipedia ? Langue
Journalisée
Toiletking Hors ligne
Mega Member
***
Messages: 1289


Voir le profil WWW
"Caca Boudin!"

« Réponse #7 : 26 Septembre 2007, 22:53:35 »

Citation
En même temps, quand tu vois ça :
Citation de: Wikipedia
Les Amiga 1000, 500, 2000, 1500, 500+ et 600 étaient tous basés sur un microprocesseur Motorola 68000 à un peu plus de 7 Mhz.
Pas étonnant qu'ils aient eu plus de mal Wink

Bah, l'amiga était super bien épaulé par paula (sans jeux de mot Azn ), non sérieux, en gestion de sprite, elle en avait pas moins dans le sac qu'une mégadrive, ou une super nintendo... et pis en tâtant du croco ds, j'ai trouvé un jeux qui exploite carrément cette technique sur amstrad cpc : gauntlet!

op la preuve en image :

http://www.cpcgamereviews.com/g/gauntlet_2.png

Si vous regardez bien, il y a des fantômes :  et bien pour le fun, lancer croco ds, avec le dsk de gauntlet (http://www.phenixinformatique.com/CPCGAMES/index.php?page=detail&num=960 sur le site de kukulcan : très très bien ce site  Azn) et allez au niveau 3 : c'est blindé de fantômes qui vous suivent, peut-être 50!

CPC POWA!  Cheesy

ps : bah il manque juste les rotsets... (ca pompe un peu ca, non?  Roll Eyes )
« Dernière édition: 27 Septembre 2007, 00:47:15 par Toiletking » Journalisée
Toiletking Hors ligne
Mega Member
***
Messages: 1289


Voir le profil WWW
"Caca Boudin!"

« Réponse #8 : 16 Juillet 2008, 01:23:19 »

ehhhh mais il est trop bien ce tuto!!!  Shocked

Bon c'est un déterage de topic un peu violant, mais ca répond a plein de question que je me pose!!!  Cheesy
Journalisée
Toiletking Hors ligne
Mega Member
***
Messages: 1289


Voir le profil WWW
"Caca Boudin!"

« Réponse #9 : 17 Juillet 2008, 00:25:25 »

Bonsoir tout le monde!

Yop! Petit up! En fait j'ai voulu reprendre ce code super performant, car il présente un avantage certain : c'est super plus optimisé que les bouts de codes que j'ai pu trouver, et c'est le système de supression de shoot qui me semble indispensable, d'autant plus qu'il n'y aucun risque de saturer la mémoire de la sorte (c'est le mon premier problème, je créais des sprites à chaque tire avant...  whistle )

J'ai donc trafficoté le code de mollusk pour que les tires partent du vaisseau et qu'ils ayent dans l'angle posé par le stylet autour du vaisseau...

Simplement, en suivant la logique de conception de la fonction "ajouter un tire", qui est vraiment différente de celle que j'ai repompé dans jetfighter (cf : http://www.dev-fr.org/index.php/topic,1947.0.html => merci encore Cid  Wink ) je n'arrive pas a faire en sorte que chaque tire, aye a sa propre direction... (éventuellement que chaque tir ai son propre rotset, dans la limite des rotset disponible pour une arme de type missile tete chercheuse  Smiley le lance roquette ne pourra pas pas tiré plus de 32 roquettes a la fois donc Azn )

j'en suis là dans la gestion des tires :

Code
(c):
 
 
// Includes
#include <PA9.h>       // Include for PA_Lib
 
#include "gfx/all_gfx.c"
 
 
s16 angle = 0;
 
// struct du joueur
typedef struct
{
   s16 posx, posy;
   s16 speedy;
   s16 angle;// angle du vaisseau : angle du tir
}info;
 
info Player1;
 
 
 
 
 
// struct du shoot
typedef struct
{
   s32 fx, fy, x, y, vitesse;
   s16 angle;
   s8 chgangle;
   s16 rotset;
} tir_type;
 
tir_type tirs[100]; // 100 max
u8 ntir[100];
u8 nlibre[100];
u8 ntirs;
 
 
u16 gfx[2];
 
 
 
s8 DeplacerTir(u8 i)
{
 
 u8 n = ntir[i];
 
   // Ajust l'angle...
   tirs[n].angle = angle ;
 
   tirs[n].fx += (PA_Cos(tirs[n].angle)*tirs[n].vitesse);
   tirs[n].fy -= (PA_Sin(tirs[n].angle)*tirs[n].vitesse);
 
   // Positions normales
   tirs[n].x = tirs[n].fx>>8;
   tirs[n].y = tirs[n].fy>>8;
 
//    if (PA_Distance(Stylus.X, Stylus.Y+192, tirs[n].x, tirs[n].y) < 8*8)  
   if (tirs[n].x>255||tirs[n].x<0) //Si touche, on le retire !
{
       ntirs--;
       nlibre[ntirs] = n; // On remet dans le pool...
 
       ntir[i] = ntir[ntirs]; // On met à la place le dernier...
       PA_SetSpriteXY(0, n+1, 256, 192); // Cacher...
       PA_SetSpriteXY(0, n+1, 256, 192); // Cacher...
 
       return -1; // Refaire ce numéro
   }
   else
   {
       PA_SetSpriteXY(0,n+1, tirs[n].x-8, tirs[n].y-8); // Afficher...
       PA_SetSpriteRotEnable(0,n+1, (((tirs[n].angle+8)&511))/16); // Mettre un des 3 rotsets pour le bon zoom
   }
 
   return 0;
}
 
 
void AjouterTir(void)
{
 
 
 
   if (Pad.Newpress.L)
   {
     u8 n = nlibre[ntirs]; // Récupérer un slot disponible
     ntir[ntirs] = n;
 
 
for (n =0; n< 100; n++)
{
       if (ntirs == 100) return; // On ne fait rien
 
       // Position  pour le nouveau tir...
       tirs[n].x = 8+ Player1.posx;
       tirs[n].y = 8+ Player1.posy;
       tirs[n].angle = angle; // Direction au hasard
       tirs[n].vitesse = 2; // Vitesse aléatoire de 2 à 5
       tirs[n].chgangle = 2+(PA_Rand()&7); // Vitesse de rotation aléatoire de 2 à 9/VBL
 
       tirs[n].fx = tirs[n].x<<8;
       tirs[n].fy = tirs[n].y<<8; // Fixed point...
       ntirs++;
}  
}
}
 
 
// Function: main()
   int main(int argc, char ** argv)
   {
       PA_Init();    // Initializes PA_Lib
       PA_InitVBL(); // Initializes a standard VBL
 
 
 
       s16 j, i;
 
       PA_LoadSpritePal(0, 0, (void*)ship_Pal);
       PA_LoadSpritePal(0,1, (void*)tir_Pal);
 
       PA_CreateSprite(0, 0, (void*)ship_Sprite, OBJ_SIZE_16X32, 1, 0, 128-8, 96-16);
 
 
           gfx[1] = PA_CreateGfx(0, (void*)tir_Sprite, OBJ_SIZE_16X16, 1); // Creer l'image en vram
 
           // Créer les sprites à l'avance...
           for (j = 0; j < 100; j++)
           {
               PA_CreateSpriteFromGfx(0, j+1, gfx[1], OBJ_SIZE_16X16, 1, 1, 256, 192); // Dehors...
               PA_SetSpriteRotEnable(0, j+1, 0); // Rotsets
           }
 
           // Préparer les rotsets
       for (j = 0; j < 32; j++) PA_SetRotsetNoZoom(0, j, j*16);
 
       for (j = 0; j < 100; j++) nlibre[j] = j; // Emplacments dispos = 0-99
 
 
       ntirs = 0;
       PA_InitText(1, 0);
 
 
 
 
 
 
 
// Infinite loop to keep the program running
 
   while (1)
   {
 
      if (Stylus.Held)
       {
           angle = PA_GetAngle(Player1.posx, Player1.posy, Stylus.X, Stylus.Y);
       }
 
 
 
 
       AjouterTir(); // Ajouter 1 tir à chaque VBL ^^
 
       for (i = 0; i < ntirs; i++) i+= DeplacerTir(i);
 
 
       // déplacement du jet
       Player1.posx +=Pad.Held.Right - Pad.Held.Left;
       Player1.posy +=Pad.Held.Down - Pad.Held.Up;
       PA_SetSpriteXY(0,0, Player1.posx, Player1.posy);
 
 
       PA_WaitForVBL();
   }
 
   return 0;
} // End of main()
 


Parmis les modifs que j'ai faite par rapport a la démo de mollusk :
- j'ai voulu distingué le vaisseau des tires = j'ai donc fait un structure à par entière  Smiley
- Pour mon projet j'ai mis les sprites dans un seul écran et le vaisseau est bougé au pad
- J'ai remplacé la condition de suppréssion de sprite qui correspondaient au coordonnée du stylet (la ou est le vaisseau) par les limites de l'écran (en attendant de rajoutter la gestion de collision au murs Azn)

Voilà, je suis donc ouvert à toute proposition pour que chaque tire face ca vie de son coté Azn
« Dernière édition: 17 Juillet 2008, 09:16:19 par Toiletking » Journalisée
Mollusk Hors ligne
PAlib Guru et
Administrateur
*****
Messages: 3480


Voir le profil WWW
Ne vous posez pas de questions, codez !

« Réponse #10 : 17 Juillet 2008, 07:20:35 »

Je comprends pas trop ton 'for (n =0; n< 100; n++)' sachant que tu veux ajouter un seul tir...
Journalisée

Toiletking Hors ligne
Mega Member
***
Messages: 1289


Voir le profil WWW
"Caca Boudin!"

« Réponse #11 : 17 Juillet 2008, 09:25:07 »

Je comprends pas trop ton 'for (n =0; n< 100; n++)' sachant que tu veux ajouter un seul tir...

Coucou Mollusk!
Eh bien ca c'est un des tests que j'ai fait (et qui ne marche pas Azn) et que j'ai oublié de virer avant de poster... whistle

Ben en fait, je voulais que chaque tire aient ses propres caractèristique : ce ne sont plus des tires a têtes chercheuses que je voulais faire mais des tirs prenant la direction de l'angle du stylet par rapport au vaisseau...

Dans l'état actuel du code, les tires partent bien en direction de l'angle, mais il suffit que je change l'angle et tout les tires se suivent et change d'angle... Smiley je voudrais donc que chaque tire ayent tout droit dans l'angle données, qu'il aient chacun leur vie quoi Azn

Un moment, j'ai même fait des manips avec  "for (i = 0; i < ntirs; i++) i+= DeplacerTir(i); " mais ca na rien donné du tout...

Edit : Dites le moi hein si je suis pas claire Azn
« Dernière édition: 17 Juillet 2008, 15:07:31 par Toiletking » Journalisée
Copper Hors ligne
Mega Member
***
Messages: 1232


Voir le profil
« Réponse #12 : 17 Juillet 2008, 15:15:52 »

A mon avis tu n'es pas Claire... ben oui c'est un prénom de fille après tout  police
Journalisée
Pouer Hors ligne
Hero Member
*****
Messages: 531


Voir le profil
Petit 1/2 en puissance :)

« Réponse #13 : 17 Juillet 2008, 15:19:34 »

Merci copper d'expliquer ta blague j'avais pas compris jusqu'au trois petits points Cheesy
Journalisée

Toiletking Hors ligne
Mega Member
***
Messages: 1289


Voir le profil WWW
"Caca Boudin!"

« Réponse #14 : 17 Juillet 2008, 15:43:59 »

arf!  Grin

Je vous envoie la rom des modifs cité si dessus (j'ai enlevé le test foireu que momo a remarqué). Donc, pour déplacer le jet, on utilise le pad, et le stylet sert a orrienter le shoot. Pour tirer, il suffit d'appuyer sur L (alt gauche sur no@gba).

je voudrai juste que chaque tire fasse sa vie, et continu dans une seul direction!  Grin 

Edit : je vais quand même garder ce bout de code : il est super pour faire des missiles guidés !  Cheesy(imaginé le gameplay à la metal gear  Cheesy )

* FollowMe.ds.rar (36.52 Ko - Téléchargé 128 fois.)
Journalisée
Pages: [1] 2   Haut de page
Imprimer

Aller à: