PpHd :Heu... bof j'ai rien pigé parce que non seulement je ne connais pas un mot à l'assembleur mais en plus ton prog est bien trop gros pour que je puisse m'y retrouver...
Ils l'ont deja fait.
Brunni :
Bon, quand je dis RLE, je ne sais pas si c'est vraiment ça... mais bon personnellement c'est très simple; je travaille par ligne, et j'ai un index (dire l'octet n° x = bloc compressé n° y) et une petite sauvegarde des variables importantes de la décompression. Après, je compare l'index de chaque ligne et le nouvel index (à cette frame) et je parcours la map dans un sens ou dans l'autre jusqu'à ce que les deux index soient égaux (en me servant des variables internes sauvées). Ensuite, je peux poursuivre la décompression sur la ligne courante sans problème.
Donc en tombant de très haut (et de très loin vers la droite), on peut demander beaucoup de travail, car si certaines lignes qui sont découvertes pour la première fois (leur index est encore 0), il faut que je les parcoure jusqu'à un index relativement élevé avant de commencer à les décompresser puis les afficher.
BrunniToi t'as pas regardé mes screenshots
: Sinon tu as déjà un moteur de gestion des objets?
Et c'est assez rapide tout ça?Oui. Tout compris ça donne 4% d'une frame (à 60 frames/secondes donc) sur GBA, pour décompresser 23x18 blocs 16 bits sur une map de 1024x512. Comme je te l'ai dit, il y aura des ralentissements dans certains cas, mais même s'ils peuvent être importants, ça passe inaperçu (une seule frame ratée ne se remarque même pas). Bon, comme il y a deux plans, total: 8%. Ca reste vivable, et surtout mon code C largement optimisable.
Toi t'as pas regardé mes screenshotsSans vouloir la jouer boulet, je peux les trouver où?
Perso je ne gère pas tous les objets de la map en même temps, je traite seulement ceux qui sont dans la région de l'écran que j'ai appelée le "scope": c'est une zone centrée sur l'écran mais plus large de quelques tiles de chaque côté. En fait c'est surtout pour cette gestion des objets que le cpp est très utile...Je suis d'accord avec toi sur le principe, en revanche pas pour ce qu'il s'agit de l'utilité du C++. Perso j'utilise des structures, et je suis plus que satisfait, en plus d'être sûr que le code est rapide (puisqu'il y en a besoin
Le point positif est que je n'ai donc que peu d'objets à gérer simultanément, par contre le fait que leur nombre (et leur taille) soit tres variable a de grosses influences sur le framerate (notamment lorsque sonic est blessé et qu'il faut faire voler des anneaux dans tous les sens); c'est pour ça que les timers me sont très précieux, puisque grâce à eux on ne décèle aucun ralentissement dans le jeu même si j'ai tout à coup 3 fois plus d'objets à gérerEn effet, mais bon perso j'ai du abandonner. J'en ai marre de passer tant de temps à l'optimisation. S'il pourrait éventuellement y avoir peut-être parfois des ralentissements à certains moments, ça ne me démonte pas, sinon je n'en aurai jamais fini
Un autre point négatif à ma méthode est que les objets sont incapables d'interagir entre eux... mais je n'ai pas encore trouvé de cas où cela serait nécessaire.D'ailleurs je crois bien que les jeux originaux non plus ne le font pas. La vitesse de leur moteur est déjà assez impressionnante comme ça, ajouter ce genre de détection serait du suicide.
switch (objets[i].type) { case ENNEMI_ROULANT: [...] break; }Qui est très lent, mon handler actuel ressemble alors à ça.
void IN_IWRAM GerePersos(SONIC *sonic, ECRAN *ecr) {
int i, x,y;
unsigned short dehors_ecran;
unsigned int msg;
//Prend beaucoup de temps car gère des persos inutiles.
//Optimisable, en ne gérant que le nombre d'objets actifs.
for (i=0;i<LIMITE_PERSOS;i++) {
if (!persos[i].type)
continue;
x=persos[i].x-ecr->scrlX;
y=persos[i].y-ecr->scrlY;
if (x<-64 || y<-64 || x>=240 || y>=160)
dehors_ecran=TRUE;
else
dehors_ecran=FALSE;
if (dehors_ecran && persos[i].instructions&INSTR_SEULEMENT_ECRAN) {
persos[i].nObj=0;
continue;
}
msg=0;
if (!dehors_ecran) {
if (persos[i].instructions&INSTR_COLLISION_DECOR) { //Requiert un moteur de collisions standard
Exec(MoteurCollisionObjets,&persos[i],0,2);
}
if (persos[i].instructions&INSTR_COLLISION_SONIC) { //Requiert une détection de collision avec Sonic
if (CollisionEntreDeuxSprites(sonic,&persos[i]))
msg|=MSG_COLLISION_SONIC; //Enverra un message signalant la collision
}
msg|=MSG_AFFICHER;
}
persos[i].fHandler(sonic,&persos[i],msg); //Exécute le handler qui retournera le numéro d'un nouveau sprite
if (dehors_ecran)
persos[i].nObj=0;
else
persos[i].nObj=objetsUtilises;
}
}
notamment lorsque sonic est blessé et qu'il faut faire voler des anneaux dans tous les sens); c'est pour ça que les timers me sont très précieux, puisque grâce à eux on ne décèle aucun ralentissement dans le jeu même si j'ai tout à coup 3 fois plus d'objets à gérerPas si tu codes comme moi!
Un autre truc: bien que tu utilises des timers, la gestion des objets reste vitale hm? Enfin si tes bagues volent (tiens pas encore implémenté ça moi), tu dois continuer de les gérer, hors écran ou non, sinon elles vont être dépendantes du framerate... donc ton moteur d'objets (hors affichage) se doit d'être optimisé, timer ou non (ou bien j'ai rien pigé?).Quel que soit l'objet, les mouvements sont basés sur le temps et pas sur le framerate... Je n'ai donc pas besoin de savoir si les anneaux sont visibles ou non ou si leur nombre a changé etc
/* GESTION DES LISTES D'OBJETS */
// gestion des 3 plans d'objets "derriere" le perso
void ObjGroup::gereArrierePlan()
{
GestionListe(m_listes[0]);
GestionListe(m_listes[1]);
GestionListe(m_listes[2]);
}
// gestion d'un des 4 plans d'objets
void ObjGroup::GestionListe(Objet* &liste)
{
if(liste==NULL) return;
bool reste = liste->estGere(); // appelle le handler de l'objet
GestionListe(liste->next); // passe au suivant
if(!reste) // suppression si nécessaire
{
Objet* vict = liste;
liste = liste->next;
delete vict;
}
}
// ajoute un objet supplementaire a gerer
void ObjGroup::add(Objet* nouv)
{
nouv->next = m_listes[ nouv->m_liste ];
m_listes[ nouv->m_liste ] = nouv;
}
/* GESTION DES RINGS SIMPLES */
struct Ring : public Objet
{
Ring(int X,char Y,uchar Type,Tile tile);
bool estGere();
void apparait();
bool interagitAvecPerso();
static Timer m_timerRotation; // timer commun à tous les rings
Timer m_timerDisparition;
bool m_estAttrape;
};
Ring::Ring(int X,char Y,uchar Type,Tile tile) : Objet(X,Y,Type,tile,1,POS_SPR_RING)
, m_estAttrape(false)
, m_timerDisparition(TIMER_OFF)
{}
bool Ring::estGere()
{
bool peutRester = true;
bouge();
if(m_sortDuScope)
{
if(m_estAttrape==false) incrusteDansMap();
peutRester=false;
}
else
{
if(m_estAttrape==false) interagitAvecPerso();
else if(m_timerDisparition>48) peutRester=false;
apparait();
}
return peutRester;
}
void Ring::apparait()
{
if(m_estAttrape) draw(4+((m_timerDisparition/10)&1));
else draw(m_timerRotation/10);
}
bool Ring::interagitAvecPerso()
{
setNewPos(m_timerRotation/10);
if(persoEnContact())
{
lePerso->ajouteRings();
m_estAttrape=true;
m_timerDisparition.start(); // on commence a compter le temps qui s'écoule
}
return true;
}
void Objet::bouge() // on utilise la version par defaut: objet attache a la map
{
m_x+=laMap.m_depH;
m_y+=laMap.m_depV;
if(m_x < -40 || m_x > 168 || m_y < -40 || m_y > 108) m_sortDuScope=true;
}
Et c'est assez rapide tout ça?Tu as été jusqu'à combien d'objets (en tout, y compris ceux qui ne sont pas affichés) dans tes tests? (avant que ça ne rame trop). C'est vrai que pour dans ton cas le C++ est assez utile.
J'ai mis les fonctions générales pour gérer les différents plans des objets (par ordre d'affichage à l'écran selon le type), et puis l'ensemble du code que j'ai du écrire pour gérer les rings fixes sur la map. Je trouve que le principe est assez simple: le handler (ici la fonction estGere() ) a à peu près la même forme pour tous les objets avec quelques modifs par ci par là... En tout cas, avec çà je définis entièrement le comportement de mon objet "ring".En effet. Mes handlers sont beaucoup plus compliqués, et il faut même gérer les collisions soi-même à partir du handler, mais je vais heureusement écrire des fonctions pour ça. Voici un exemple si ça t'intéresse:
void hRoller(SONIC *s, PERSO *p, u32 msg) { //Handler pour l'ennemi roulant
if (msg&MSG_COLLISION_SONIC) //Il doit mourir?
p->type=0;
if (msg&MSG_AFFICHER) { //Faut-il afficher le personnage?
//Le déplacement ne doit s'effectuer que dans ce cas (car le moteur de collisions est sauté sinon)
p->x--; //Le déplace sur la gauche
CreeObjetStandard(pAjSprite(ennemis_tiles,32*64),1,1,3);
}
return;
}
(avant que ça ne rame trop)
// dep horiz: vitesse constante
int time = m_timer>>1;
// time donne la position théorique;
// on se deplace de 1 pixels toutes les 2 unités de temps
// ( -> ~25 pix/sec )
char dep = time - m_distParcX; // dep a effectuer
m_x+=(m_speedX>0 ? dep : -dep);
m_distParcX+=dep;
// dep vertic: subit la "gravite"
int postheo = (m_speedY*time - 4*time*time)/32; // v0 = m_speedY/16 , g = -8/16
m_y += m_posReelle - postheo; // les y sont comptés à l'envers
m_posReelle=postheo;
Mouarf Le jeu ne ramera jamais.Le mien oui, avec 512 objets de type plate-forme mobile par exemple, le temps CPU demandé pour la gestion (hors affichage) est déjà très proche du 100% (95% exactement), donc il est impossible dans ce cas-là de tenir une vitesse correcte, donc l'affichage ne se fera jamais puisqu'il n'y a pas le temps pour, donc le jeu rame, oui.
C'est assez rapide, contrairement à ce qu'on pourrait croire... Ma gestion des listes d'objets est plus efficace qu'un simple tableau dans lequel on devrait chercher des emplacements libres pour rajouter des nouveaux objets par exempleJe reconnais que ta méthode est bien meilleure.
Mais ce qu'il se passe en fait c'est qu'on a tendance à définir une quantité impressionante de fonctions inline (surtout avec les fonctions membres de classe), et qu'on les utilise sans vraiment se rendre compte du nombre d'opérations qu'elles contiennent...Tu définis volontairement tes fonctions en inline? Perso je n'utilise vraiment jamais ce mot-clé. Sur ARM, l'appel des fonctions est assez rapide, et lorsque j'en ai besoin, j'utilise une macro.
main: b fonction b main fonction: bx lrComme tu le vois, un appel de fonction est très rapide (2 cycles), le retour également (3 cycles async). Je crois que les registres r0-r3 restent disponibles avec GCC sans qu'on aie besoin de les sauver/restaurer, donc c'est assez pour les petites routines.
le sprite de sonic lui-même fait un peu plus de 16 pixels de large, mais mon moteur de collisions entre sprites est également prévu pour les cas extrêmes donc je n'ai pas trop à m'inquiéterSonic avance combien de fois par seconde(indépendamment du framerate)? En fait qu'entends-tu par ces cas extrêmes?
mais bon honnetement j'ai déjà remarqué que ce n'est pas la rapidité du code qui compte, c'est plutôt la rapidité d'affichage des sprites... Le simple fait d'afficher un ring à l'écran est une opération bien plus lourde que de le gérer, crois-moiOui oui, mais ce que je voulais dire, c'est que la lenteur d'affichage des sprites n'est pas "grave", car ce n'est pas une partie vitale du jeu. En revanche, la gestion des objets se doit d'être rapide puisque tu ne peux pas y appliquer de "frameskip", et sans ça, ton jeu ne pourra rien faire.
Pour moi le problème principal est que certains ennemis (ceux qui requierent INSTR_COLLISION_DECOR par exemple) ne peuvent être gérés hors écran puisque la partie correspondante n'est pas décompressée tu as une solution?
Le mien oui, avec 512 objets de type plate-forme mobile par exemple, le temps CPU demandé pour la gestion (hors affichage) est déjà très proche du 100% (95% exactement), donc il est impossible dans ce cas-là de tenir une vitesse correcte, donc l'affichage ne se fera jamais puisqu'il n'y a pas le temps pour, donc le jeu rame, oui.
Oui oui, mais ce que je voulais dire, c'est que la lenteur d'affichage des sprites n'est pas "grave", car ce n'est pas une partie vitale du jeu. En revanche, la gestion des objets se doit d'être rapide puisque tu ne peux pas y appliquer de "frameskip", et sans ça, ton jeu ne pourra rien faire.C'est là qu'on voit la différence de philosophie Ti >< Casio
Pour l'avancement de Sonic, tu es à combien de pixels par passe (de combien de pixels il avance avant de faire une détection de collisions sur la map? avec les objets?). Perso, c'est 2 px/passe sur la map, 16 px/passe avec les objets.En fait j'aurais du mal à te répondre exactement vu que je ne le sais pas moi-même... La vitesse de sonic est utilisée par le jeu en pixels par secondes, à un certain facteur multiplicatif près pour la précision. Et comme mon framerate n'est pas constant, je peux difficilement te donner l'équivalent en pix/frame.
Orwell :
C'est là qu'on voit la différence de philosophie Ti >< Casio![]()
En fait j'avais déjà remarqué que vous faisiez attention au temps CPU utilisé par vos progs... les processeurs motorola supportent le multi-threading?J'avoue que je ne me casse pas trop la tête pour tout ça: à chaque frame, je gère tout et j'affiche tout, point barre.
Evidemment au plus il y a de choses à gérer et au plus il y a à afficher, au plus le temps mis pour préparer le frame en buffers sera long, et donc au moins je pourrai en afficher par seconde... J'ai donc intérêt à optimiser la geston des objets et surtout les méthodes d'affichage de sprites si je veux garder un framerate élevé.
Il n'est donc pas question de frameskip dans mon prog...
En fait j'avais déjà remarqué que vous faisiez attention au temps CPU utilisé par vos progs... les processeurs motorola supportent le multi-threading? J'avoue que je ne me casse pas trop la tête pour tout ça: à chaque frame, je gère tout et j'affiche tout, point barre. Evidemment au plus il y a de choses à gérer et au plus il y a à afficher, au plus le temps mis pour préparer le frame en buffers sera long, et donc au moins je pourrai en afficher par seconde... J'ai donc intérêt à optimiser la geston des objets et surtout les méthodes d'affichage de sprites si je veux garder un framerate élevé.Non, les TI ne supportent pas le multi-threading, mais le processeur est très peu performant, bien qu'il tourne à une fréquence élevée (12 MHz). Optimiser le dessin de sprites, c'est bien, mais si le jeu rame même s'il n'y a aucun sprite sur l'écran à cause du nombre d'objets trop élevé, ça ne va pas non plus. Mais sur TI ce n'est pas un problème en général non plus.
Tu définis volontairement tes fonctions en inline? Perso je n'utilise vraiment jamais ce mot-clé. Sur ARM, l'appel des fonctions est assez rapide, et lorsque j'en ai besoin, j'utilise une macro.En fait en C++ toutes les fonctions définies dans la déclaration d'une classe sont automatiquement traitées comme étant inline: c'est un énorme atout pour encourager le principe d'encapsulation.
Brunni :
Donc si j'ai bien compris pour ton système d'objets, à chaque fois que Sonic avance, tu envoies un signal à tous les objets dans le scope? Pas mauvaise idée tiens. Je vais y réfléchir.![]()
Pour quelqu'un qui a l'habitude du C, ca parait aberrant d'utiliser des fonctions valeur() et setValeur() plutot que de passer directement par la variable val. Par contre pour quelqu'un qui est habitué à la prog orienté objet, c'est vital!Ah ok. J'utilise parfois le C++ sur PC, mais je ne me doutais pas de ça.
A la fois dans la plupart des jeux "temps réel" (par opposition aux jeux qui dépendent d'évênements blocants), et particulièrement pour un jeu de plate-forme, l'affichage des sprites prend la quasi-totalité du CPU, aussi optimise l'affichage des sprites est une priorité quand un jeu ramm.En effet, mais comme je code comme un porc, le dessin des sprites ne représente quasi rien comparé au reste du code :]
Ce n'est pas vraiment un signal... Les objets peuvent connaitre le dernier déplacement que sonic a effectué (tout comme ils peuvent savoir comment la map s'est déplacée pour y rester accrochés (je travaille en relatif )), et donc simplement en regardant les intersections de leurs sprites au frame suivant, on peut savoir si un contact est apparu ou pasTu peux donc savoir si sonic a "traversé" l'objet, même si avec sa vitesse il est à présent à une distance de 100 pixels de cet objet?
Et tu as combien "d'objets" par map, environ ?Je crois qu'il avait dit une trentaine, ce qui semble assez peu, je pense qu'il voulait parler du nombre max. sur l'écran.
Tu peux donc savoir si sonic a "traversé" l'objet, même si avec sa vitesse il est à présent à une distance de 100 pixels de cet objet?Non bien entendu, mais ce n'est pas censé arriver souvent
Et tu as combien "d'objets" par map, environ ?