oui mais cela voudrais dire qu'il peut sortir par n'importe quel endroit de la map donc pour contrôler le changement de map cela deviendrait peut-être plus complexe car il pourrait sortir par en haut comme en bas comme à droite ou encore à gauche
Version imprimable
oui mais cela voudrais dire qu'il peut sortir par n'importe quel endroit de la map donc pour contrôler le changement de map cela deviendrait peut-être plus complexe car il pourrait sortir par en haut comme en bas comme à droite ou encore à gauche
En effet. Si ça te fait peur, on peut garder les téléporteurs pour l'instant. Dans ce cas, il faut pouvoir en avoir plusieurs par map.
Ensuite, pour savoir quel téléporteur est le bon, deux solutions: Soit les numéroter, soit les connaître uniquement par leurs coordonnées.
Ce qui veut déjà dire que ta map doit être plus complexe qu'un simple tableau 2D: Il nous faut une structure à présent:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 struct SWarp { /*ici les coord de départ sur la map courante*/ int sourceX; int sourceY; /*Ici, l'identification de la map de sortie: nom, coordonnées, etc.*/ int destMapX; int destMapY; /*ici les coordonnees d'arrive sur la map de sortie*/ int destX; int destY; } struct SMap { char tiles[10][20]; struct SWarp warps[8]; char titre[32]; };
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 struct SMap allMaps[2] = { /*Salle (0; 0)*/ { { "###################", "#@ #", "# #", "# #", "# #", "# : #", "# :", "# + #", "# #", "###################" }, { { 5, 5, ??, ??, ??, ?? }, /*Je ne sais pas où ça mène*/ { 19, 6, 1, 0, 0, 6 } /*La porte à droite*/ }, "Cuisine Ouest" }, /*Salle (1; 0)*/ { { "###################", "# #", "# #", "# #", "# + #", "# #", ": #", "# #", "# #", "###################" }, { { 0, 6, 0, 0, 19, 6 } /*La porte à gauche*/ }, "Cellier Est" };
Bon je vais me plonger dans le code et reviendrai vers vous si je ne comprends pas tout car à première vue je vois un peu mais il me reste des points d’interrogation en tout cas merci pour cette réponse
Note que le champ titre n'est pas un champ nom. Sa valeur est uniquement vouée à être affichée, surtout pas à être utilisée en interne pour identifier la map. Si on veut identifier une map par son nom plutôt que ses coordonnées, il faudra lui donner un champ nom séparé.
Alors voila j'ai deux questions peut-être bête je ne sais pas :
1erpourquoi 1; 0 et non pas 0; 1 ?Code:/*Salle (1; 0)*/
2emequand il tombe sur ça l’interprète t-il ou pas ?Code:
1
2 { 5, 5, ??, ??, ??, ?? }, /*Je ne sais pas où ça mène*/ { 19, 6, 1, 0, 0, 6 } /*La porte à droite*/
quelques remarques:
- tu ne devrais pas itérer sur toutes les cases d'une map pour trouver le joueur, tu peux stocker sa position. (Et si d'autres entités doivent bouger, tu peux stocker leurs position aussi.)
- vu que tu bosses avec un affichage console pour le moment, tu devrais éviter de redessiner la map pour rien (quand rien ne bouge), ça évitera des scintillements.
- utilise une enum pour différencier tes tiles.
ça t'évitera des erreurs du à des fautes de frappes et rendra le code plus clair. Tu auras juste à "convertir" ça lors de l'affichage, par exemple avec une fonction du styleCode:
1
2
3
4
5
6 enum TileType { TT_EMPTY, TT_PLAYER, TT_WARP, etc... };
edit:Code:
1
2
3
4
5
6 inline char charFromTile(TileType tp) { switch(tp) { case TT_EMPTY: return ' '; case TT_PLAYER: return '@'; etc.. }
Si tu te poses la question des "??", non, c'est à toi de les remplacer par les bonnes valeurs ;)
Donc si je te suis bien quand je vais déplacer le joueur sur la case et que le programme va voir celail va interpréter toute les informations et changer de mapCode:{ 19, 6, 1, 0, 0, 6 } /*La porte à droite*/
bon alors je suis parti sur la structure de médinocmais une chose reste en parti un peu flou donc si quelqu'un peu m'expliquer cela serai merveilleux. la parti flou est le changement de map je comprend pas tout ?Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 struct SWarp { /*ici les coord de départ sur la map courante*/ int sourceX; int sourceY; /*ici, l'identification de la map de sortie: nom, coordonnées, etc.*/ int destMapX; int destMapY; /*ici les coordonnees d'arrive sur la map de sortie*/ int destX; int destY; }; struct SMap { char tiles[10][20]; struct SWarp warps[8]; char titre[32]; }; struct SMap allMaps[2] = { /*Salle (0; 0)*/ { { "###################", "#@ #", "# #", "# #", "# #", "# #", "# :", "# + #", "# #", "###################" }, { //{ 5, 5, ??, ??, ??, ?? }, /*Je ne sais pas où ça mène*/ { 19, 6, 1, 0, 0, 6 } /*La porte à droite*/ }, "Cuisine Ouest" }, /*Salle (1; 0)*/ { { "###################", "# #", "# #", "# #", "# + #", "# #", ": #", "# #", "# #", "###################" }, { { 0, 6, 0, 0, 19, 6 } /*La porte à gauche*/ }, "Cellier Est" } };
edit : alors j'ai bidouiller un peu pour faire en sorte d'utiliser la structure et voila le codeseul petit bémol il ne change pas de map va savoir pourquoi je travail encore dessus n'hesiter pas a me dire si vous avez la raison du pourquoi merciCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 #include <iostream> #include <windows.h> using namespace std; struct SWarp { /*ici les coord de départ sur la map courante*/ int sourceX; int sourceY; /*ici, l'identification de la map de sortie: nom, coordonnées, etc.*/ int destMapX; int destMapY; /*ici les coordonnees d'arrive sur la map de sortie*/ int destX; int destY; }; struct SMap { char tiles[10][20]; struct SWarp warps[8]; char titre[32]; }; struct SMap allMaps[2] = { /*Salle (0; 0)*/ { { "###################", "#@ #", "# #", "# #", "# #", "# #", "# :", "# + #", "# #", "###################" }, { //{ 5, 5, ??, ??, ??, ?? }, /*Je ne sais pas où ça mène*/ { 19, 6, 1, 0, 0, 6 } /*La porte à droite*/ }, "Cuisine Ouest" }, /*Salle (1; 0)*/ { { "###################", "# #", "# #", "# #", "# + #", "# #", ": #", "# #", "# #", "###################" }, { { 0, 6, 0, 0, 19, 6 } /*La porte à gauche*/ }, "Cellier Est" } }; bool Exit = false; int inmap = 0; int gamespeed = 200; int main() { int y, y2, x, x2; while(Exit == false) { system("cls"); for(y = 0; y < 10; y++) { cout << allMaps[inmap].tiles[y] << endl ; } cout << allMaps[inmap].warps->sourceX << endl; for(y = 0; y < 10; y++) { for(x = 0; x < 20; x++) { switch(allMaps[inmap].tiles[y][x]) { case '@': { if(GetAsyncKeyState(VK_UP) != 0) { y2 = y - 1; switch(allMaps[inmap].tiles[y2][x]) { case ' ': allMaps[inmap].tiles[y][x] = ' '; y--; allMaps[inmap].tiles[y2][x] = '@'; break; } } if(GetAsyncKeyState(VK_DOWN) != 0) { y2 = y + 1; switch(allMaps[inmap].tiles[y2][x]) { case ' ': allMaps[inmap].tiles[y][x] = ' '; y++; allMaps[inmap].tiles[y2][x] = '@'; break; } } if(GetAsyncKeyState(VK_LEFT) != 0) { x2 = x - 1; switch(allMaps[inmap].tiles[y][x2]) { case ' ': allMaps[inmap].tiles[y][x] = ' '; x--; allMaps[inmap].tiles[y][x2] = '@'; break; case ':': inmap = allMaps[inmap].warps->destX; x = allMaps[inmap].warps->sourceX; y = allMaps[inmap].warps->sourceY; break; } } if(GetAsyncKeyState(VK_RIGHT) != 0) { x2 = x + 1; switch(allMaps[inmap].tiles[y][x2]) { case ' ': allMaps[inmap].tiles[y][x] = ' '; x++; allMaps[inmap].tiles[y][x2] = '@'; break; case ':': inmap = allMaps[inmap].warps->destX; x = allMaps[inmap].warps->sourceX; y = allMaps[inmap].warps->sourceY; break; } } } break; } } //Sleep(gamespeed); } } return 0; }
edit : non finalement erreur de ma part il change bien de map juste petit probleme a regler encore je tiens au courant
Salut,
D'abord, une petite correction...
Contrairement à C, lorsque tu définis une structure, une union, une énumération ou une classe en C++, tu déclare automatiquement un type dont l'identifiant est celui de la structure, de l'union, de l'énumération ou de la classe en question.
Il est donc tout à fait inutile de répéter le mot clé struct (ou enum, ou union ou encore class) lorsque tu veux utiliser le type en question comme membre d'une autre classe ou structure (ou union).
le codeserait donc avantageusement remplacé parCode:
1
2
3
4
5
6 struct SMap { char tiles[10][20]; struct SWarp warps[8]; char titre[32]; };
et le codeCode:
1
2
3
4
5 struct SMap { char tiles[10][20]; SWarp warps[8]; char titre[32]; };
peut (doit :question:) s'écrire sous la forme deCode:struct SMap allMaps[2] = { /*...*/
et, dans la même veine, tant qu'on y est:Code:SMap allMaps[2] = { /*...*/
avec le code
tu force ta map à une taille fixe et définitive.Code:char tiles[10][20];
Cela peut être une solution, mais je te donne mon billet qu'elle t'embêtera très rapidement, soit parce que tu voudra un jour créer un couloir de deux cases de large, soit parce que tu voudra un jour créer une "méga salle" de 20*40 ;) :aie:
L'idéal est sans doute de fournir une valeur pour le nombre de colonnes et une autre le nombre de lignes, et de travailler avec un tableau de (nombre de lignes * nombre de colonnes) éléments.
Il "suffit" de travailler avec la formule index = numéro de ligne * nombre de colonnes + numéro de colonnes pour récupérer l'index auquel correspond la case se trouvant à la ligne (numéro de ligne) et à la colonne (numéro de colonne).
Si tu travailles avec la classe vector, fournie par le standard dans l'espace de noms std par simple inclusion du fichier d'en-tête <vector>, tu auras l'occasion de travailler avec des map de n'importe quelle taille sans avoir à t'inquiéter de quoi que ce soit en terme d'espace mémoire dynamique ;)
De même avec le code
tu force chaque map à avoir systématiquement 8 téléporteurs, ni moins, ni (et surtout pas!!!!) plus.Code:SWarp warps[8];
Cela peut finir par poser quelques problèmes :aie: : certaines map ont peut etre des portes, mais pas de télé-porteurs du tout, et tu pourrais même envisager une "salle des télé-porteurs" qui te permet d'accéder à n'importe quelle map existante, ou que sais-je, mais qui nécessite d'avoir bien plus de 8 télé-porteurs :aie:
Encore une fois, le fait de pouvoir avoir "n'importe quel nombre de télé-porteurs" peut s'avérer intéressant, et la classe vector pourrait t'aider énormément à ce sujet ;)
Enfin, la ligne
pose aussi problème, car elle place une limite tout à fait artificielle au nombre de caractères que tu peux afficher (en l’occurrence, 31 vu qu'il y a le '\0' terminal)Code:char titre[32];
Encore une fois, le standard vient à ta rescousse en fournissant la classe string, disponible dans l'espace de noms std par simple inclusion du fichier <string> qui est la classe à préférer à toute autre solution pour représenter une chaine de caractères.
Au final, ta structure SMap devrait sans doute ressembler à quelque chose comme
En plus, le fait de coder les maps en "dur" (comprends: directement dans ton code) va te poser de très sérieux problèmes, dans le sens où tu devra systématiquement re compiler ton jeu chaque fois que tu voudra, par exemple, rajouter un mur ou une porte (et je ne te parle pas des problèmes que tu auras si tu veux carrément rajouter toute une map :aie:).Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 struct SMap { size_t rows; size_t cols std::vector<unsigned char> tiles; std::vector<SWrap> wraps; std::string title; /* et, comme on peut travailler en orienté objets, on peut rajouter * une fonction membre qui renvoie le caractère qui se trouve en X,Y ;) */ const unsigned char tileAt(size_t x, size_t y) const {return tiles_[y*cols + x];} /* et la même chose avec une Position (sait on jamais ;)) */ const unsigned char tileAt(Position const & pos) {return tileAt(pos.x(), pos.y());} };
L'idéal est donc de prévoir, dés le départ, la possibilité de gérer un nombre quelconque de maps de dimensions quelconques et composées d'un nombre quelconque de télé-porteurs et de portes, et surtout, de faire en sorte de ne pas avoir à recompiler ton jeu pour que les changements soient pris en compte.
Et pour cela, il n'y a pas 36 solutions : il faut passer par un (ou plusieurs) fichier(s) qui fournira (fourniront) toutes les indications que tu peux envisager.
Mais, pour cela, il faut réfléchir un peu à la manière dont on va fournir toutes les informations, pour pouvoir déterminer comment on peut les charger (et pourquoi pas, les sauvegarder, si d'aventure tu décide de créer un "éditeur de cartes" :D )
Le "monde" pourrait très bien etre subdivisé en différentes cartes selon un système proche de l'image ci-jointe sur laquelle chaque rectangle de couleur correspond à une carte particulière et les nombres séparés par des virgule (la première ligne dans chaque rectangle ;)) correspond aux coordonnées du coin supérieur gauche de la carte dans le monde. (Bon, ce n'est pas fait à l'échelle, mais ca te permettra de comprendre le principe ;))
Si tu associe pour chaque carte les coordonnées et un nom de fichier, tu peux charger une bonne fois pour toute une table d'équivalence entre les coordonnées et le fichier qui correspond à la carte à laquelle tu dois accéder.
Cela pourrait se faire avec un simple fichier texte proche de
world.txt(j'ai choisi des noms de fichiers correspondant aux couleurs employées, mais tu peux choisir n'importe quoi hein ;))Code:
1
2
3
4
5 0 0 rouge.txt 25 0 jaune.txt 45 0 vert.txt 0 25 gris.txt 45 31 bleu.txt
Chaque fois que tu réduis l'espace où "vivent les koalas" en créant une carte supplémentaire, il te "suffit" de rajouter les coordonnées de son coin supérieur gauche et le nom du fichier qui en donne la description. Simple et efficace ;)
Tu pourrait envisager charger cette liste sous la forme d'un
où la classe Position serait proche deCode:std::map<Position, std::string> maplist;
Le chargement pourrait prendre la forme deCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 class Position { public: Position(int x, int y):x_(x), y_(y){} int x() const{return x_;} int y() const{return y_;} private: int x_; int y_; }; /* Pour pouvoir utiliser la std::map, on a besoin de l'opérateur de *comparaison < * */ bool oprtator <(Position const & first, Position const & second) { return first.x() < second.x() || (first.x() == second.x() && first.y() < second.y() ); } /* et, tant qu'à faire, te l'opérateur de comparaison == (pour d'autres * usages ultérieurs, très certainement ;) */ bool oprtator ==(Position const & first, Position const & second) { return first.x() == second.x() && first.y() == second.y() ); }
En fait, l'idée est simple: chaque fois que tu arrives sur un télé-porteur, tu cherches dans maplist (selon mon exemple) le nom du fichier qui correspond aux coordonnées destMapX et destMapY de la structure SWarp.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 void loadMapList(std::map<Position, std::string> & toFill) { std::ifstream ifs("world.txt"); int x; int y; std::string filename; while(ifs>>x>>y>>filename) { toFill.insert(std::make_pair(Position(x,y),filename); } } int main() { std::map<Position, std::string> maplist; loadMapList(maplist) ; /* tout le reste vient ici ;) */ return 0; }
Ce nom de fichier correspond au nom de fichier qu'il faut charger pour obtenir la carte en question
Evidemment, ca signifie que tu dois prévoir un format qui permette de représenter une carte.
Je te proposerais bien:
sur la première ligne, le nombre de lignes et le nombre de colonnes qui composent la carte, séparés par un espace
sur les (nombre de lignes) suivante, les (nombre de colonnes) qui correspondent à la topologie de la carte
juste après, nous aurions le nombre de télé-porteurs suivi
des informations relatives aux portes et télé-porteurs (après tout, y a pas des masses de différences entre les deux ;), et, enfin, le titre à afficher.
En respectant les conventions de ton propre code, la carte rouge pourrait ressembler à quelque choses comme
(en fait, ce qui suit les // n'apparait pas dans le fichier, je les ai juste rajoutés pour que tu comprennes de quoi il s'agit ;)Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 50 25 // 50 linges, 25 colonnes ######################## # # # # # ### # # #@####### # # # # # # # ! # # # # # ### # # # # # # # # # # ##### # # # # # # # # # ##### # # # # # ### ################# ## # # # # # # # # # ##### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ################## ### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################## 3 // 3 portes ou télé-porteurs 25 6 25 0 6 0 //la porte en 6,25 mène à la carte jaune (25,0) en position 6,0 2 5 45 31 50 50 // le télé-porteur en 2,5 mène à la carte bleue (45,31) en position 50,50 5 50 0 50 5 0 // la porte en 5,50 mène à la carte grise (0, 50) en position 5,0 desert
Et tu aurais un code pour charger ta map proche de
NOTA: tu devras peut etre prévoir quelque chose pour valider le format du fichier que tu lis, histoire de t'assurer que les données lues soient correctes, mais je ne vais quand meme pas tout faire pour toi ;)Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 void loadMap(SMap & map, std::string const & filename) { map.tiles.clear(); //supprime les informations de la map précédante map.wraps.clear(); stdifstream ifs(filename); size_t rows; size_t cols; ifs>>rows>>cols; for(size_t y = 0; y <rows; ++y) { for(size_t x = 0; x <cols; ++x) { char c; ifs>>c; map.tiles.push_back(c); } } int wrapCount; ifs>>wrapCount; for(int i = 0; i< wrapCount;++i) { SWrap wrap; ifs>>wrap.sourceX>>wrap.sourceY >>wrap.destMapX>>wrap.destMapY >>wrap.destX>>wrap.destY; map.wraps.push_back(wrap); } ifs>>map.title; } // modifions la fonction main pour commencer dans la carte rouge int main() { std::map<Position, std::string> maplist; loadMapList(maplist) ; auto it= maplist.find(Position(0,0)); SMap map; loadMap(map, it.second); return 0; }
Bon, j’atteins de nouveau la taille d'un roman avec mon intervention, je vais donc m'arrêter là, histoire de te laisser le loisir de digérer tout cela ;)
Hope it helps ;)
Euh, je ne sais pas pourquoi, je me croyais en C... :oops:
Quant aux zones de taille fixe, je raisonnais en termes de fichiers binaire et de localité mémoire totale (donc, pas de char*). Mais tu avais parfaitement raison sur le fait d'avoir besoin d'indiquer le nombre de téléporteurs.
@devdeb91: C'est à toi d'écrire le code pour interpréter les téléporteurs; personnellement, j'avais juste défini la structure.
Quand le joueur tombe sur un ':', le code doit parcourir la liste de warps de la map courante pour trouver le bon à partir de ses coordonnées (en gros, une bète recherche linéaire dans le tableau de 8). Puis, charger la map de destination si elle est différente, et déplacer le perso.
@koala01 :
bon j'avoue que j'ai lâché en cours de route mais je vais relire et relire jusqu’à comprendre merci a toi
edit : alors voila quelques questions toutes bêtes
est on obliger de utiliser size_t peut on plutot ne pas utiliser int ?Code:size_t rows;
est on obliger de utiliser des unsigned et const car j'avoue ne jamais utiliser ceux la ?Code:
1
2 const unsigned char tileAt(size_t x, size_t y) const {return tiles_[y*cols + x];}
en quoi consiste les instruction entre {} car je n'est jamais vue cette façon de coder ?
est il vraiment obliger des fonction comme map ou make_pair car j'avoue ne pas m'y connaitre au point de les connaitre a vrai dire c'est la première fois que je les voisCode:
1
2 std::map<Position, std::string> maplist; toFill.insert(std::make_pair(Position(x,y),filename);
- Size_t est le type conseillé pour les tailles et indices de tableau.
- Pour le coup des unsigned char, je ne suis pas convaincu. Pour const, oui tu vas l'utiliser, tu vas le voir tous les jours et tu vas apprendre à l'aimer. La const-correctness est indispensable quand on bosse ainsi à bas niveau, sans filet.
- Par contre, un type de retour par valeur const, je ne vois pas trop à quoi ça sert.
- std::map est vachement utile. make_pair on peut peut-être s'en passer, je n'ai pas l'habitude de l'utiliser.
Dans ce cas la pourriez vous m'expliquer a quoi sert std::map car j'ai beau lire sur internet cela ne m'apporte pas grande info
Source: http://en.cppreference.com/w/cpp/container/mapCitation:
std::map is a sorted associative container that contains key-value
pairs with unique keys.
Qu'est-ce que tu ne comprends pas dans cette phrase?
Un conteneur associatif associe une clee a une ou plusieurs valeur. Dans le cas de std::map, c'est une clee et une valeur.
make_pair() permet just de ne pas avoir a specifier les types des elements du constructeur de std::pair.Citation:
Envoyé par Medinoc
deviensCode:mon_index.insert( std::pair< int, std::string >( clee, valeur ) );
Ce qui est donc beaucoup plus facile a ecrire et lire dans du code generique, ou meme en general. Mais sinon ces deux codes sont equivalents (a ce que je sache).Code:mon_index.insert( std::make_pair( clee, valeur ) );
dans ce cas laposition est la clé et étant une classCode:std::map<Position, std::string> maplist;
il associe sa a une chaine si je comprend bien ?Code:
1
2
3
4
5
6
7
8
9
10
11 class Position { public: Position(int x, int y):x_(x), y_(y){} int x() const{return x_;} int y() const{return y_;} private: int x_; int y_; };
Han s'te révélation *_*
A chaque fois que je vois un post partant de sémantique d'entité vs sémantique de valeur je me sens con car c'est un concept fondamental, mais il y a quelques semaines / mois, j'avais jamais entendu ces 2 termes.
Et la, en lisant ça, je me disais que j'aurais codé ça un peu différemment (sans vouloir relancer un énième débat sur les getters :aie:)
Bref je me rend compte qu'en fait même sans connaitre ces termes je faisait déjà la différence entre les 2, j'utilise des class pour les entités, des struct pour les valeurs.Code:
1
2
3
4 struct Position { const int x, y; Position(int x, int y): x(x), y(y) { } };
C'est une pratique courante ? (car j'ai forcément appris ça quelques part...)
Voila, ici la Position est la position d'une map dans "le monde" (Ou une grande map, regroupant toute les autres), et le string est le nom du fichier contenant la définition de cette map.
oh merci iradrille tu est mon sauveur:lol: voila ce que j'attendais. Là ça parait plus clair mais largement plus clair ça m'enlève déjà un blocage
edit: bon au point de paraitre lourd ( je vous l'accorde ) je vais quand même demander
ici aussi map est utilisé mais pas de la même syntaxe donc comment je dois interprété cette ligne (en langage humain)Code:void loadMap(SMap & map, std::string const & filename)
D'abord, excusez cette réponse tardive, mais, étant nouvellement papa, je passe encore pas mal de temps à l'hopital :PC'est le problème des romans, ils contiennent un tas d'informations qu'il faut arriver à digérer :?
Mais n'hésite pas à demander des précision sur ce qui pose problème ;)
edit : alors voila quelque question toute bete
En fait, size_t est un alias de type (un bête typedef en sommes :D) sur un type non signé (donc dont la valeur minimal est 0) qui permet au minimum de représenter "n'importe quelle adresse accessible par le système".Citation:
est on obliger de utiliser size_t peut on plutot ne pas utiliser int ?Code:size_t rows;
Comme il n'est vraiment pas conseillé d'utiliser un index négatif, le fait d'utiliser un entier non signé nous évite déjà ce genre de problème.
De plus, size_t est le type que renvoient certaines fonctions membres de pas mal de classes de la S(T)L, comme size() pour les différentes collections ou encore find_X_X_X pour la std::string.
Le fait d'utiliser directement le bon type pour chaque usage permet d'éviter:
Bref, cela t'évitera surement pas mal de soucis et de bugs "difficilement compréhensibles" :D
- un avertissement du compilateur (s'il est bien réglé) qui pourrait en fait dégénérer en
- un problème du à un dépassement de valeur (si tu utilises un type dont la fourchette de valeur est inférieure à celle à laquelle tu veux la comparer, par exemple)
j'aurais pu renvoyer un char non unsigned, non const...Citation:
est on obliger de utiliser des unsigned et const car j'avoue ne jamais utiliser ceux la ?Code:
1
2 const unsigned char tileAt(size_t x, size_t y) const {return tiles_[y*cols + x];}
Mais le const qui précède unsigned indique que le caractère renvoyé n'a pas vocation à être modifié.
Cela ne change pas grand chose ici, mais le fait est que, si tu prends l'habitude de déclarer constant tout ce qui n'a pas vocation à être modifié, tu fera du compilateur ton meilleur allié car il se mettra à t'insulter chaque fois que tu te mettras dans une situation où ce qui n'a pas à être modifié risque de l'être ;)
Par contre, le const qui suit les parenthèses est particulièrement important parce qu'il indique au compilateur que cette fonction s'engage à ne pas modifier l'état de l'objet courent.
Tu peux sans problème appeler ce genre de fonctions sur des objets non constants (comprends: modifiables), mais tu ne peut appeler que des fonctions membres qui ont pris cet engagement depuis des objets constants (les deux coté d'une même pièce, en fait, qui font que le compilateur se transforme en ton meilleur allié ;))
Ben, c'est tout simple...Citation:
en quoi consiste les instruction entre {} car je n'est jamais vue cette façon de coder ?
tiles est un objet de type std::vector qui n'est (faisons simple :D) qu'une collection de type "tableau d'éléments contigus en mémoire".
Comme tous les tableaux d'élément contigus en mémoire, il est possible d'accéder à un élément donné sur base de son indice dans le tableau (sa "position" dans le tableau si tu préfères).
Le fait est que le tableau n'a ici qu'une dimension, alors que tu l'aurais sans doute représenté sous la forme d'un tableau à deux dimensions.
Il n'y a pas de problème quant au nombre d'éléments pour la simple et bonne raison qu'un tableau deux dimensions proche de
permet de représenter linges*colonnes éléments, et que cela reviens donc tout à fait au même que si l'on créait un tableau sous la forme deCode:UnType tab2D[linges][colonnes];
qui contient lui aussi linges*colonnes éléments.Code:UnType tab1D[linges*colonnes];
Seulement, voilà... tab2D (selon mon premier exemple) est en réalité un tableau de tableau, et c'est pour cela que l'on peut utiliser les deux opérateurs [] l'un à coté de l'autre alors que tab1D est... un tableau, sans plus, et on ne peut donc utiliser qu'un seul opérateur [].
Par chance, il existe une formule très simple pour convertir l'un en l'autre car si tu veut récupérer l'élément qui se trouve à la Yeme ligne et à la Xeme colonne (l'équivalent de tab2D[Y][X]), il te suffit d'accéder à l'élément qui se trouve à l'indice Y*(nombre de colonnes) + X dans tab1D.
Le code
ne fait rien d'autre qu'appliquer cette formule ;)Code:{return tiles_[y*cols + x];}
Mais peut etre es tu surtout étonné de voir l'implémentation de la fonction membre directement dans la définition de la classe :question:
En fait, il faut savoir qu'il est totalement légal de le faire (sauf dans un cas particulier), et que je suis un peu flemmard par moment, mais que, en plus, cela a pour résultat de rendre implicitement la fonction inline (autrement dit: cela demande au compilateur de remplacer directement tous les appels à la fonction par le code de la fonction elle-même).
Cela peut améliorer grandement les performances pour des fonctions très simples, mais le compilateur reste seul juge pour décider de l'opportunité de le faire... ou non.
Dans le cas présent, la fonction est particulièrement simple (elle se traduit par une ou deux instruction(s) processeur(s) dans le code binaire exécutable) et on a donc la garantie qu'elle sera effectivement inlinée ;)
Je gagne donc sur les deux tableaux en travaillant de la sorte: je gagne un peu de performances et je me laisse aller à ma flemmardise naturelle en évitant d'avoir à créer un fichier *.cpp "juste pour cela" ;) :D
La fonction make_pair est juste une fonction que j'aime utiliser car elle permet de se faciliter la vie (autrement, tu dois toi-même créer une std :: pair avec les bons types et tout et tout... je trouve cela ch..ant, et moins lisible ;))Citation:
est il vraiment obliger des fonction comme map ou make_pair car j'avoue ne pas m'i connaitre au point de les connaitre a vrai dire c'est la premiere fois que je l'est voisCode:
1
2 std::map<Position, std::string> maplist; toFill.insert(std::make_pair(Position(x,y),filename);
en fait map est une classe fournie par le standard, et non une fonction...
C'est ce que l'on appelle un conteneur associatif qui associe une valeur (la std :: string) à une clé qui permet de retrouver très rapidement la valeur sur base de la clé.
C'est en fait un arbre binaire équilibré, qui permet une recherche dichotomique qui fait que tu n'a, par exemple, besoin que de 8 comparaisons pour retrouver un élément parmi 255 éléments potentiels ;)
Tout à fait
Ca ne m'étonne absolument pas.
A part les cours de quelques pointures que l'on rencontre sur le forum, il y a très peu de cours (de langue francaise s'entend) où l'on en entende parler, pour être tout à fait honnête ;)
Et pourtant, cette distinction est particulièrement importante, car elle te permet de déterminer très rapidement ce que tu peux envisager de demander à tes différentes classes... ou non ;)
C'est une manière de faire qui n'est pas sans fondement ;)Citation:
Et la, en lisant ça, je me disais que j'aurais codé ça un peu différemment (sans vouloir relancer un énième débat sur les getters :aie:)
Bref je me rend compte qu'en fait même sans connaitre ces termes je faisait déjà la différence entre les 2, j'utilise des class pour les entités, des struct pour les valeurs.Code:
1
2
3
4 struct Position { const int x, y; Position(int x, int y): x(x), y(y) { } };
Parles tu de ma pratique ou de la tienne :question:Citation:
C'est une pratique courante ? (car j'ai forcément appris ça quelques part...)
Sinon, je crois que c'est surtout une question d'habitude :aie:
Personnellement, j'ai pris l'habitude de limiter l'utilisation des structures à deux domaines:
- les structures vides (qui sont, généralement destinée à servir de flag ;))
- les foncteurs, dont je suis très friand
Et j'utilise les classes dés qu'il s'agit d'un type de donnée pour lesquels j’attends un certain nombre de services (fusse juste le fait de renvoyer la valeur d'un membre ;))
Je tiens à préciser qu'il s'agit ici d'une habitude strictement personnelle (quoi que me semble-t-il régulièrement rencontrée ;)), qui n'est ni plus ni moins justifiable que n'importe quelle habitude du même type.
Simplement, je trouve que cela permet d'avoir un code cohérent, dans lequel on invoque systématiquement une fonction au départ d'un objet pour en obtenir un service donné ;).
Il n'y a jamais de question idiote, il n'y a que les réponses qui le soient ;)
Ceci dit, tu aurais peut etre pu trouver la réponse dans n'importe quel tutoriel correct :D
Mais bon, je suis décidément trop bon, et doncCette fonction ne renvoie rien et prend comme argumentsCitation:
ici aussi map est utilisé mais pas de la meme syntaxe donc comment je dois interprété cette ligne (en langage humain)Code:void loadMap(SMap & map, std::string const & filename)
Alors, comme je présume que tu seras gras si je te laisse en plan avec cette explication, je vais être obligé d'aller un peu plus loin ;)
- une référence (non constante) sur un objet de type SMap qui sera connu sous le nom de map
- une référence constante sur un objet de type std ::string qui sera connu sous le nom de filename
Mais, pour que l'explication ait un sens, il me faut commencer par rappeler un point essentiel:
Sauf indication contraire, les arguments sont passés aux fonctions par singleton, et le passage par valeur occasionne la copie de la variable transmise.
Le premier résultat est que si la fonction appelée modifie l'argument qu'elle reçoit par valeur, aucune des modifications qu'elle pourrait y apporter ne sera répercutée sur la variable qui a servi d'argument dans la fonction appelante.
De plus, la copie peut etre particulièrement gourmande en ressource (quant elle est autorisée), tant du point de vue du temps nécessaire à la copie que du point de vue de la mémoire utilisée.
Il fallait donc trouver le moyen d'éviter cette copie. En C, le seul moyen est de passer un pointeur. mais cela présente quelques désagréments (dont le moindre n'est pas le fait qu'un pointeur puisse être nul).
En C++, on peut disposer de références. Une référence n'est jamais qu'un "alias" de l'objet auquel elle fait ... (référence :aie: :?).
L'énorme avantage (en dehors de la syntaxe pour la manipuler) de la référence est qu'elle référence d'office un objet qui existe (elle offre une garantie de non nullité ;))
De plus, comme il s'agit d'un alias, toutes les modifications qui y seront apportées au niveau de la fonction appelée seront automatiquement répercutées dans la fonction appelante (tout comme tout ce qui arrive à devdeb91 t'arrive en réalité à toi ;)).
Le deuxième avantage que les références ont sur les pointeurs, c'est qu'elle respectent la constante: si tu informes le compilateur que la référence n'a absolument pas vocation à être modifiée, il est en mesure de s'assurer que tu ne fais rien qui tente de la modifier, et de t'insulter dans le cas contraire.
C'est la raison pour laquelle je passe la SMap sous la forme d'une référence car le but de la fonction est... de charger (donc de modifier) l'argument connu sous le nom de map, mais que je passe filename sous la forme d'une référence constante , parce qu'il n'y a strictement aucune raison que la fonction essayes de modifier le nom que je lui donne ;)
Au vu de toute ces explications tout se précise surtout sur la cas de la flemmardise:) je me douter de la signification. merci pour cette longue et très précise réponse
Une précision concernant size_t (qui ne concerne pas cette discussion, mais qui me semble importante). En plus de ce qui a déjà été dit, size_t résous certains problèmes dus au fait que la taille réelle des types natifs en c++ ne sont pas définis par le standard. En gros, lorsqu'on utilise size_t, on laisse la responsabilité à la SL des choix concernant la taille réelle (voire même l'encodade, si j'ai bien compris, sur certaines plateformes exotiques); ainsi on améliore la portabilité de notre code et on se simplifie la vie. Cette précision concerne les plateformes "inhabituelles", en particulier les devices destinés à l'embarqué.
En fait, il y a aussi l'aspect de l'inlining de la fonction qui n'est pas à négliger, surtout pour des fonctions très simples (comprends: des fonctions qui seront "traduites" par un nombre très bas d'instructions processeur).
Il faut savoir que, chaque fois que tu fais appel à une fonction non inline, il y a une séries d'instructions processeur qui fait en sorte de préparer l'appel à la fonction avant qu'elle ne soit appelée et de préparer le retour au "bon endroit du code" lorsque la fonction est terminée.
Ces instructions sont présentes et en nombre constant, bon an mal an, pour tout appel de fonction qui sera effectué.
Bien sur, cela ne correspond qu'à un très petit nombre de cycles d'horloge pour le processeur et donc à un délais particulièrement minime, mais il reste dommage de "perdre" ces cycles à attendre que le programme prépare l'appel et la sortie de la fonction quand on sait pertinemment que le corps de la fonction elle-même prendra un nombre de cycles d'horloge plus faible que le total de ces "préface" et "postface", surtout si c'est une fonction qui est destinée à être appelée en de très nombreuses occasion (parfois plusieurs dizaine de milliers de fois !!) par exemple.
En décidant judicieusement d'inliner certaines fonctions, on peut arriver à constater un gain non négligeable en terme de performances ;)
On peut presque considérer comme une pratique courante le fait d'inliner des fonctions comme:mais l'opportunité de ce faire doit malgré tout être évaluée à chaque fois ;)
- des accesseurs
- les opérateurs de comparaison
- certains opérateurs "mathématiques" (pour autant qu'il ne nécessitent pas forcément de copie de leurs paramètres)
- ...d'autres exemples pourraient me venir en tete plus tard ;)
euh petite autre :question:
les & entre const et filename ou SMap et map veulent dire la meme chose que si on faisait sa ou pasCode:void loadMap(SMap & map, std::string const & filename)
si non pourriez m'expliquer qu'est ce que cela changeCode:void loadMap(SMap map, std::string const filename)
Attention, si une référence const est fonctionnellement équivalente (par manque d'un meilleur mot) à un passage par copie, ce n'est pas du tout le cas d'une référence non-const! Celle-ci permet à la fonction de modifier l'objet passé en paramètre...
alors encore des question
1ere question :
n'est t'il ps dangereux d'avoir 2 fonction qui porte le meme nom comme dans l'exemple suivant
2eme question :Code:
1
2
3
4
5
6
7
8 /* et, comme on peut travailler en orienté objets, on peut rajouter * une fonction membre qui renvoie le caractère qui se trouve en X,Y ;) */ const unsigned char tileAt(size_t x, size_t y) const {return tiles_[y*cols + x];} /* et la même chose avec une Position (sait on jamais ;)) */ const unsigned char tileAt(Position const & pos) {return tileAt(pos.x(), pos.y());}
serais ce possible d'avoir une explication de ce bout de code car il sont appeler null part donc je n'est pas trouver leur utiliter
3eme question :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 /* Pour pouvoir utiliser la std::map, on a besoin de l'opérateur de *comparaison < * */ bool oprtator <(Position const & first, Position const & second) { return first.x() < second.x() || (first.x() == second.x() && first.y() < second.y() ); } /* et, tant qu'à faire, te l'opérateur de comparaison == (pour d'autres * usages ultérieurs, très certainement ;) */ bool oprtator ==(Position const & first, Position const & second) { return first.x() == second.x() && first.y() == second.y() ); }
le toFill du code suivant est de quel type et sert a quoi exactement ?
Code:
1
2
3
4
5
6
7
8
9
10
11 void loadMapList(std::map<Position, std::string> & toFill) { std::ifstream ifs("world.txt"); int x; int y; std::string filename; while(ifs>>x>>y>>filename) { toFill.insert(std::make_pair(Position(x,y),filename); } }
- Aucun problème en C++, la surcharge c'est fait pour ça. Par contre, il n'est pas normal que la seconde méthode ne soit pas const.
- Note déjà qu'il y a une typo dans le mot-clé operator. Ensuite, c'est la méthode traditionnelle en C++ pour les comparaisons de type perso: On redéfinit operator<, qui retourne un Booléen (true si le premier objet est strictement inférieur au second). Tu ne le vois appelé nulle part, mais std::map en fait un usage intensif (c'est un conteneur trié, ne l'oublie pas).
- Pour l'opérateur d'égalité, c'est à peu près la même chose.
- toFill est une référence non-const vers une std::map de Position vers string: La fonction loadMapList() va remplir cette std::map avec les données lues dans le fichier world.txt, qui indique pour chaque couple de coordonnées le nom du fichier texte contenant les infos de la map correspondante.
Mais ici cette ligne le insert il veut dire quoi parce que à première vue et suite à des recherches je sais que il y a std::insert mais la c'est insert tout court donc cela voudrai dire que c'est une fonction ou procédure et n’étant déclaré nul part je présume que c'est lier à une classe donc pouvais m'en dire plus ?
Code:toFill.insert(std::make_pair(Position(x,y),filename);
Là, tu en oublies jusqu'aux bases de la programmation objet: Il s'agit ici de la méthode std::map<keytype, valuetype>::insert!
Ah d'accord non je n'oublie pas mais je n'avais pas vue les méthodes que contiennait map du coup je ne faisais pas le lien merci
C'est "presque" la même chose:
Simplement, la première version (celle qui contient les &) va transmettre map sous la forme d'une référence et filename sous la forme d'une référence constante alors que la deuxième version (celle qui ne contient pas les &) va transmettre les arguments par valeur, et donc par copie.
Comme on souhaite explicitement que les modifications apportée à map dans loadMap soient répercutées sur la variable maplist dans la fonction principale, il faut à tout prix faire en sorte que map ne soit pas une copie, mais bel et bien un alias de la variable d'origine ;)
Non, il n'y a strictement aucun problème.
Tout ce qu'il faut, pour pouvoir profiter de la surcharge de fonctions, c'est une différence dans le nombre ou dans le type des paramètres attendus.
Tu auras d'ailleurs remarqué que l'implémentation de la deuxième version (celle qui prend une référence constante sur un objet de type Position) ne fait qu'appeler la première version (celle qui prend une valeur X et une valeur Y).
Et c'est voulu: le nom d'une fonction reflète le service que tu attends qu'elle te rende.
Le service que tu attends de la fonction tileAt est... de t'indiquer la tuile qui se trouve à une position donnée.
Que la position soit de transmise sous la forme d'une valeur pour x et d'une valeur pour y ou sous la forme d'un objet de type position ne change strictement rien, du moment que tileAt(5,10) et tileAt(Position(5,10)) renvoient bel et bien la même tuile ;)
L'idée est "simplement" de fournir un comportement qui renvoie la tuile qui se trouve à la position donnée, que tu disposes de l'abscisse et de l'ordonnée ou d'un objet de type Position car tu ne sais, a priori, pas si l'utilisateur préférera travailler avec une version ou avec l'autre ;)
Et c'est tout à fait légal en C++
Il y a, effectivement une erreur de typo, car ca devrait etreCitation:
2eme question :
serais ce possible d'avoir une explication de ce bout de code car il sont appeler null part donc je n'est pas trouver leur utiliter
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 /* Pour pouvoir utiliser la std::map, on a besoin de l'opérateur de *comparaison < * */ bool oprtator <(Position const & first, Position const & second) { return first.x() < second.x() || (first.x() == second.x() && first.y() < second.y() ); } /* et, tant qu'à faire, te l'opérateur de comparaison == (pour d'autres * usages ultérieurs, très certainement ;) */ bool oprtator ==(Position const & first, Position const & second) { return first.x() == second.x() && first.y() == second.y() ); }
(faut vraiment que j'arrête de poster passé une certaine heure :aie: :?)Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 /* Pour pouvoir utiliser la std::map, on a besoin de l'opérateur de *comparaison < * */ bool operator <(Position const & first, Position const & second) { return first.x() < second.x() || (first.x() == second.x() && first.y() < second.y() ); } /* et, tant qu'à faire, te l'opérateur de comparaison == (pour d'autres * usages ultérieurs, très certainement ;) */ bool operator ==(Position const & first, Position const & second) { return first.x() == second.x() && first.y() == second.y() ); }
Ce ne sont que des opérateur de comparaison (les mêmes que tu pourrais utiliser pour des codes comme if (i <5) ou if (j == 43) mais adaptés pour le type Position ;)
La classe std::map fait un usage intensif de l'opérateur < (plus petit que :D) pour tout ce qui a trait au tri, et l'opérateur ==, ben on risque d'avoir à l'utiliser de manière régulière à d'autres moments ;)
As tu vu l'image que j'ai postée dans ma première intervention :question:Citation:
3eme question :
le toFill du code suivant est de quel type et sert a quoi exactement ?
Code:
1
2
3
4
5
6
7
8
9
10
11 void loadMapList(std::map<Position, std::string> & toFill) { std::ifstream ifs("world.txt"); int x; int y; std::string filename; while(ifs>>x>>y>>filename) { toFill.insert(std::make_pair(Position(x,y),filename); } }
Tu auras remarqué que j'ai donné un exemple de "monde" dans lequel tes différentes cartes pourraient s'inscrire.
J'ai, pour chaque carte qui s'inscrit dans se monde, choisi une couleur, une largeur et une hauteur.
j'ai aussi indiqué les coordonnées auxquelles se trouve leur coin supérieur gauche.
Cette fonction permet de lire le fichier "world.txt" qui contient l'ensemble des cartes qui existent dans le monde et de remplir le tableau associatif qui met un nom de fichier en relation avec les coordonnées auxquelles se trouve le coin supérieur gauche de la carte en question.
Lorsque tu veux changer de carte (que ce soit en passant par une porte ou par un télé-porteur), les informations contenues dans la structure SWrap te permettent de déterminer la position du coin supérieur gauche de la carte vers laquelle tu veux te déplacer.
Grâce au conteneur associatif que la fonction aura rempli, tu pourra facilement trouver le nom du fichier qui contient les information relatives à cette carte.
Et, grâce à ce nom de fichier, tu pourras charger le dit fichier afin d'obtenir la carte en question ;)
toFill est le nom d'une variable de type std::map.
Il se fait que la classe std::map (qui est, comme je l'ai dit plus haut, un tableau associatif associant une clé à une valeur) propose, car cela fait partie d'un des services que l'on est en droit d'attendre de sa part, une fonction insert.
Ce code fait "simplement" appel à la la fonction membre "insert" de la classe std::map.
Tu trouveras plein d'informations utiles sur la std::map ==>ici<==;)
Merci pour ta réponse qui regroupe plusieurs questions comme à ton habitude tu m'éclaires plus surtout sur le fait d'avoir des fonctions de même nom ça je ne savais pas c'est une grande nouvelle pour moi pas très original dans le nom de mes fonctions merci encore j'espère que je n'aurais pas d'autre question bien que je sais que si car une ligne me bloque encore mais je vais pour l'instant laisser les gens se reposer parce que je pose beaucoup de questions :lol:
En fait, ce n'est pas mon habitude, mais comme je suis pour le moins fort occupé pour l'instant, je n'ai malheureusement pas forcément l'occasion de répondre "just in time" (ni même de lire toutes les réponses au moment où elles arrivent :aie:).
Du coup, ben, faut bien apporter une réponse à chaque question, et j'essaye de le faire correctement ;)
Non mais il n'y a pas de soucis c'est parfait comme sa car en général les question on toujours quelque point de ressemblance donc voila sa apporte beaucoup d'un coup .
par contre encore une question ici nous avons cette ligne
puis celle laCode:auto it= maplist.find(Position(0,0));
donc voila la question est la suivante it.second qu'est ce second car a première c'est pas une méthode donc je vois pas j'ai chercher il ne vient pas de std::map et j'ai rechercher avec auto rien non plus donc qu'est ce que c'est ?Code:loadMap(map, it.second);
En fait, ce code utilise une des nouvelles possibilités offertes par C++11.
Cela permet de faciliter l'écriture du code, mais il est vrai que, lorsque l'on ne comprend pas, ca ne facilite pas forcément la compréhension :aie:
Cette possibilité permet au compilateur de déterminer lui-même le type de l'objet en question (it dans le cas présent) et devrait être remplacé dans un code "pré C++11" par quelque chose du genre de
Cela t'aurait sans doute permis de comprendre que l'on obtenait un... itérateur (constant) sur les éléments de la map.Code:std::map<Postion, std::string>::const_iterator it = maplist.find(Position(0,0));
Un itérateur n'est, pour faire simple, qu'une abstraction qui permet de parcourir l'ensemble des éléments d'une collection sans avoir à se poser *trop* de question ;)
Le fait est que tous les éléments d'une map sont composés de deux choses : une clé, qui sert pour le tri, et une valeur qui correspond à la valeur "utilisable".Citation:
puis celle la
donc voila la question est la suivante it.second qu'est ce second car a premiere c'est pas une methode donc je vois pas j'ai chercher il ne vient pas de std::map et j'ai rechercher avec auto rien non plus donc qu'est ce que c'est ?Code:loadMap(map, it.second);
Cette association entre clé et valeur est représentée par une autre abstraction nommée std:: pair (car c'est... une paire composée d'une clé et d'une valeur).
L'itérateur que l'on récupère donc en invoquant la fonction membre find de la classe std::map représente donc, tout simplement, un objet de type std ::pair (c'est la raison pour laquelle l'insertion se fait en utilisant ce qui est renvoyé par make_pair ;)).
le type std:: pair, finalement, très simple: c'est une bête structure contenant deux données : first, qui sera du type attendu pour la clé et second qui sera du type attendu pour la donnée (car std::pair peut être utilisé à d'autres endroits sans forcément être une association "clé valeur", mais en n'étant ni plus ni moins qu'une association "première valeur, deuxième valeur" ;))
Bon, c'est une structure template (pour laquelle on ne sait pas à l'avance quels seront les types réellement utilisés) mais, adaptée à mon exemple ressemble à quelque chose comme
Si tu veux récupérer la position, tu peux utiliser un code proche deCode:
1
2
3
4
5
6
7
8 namespace std { struct pair { Position first; string second; }; }
et si tu veux récupérer la chaine de caractères qui représente le nom du fichier à charger, tu peux la récupérer à l'aide d'un code proche deCode:
1
2/* soit std::pair<Position, std::string unObjet = std::make_pair(Position(0,0),"gris.txt"); */ Position pos =unObjet.first;
Tu trouveras plus d'informations sur std:: pair ==>ici<==Code:std::string filename = unObjet.second;
C'est strictement équivalent à:
auto demande au compilo de trouver le type tous seul (en général utilisé pour les itérators, comme ici, car c'est long à écrire).Code:
1
2 std::map<Position, std::string>::const_iterator it= maplist.find(Position(0,0)); loadMap(map, it.second);
l'iterator ici représente un std::pair<Position, std::string> et fonctionne "comme un pointeur".
tu peux le déréférencer pour accéder à l'élément qu'il représente.
Une pair possède 2 attributs public first et second (qui sont la clef (Position) et la valeur (std::string) ici).
Il y a par contre une typo, il faut bien déréférencer l'itérator.
edit: bouh, trop rapide le mangeur d'eucalyptus ;)Code:
1
2
3 loadMap(map, it->second); // ou loadMap(map, (*it).second);
Encore une fois explication excellente rien à dire. Ah si seule chose c'est que j’avais quand même fait le rapprochement entre it et iterator mais après pour le reste je n'aurais certainement pas trouvé et pas aussi bien expliqué merci à toi maintenant que j'ai compris je vais pouvoir enfin toucher à tout ça en connaissance de ce que je fais :) .
Par contre la classe position peut être utilisée pour le déplacement tu joueur aussi ou tu penses que je devrais la laisser pour map ?
C'est tout l'avantage de choisir des noms "explicites" (bien que j'aurais sans doute pu choisir plus clair encore :D)Mais si, une fois qu'on connait quelque chose, il suffit d'appliquer la citation de ma signature ;)Citation:
mais aprés pour le reste je n'aurais certainement pas trouver et pas aussi bien expliquer
Avec plaisir ;)Citation:
merci a toi maintenant que j'ai compris je vais pouvoir enfin toucher a tout sa en connaissance de se que je fais :) .
Hé bien, je ne vais pas répondre à cette question (il faut bien que tu cherches un tout petit peu non :question:)...Citation:
parcontre la class position peu etre utiliser pour le deplacement tu joueur aussi ou tu pense que je devrai la laisser pour map
Mais je vais néanmoins te mettre sur la voie en te disant que le role de la classe Position est de représenter un couple de valeurs en abscisse et en ordonnée sur base d'un référentiel donné (que ce soit la carte du monde ou la carte du niveau).
Qu'en déduirais tu :question: Selon toi, où auras tu besoin d'un tel couple de valeurs :question:
J'en déduirai que étant juste un couple de valeurs il sont tout à fait apte à être utilisé comme coordonnées donc le déplacement se faisant à l'aide de coordonnées ceci convient totalement
Hehe, penses tout de même à faire un typedef pour savoir que cela représente un déplacement et non une position.