RPG Creator : créez votre MMORPG ou RPG sans aucune connaissance en programmation


Disponible le 4 Juin !




- Jouez à votre jeu sur tablettes tactiles, Smartphones et navigateurs Web
- Personnalisez vos menus
- Dessinez facilement et rapidement vos cartes
- Créez des actions pour le combat A-RPG


www.rpgcreator.net


Heures au format UTC + 1 heure [ Heure d’été ]




Publier un nouveau sujet Répondre au sujet  [ 3 messages ] 
Auteur Message
 Sujet du message: [C++] Une alternative optimisée à switch
MessagePublié: 15 Sep 2007, 15:22 
Artisan (Nv 4)

Inscrit le: 18 Aoû 2006, 00:00
Messages: 171
Points d'aide: 0/60

Créations :

Voir ses créations

Bonjour à tous, je profite de mon week end amplement mérité pour vous offrir un modèle de programmation ce coup ci, sans doute connu des programmeurs un peu expérimentés il s'agit de créer une classe de base abstraite d'où l'on fait dériver des classes filles spécialisées pour chaque problème et auquel on accedera simplement via un tableau polymorphique n26

Et on optimise l'optimisation ensuite voir le message suivant si vous n'avez pas peur des maux de crâne et si vous connaissez les pointeurs de fonctions en C++

En français dans le texte ? blink ...... n6 , bon pas grave je vais expliquer point par point.


I)Switch, case et break

On se placer dans une situation de jeux vidéo pour faciliter la compréhension. Afin de gérer les entrée clavier et les evenements divers a vrai dire nous allons créé une classe (engine_event pour l'occasion) qui grace à un appel via une fonction regardera quel est le dernier évennement en date.

Pour ce faire nous déclarons donc engine_event :

Citer:
class engine_event {
public :


// **********************************

engine_event() ;

// **********************************

virtual ~engine_event() ;

// **********************************

void frame() ;

// **********************************

private :
// **********************************

SDL_Event event ;
/* variable en charge de l'evenement */

// **********************************

} ;



J'ai volontairement simplifié drastiquement la classe bien entendu en ne prennant en compte aucun moyen de dialoguer avec l'extérieur, c'est un autre problème que j'aborderais un autre moment.

Analysons nous avons donc un constructeur, un destructeur virtuel (de base définissez toujours virtuels les destructeurs de toutes vos classes, cela permettra de ne pas avoir à trembler en cas d'un appelle à delete après utilisation de l'héritage) une fonction frame() qui est en fait l'appel au moteur (donc tout ce que l'on a a faire pour que le moteur chercher le dernier évenement et d'appeller frame) et une variable interne que vous reconnaitrez.

Oui mais voilà maintenant, que mettre dans la définition de frame ?

l'idée de base est de faire ainsi (ne pas tenir compte des §, ils servent a garder la mise en forme) :

Citer:
engine_event::frame() {

SDL_PollEvent(event) ;

switch (event.type) {
_______ case SDL_QUIT :
____________//action dans ce tel cas ....
_______ case SDL_KEYDOWN :
____________ switch (event.key.keysym.sym) {
___________________ case SDLK_UP :
___________________ //ect .....
_____________ }
_______ //ect .....
}


Je supose que personne ne sera choqué si je précise que cette solution est assez mauvaise un exemple simple imaginons que l'evenement voulut soit le dernier ! le programme va quand même vérifier toutes les posibilité jusqu'à trouver le bon (et donc perdre un certain temps jusqu'en bas), pire imaginons qu'à ce moment du jeu seul un évenement clavier soit utile (lorsque des messages défilent par exemple) l'ordinateur ferra tout une liste de vérification totalement inutile quand même. n31 sur un système aussi basique que le moteur d'evennement la perte niveau temps est pas forcément négligeable diront nous. De plus le système est assez peu évolutif et au bout d'un moment va être très lourd niveau maintenace.

Alors vous voulez une solution hein ? n40 , naaaa ze la donne pas, ou alors si mais il faudra m'appeller maître 417989 et me vénerer 73894 .

(759328 )

Quoi ? unsure pourquoi vous faite cette tête là ? euh .... rangez ces épée s'il vous plait ..... non pas les haches ! Ok je dis ! n37 zen ...... *retourne au tableau*


II)Penser Objet


La solution demande que vous connaissiez très bien les mécanismes suivant : héritage, polymorphisme, méthode virtuelle et éventuellement ce que j'ai zapé en cour de route.

la première remarque à faire est que nous n'avons pas besoin à tout instant des vérifier toujours les mêmes évenements selon "l'état" du système (en cinématique/combat/dialogues ect) nous allons donc incorporer une variable dans notre moteur d'évenement qui gardera cela en mémoire :

Citer:
class engine_event : public engine {
public :


// **********************************

engine_event() ;

// **********************************

virtual ~engine_event() ;

// **********************************

void frame() ;

// **********************************

private :
// **********************************

SDL_Event event ;
/* variable en charge de l'evenement */

// **********************************

int game_stat

// **********************************

} ;


Voilà pour le moment c'est assez simple c'est maintenant que tout va se corser. Alors tennez vous bien n2

Ce que nous allons faire c'est définir une classe de base abstraite [respirez j'explique ensuite] d'écouteur d'évenement.

Globalement cela veut dire une classe tel :

Citer:
class modulate_catch {
public :

modulate_catch() ;
virtual ~modulate_catch() ;
virtual void what(SDL_Event&) = 0 ;
}


Analysons simplement on trouve un constructeur et un destructeur (toujours virtuel !) ainsi qu'une fonction virtuelle pure nommée what.

Je n'ai pas vraiment la possibilité de vous faire un cour sur le polymorphisme ici mais pour rappel une fonction virtuelle pure doit obligatoirement être redéfinie dans les classes dérivées.

Je pense que vous commencez à voir, modulate_catch est abstraite (vu que contenant une fonction virtuelle pure) mais ensuite nous allons définir des classes dérivée de celle ci pour chaque état du système :

Citer:
class in_pause : public modulate_catch {
public :

in_pause() ;
virtual ~in_pause() ;
virtual void what(SDL_Event&) ;
}


Citer:
class in_battle : public modulate_catch {
public :

in_battle() ;
virtual ~modulate_catch() ;
virtual void what(SDL_Event&) ;
}


Citer:
class in_dialogue : public modulate_catch {
public :

in_dialogue() ;
virtual ~in_dialogue() ;
virtual void what(SDL_Event&) ;
}


Et pour chaque module nous décrirons un switch restrint à ce qui nous interresse actuellement :

Citer:
in_pause::what(SDL_Event &event) {
switch (event.type) {
case SDL_KEYDOWN :
switch (event.key.keysym.sym) {
case SDLK_PAUSE :
retirer la pause....
break ;
}
}


Citer:
in_battle::what(SDL_Event &event) {
switch (event.type) {
case SDL_KEYDOWN :
switch (event.key.keysym.sym) {
case SDLK_A :
faire attaquer....
break ;
case SDLK_B :
mettre en défense....
break ;
}
}


Maitenant il nous faut donc créer un tableau de pointeur vers des modulate_catch dans notre classe de moteur d'évenement :

Citer:
class engine_event : public engine {
public :


// **********************************

engine_event() ;

// **********************************

virtual ~engine_event() ;

// **********************************

void frame() ;

// **********************************

private :
// **********************************

SDL_Event event ;
/* variable en charge de l'evenement */

// **********************************

int game_stat

// **********************************

modulate_catch* catch_system[NBR_MODULATE_CATCH] ;

// **********************************

} ;


Et ensuite on définit un état par un numéro (de 0 à NBR_MODULATE_CATCH -1 bien sûr) pour cela il suffit d'utiliser quelques #define ou un enum.

reste à penser au constructeur et au destructeur :

Citer:
engine_event::engine_event() {
catch_system[IN_PAUSE] = new in_start() ;
catch_system[IN_BATTLE] = new in_battle() ;
catch_system[IN_DIALOGUE] = new in_dialogue() ;


(sous entendu ici IN_PAUSE = 0, IN_BATTLE = 1 ect)

Citer:
engine_event::~engine_event() {
for(int i = 0 ; i < NBR_MODULATE_CATCH ; i++) {delete catch_system ;}
}


Et maintenant il est très simple de voir la méthode frame() :

Citer:
engine_event::frame() {
SDL_PollEvent(event) ;
catch_system[game_stat]->what(event) ; //On aura toujours ici le minimum de vérification néccéssaire
}


Ainsi il suffit de faire varier la variable game_stat a chaque changement néccéssaire.

III)Le revers de la médaille

Les programmeurs en C++ (et même les autres d'ailleurs) le savent bien à chaque médaille son revers, ici on gagne bien souvent en vitesse d'éxecution (mais aussi en maintient du code) mais cependant l'existence des modules prend plus de place en mémoire vive qu'un code utilisant switch. Il ne faut cependant pas exagerer trop ce dernier point mais il faut le connaitre.

Voilà pour cette petite "astuce" assez violente au niveau de la programmation quand même j'avoue (mais que voulez vous on a rien sans rien). Sur ce je vous laisse et vous souhaite une bonne journée à tous les programmeurs/makeurs/personnes qui se seraient paumés ici et à la prochaine n41


Haut
 Profil  
 
 Sujet du message: Re: [C++] Une alternative optimisée à switch
MessagePublié: 23 Oct 2007, 16:43 
Villageois (Nv 2)

Inscrit le: 22 Avr 2007, 00:00
Messages: 34
Points d'aide: 0/60

Créations :

Voir ses créations

blink Ouaouh costaud !
Par contre c'est de la SDL, non ?
J'ai peut-être loupé quelque chose mais si on à pas SDL.dll,
Ca doit bugger... Après, avec les commandes SDL,
Ceux qui connaissent le voit facilement happy ...
Bon, après, j'ai pas tout lu ( n37 )

ciao


Haut
 Profil  
 
 Sujet du message: Re: [C++] Une alternative optimisée à switch
MessagePublié: 08 Déc 2007, 17:15 
Artisan (Nv 4)

Inscrit le: 18 Aoû 2006, 00:00
Messages: 171
Points d'aide: 0/60

Créations :

Voir ses créations

Darkemblem (avec un certain retard) >> Les structures SDL sont juste ici pour illustrer l'idée de l'optimisation du système, d'ailleurs je vous avait dit que la méthode plus haut présentait un revert de médaille alors en voici une qui présente un revert de médaille quasi inexistant :


Les pointeurs de fonctions

Rappel rapide sur ce qu'est un pointeur de fonction, je vous présente le code suivant :

Citer:
void (*Table)(const SDL_Event&) ;


Voilà aucune crise cardiaque ? happy tant mieux je vais m'expliquer vite fait sur ce détail sublime du langage avec un exemple et un micro discour.

En gros un pointeur de fonction est un pointeur vers une fonction et non vers une variable en mémoire, si cela est possible c'est surtout parce que mine de rien fonction ou variable tout reste des données en mémoire. L'avantage principal étant qu'un pointeur de fonction peut pointer sur une fonction, puis plus tard sur une autre regarder ceci par exemple :

Citer:
int add(int a, int b)
{
int c = a + b ;
return c ;
}

int sub(int a, int b)
{
int c = a - b ;
return c ;
}

int main() // Sous certain compilateur main() est standardisé, enfin bon ça on s'en moque
{

int (*fonct)(int, int) = NULL ;

fonct = add ; // Et oui ça marche
fonct(3 , 2) ; // Renvoit 5

fonct = sub ;
fonct(3 , 2) ; // Renvoit 1

return 0 ;
}


nota il est parfois considéré comme plus correct d'utiliser cette notation pour les pointeurs de fonctions lors de l'affectation et de l'appel (à vérifier cependant j'utilise bien plus la notation que je vous ai sus-montrée, je suis moyènnement sûr de celel ci) :

Citer:
// affectation : fonct = add
fonct = &add ;

// appel : fonct(3 , 2)
(*fonct)(3 , 2) ;


Analyse rapide lors de la création d'un pointeur de fonction vous devez spécifier le type de retour de la fonction (ici int), le nom de votre pointeur précédé d'un * (ici (*fonct) ) entre parenthèse ceci est très important ! et est une question de priorité des opérateurs. ensuite vous devez spécifier les paramètres de la fonction (ici (int, int) )

récapitulatif rapide : return_type (*fonction_name)( param_type(s) .... )

Vous l'aurez compris donc un pointeur de fonction ne peut pointer que des fonction renvoyant le même type et demandant les mêmes paramètres.

Bon maintenant revenons à cette histoire de switch que je vous explique quel est le rapport ^^

Et Switch dans tout ça ?

Et bien il y a autre chose avec les pointeurs de fonctions, car un pointeur de fonction est un pointeur et se manie comme un pointeur de variable en définitive donc.

Il est possible de créer des Tableau de pointeur de fonction ! happy

Autrement dit, ceci compile :

Citer:
void (*Table[nombre_de_fonction])(const SDL_Event&) ;


Vous voyez où je veux en venir ? n2

Plutôt que de créer un objet par 'statut' du jeu on se contente de créer une fonction renvoyant void et prenant un const SDL_Event& en paramètre. Et on affecte dans le constructeur de nombre moteur de jeu.

Citer:
void in_pause(const SDL_Event &event) {

switch (event.type) {

case SDL_KEYDOWN :
switch (event.key.keysym.sym) {

case SDLK_PAUSE :
retirer la pause....
break ;

}
break ;
}
}


Citer:
void in_battle(const SDL_Event &event) {

switch (event.type) {

case SDL_KEYDOWN :
switch (event.key.keysym.sym) {

case SDLK_?:
ce que vous voulez
break ;

}
break ;
}
}


Citer:
engine_event::engine_event() {
Table[IN_PAUSE] = in_pause ;
Table[IN_BATTLE] = in_battle ;
}


Et on ne touche pas au destructeur ! ^^ et oui on n'a strictement rien alloué dynamiquement ici après tout on utilise juste un pointeur vers une fonction en mémoire qui sera elle désallouée à la fin du programme comme toute les fonctions. Plus besoin non plus de créer une classe de base abstraite puis des classes héritant d'elle.

Et surtout niveau performance on gagne en temps et en place mémoire assez notablement happy . Donc finalement du tout benef par rapport à l'ancienne méthode.

Cependant une chose à savoir sur les pointeur de fonction est qu'il faut en général un petit peu plus de temps pour appeler une fonction via un pointeur de fonction que directement (ici c'est largement négligeable par rapport aux gains même face à l'utilisation de switch), tout simplement car l'ordinateur doit d'abord aller chercher la fonction en mémoire avant de l'appeller, en résumé rapide ^^'.

Voilà, pour n'importe quel question, remarques ou chaussures sales mp moi n2

Cordialement, Kco_Quidam


Haut
 Profil  
 
Afficher les messages depuis:  Trier par  
Publier un nouveau sujet Répondre au sujet  [ 3 messages ] 

Heures au format UTC + 1 heure [ Heure d’été ]


Qui est en ligne ?

Utilisateurs parcourant actuellement ce forum : Aucun utilisateur inscrit et 1 invité


Vous ne pouvez pas publier de nouveaux sujets dans ce forum
Vous ne pouvez pas répondre aux sujets dans ce forum
Vous ne pouvez pas éditer vos messages dans ce forum
Vous ne pouvez pas supprimer vos messages dans ce forum
Vous ne pouvez pas insérer de pièces jointes dans ce forum

Rechercher pour:
Sauter vers:  
cron
RPG Creative Forum version 5 ; Tous droits réservés
phpBB Group (Traduit par Xaphos)
Optimisé pour une résolution 1024*728