IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Projets Discussion :

OpenAWars - Advance wars like


Sujet :

Projets

  1. #21
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Pour l'avancement, je dois dire que moi aussi je suis lent, surtout que je suis en vacance

    Pour le script LUA, je n'ai jamais dit que je n'étais pas interessé, mais, mon problème, c'est que je ne vois pas du tout comment l'intégré dans un de mes projets. Il faudra que je fasse des essais à part.

    Vos images sont pas mal, quelles résolutions de sprite et de fenêtre utilisez vous?

    Et finalement, il semble que vous faites en script, ce que je vais bientot faire en C++ ... donc pour le moment, je ne vois pas l'avantage ( bientot, je me pencherai ( sans tomber j'espère ) sur votre code ).

  2. #22
    Membre averti
    Homme Profil pro
    Lycéen
    Inscrit en
    Novembre 2008
    Messages
    86
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2008
    Messages : 86
    Points : 355
    Points
    355
    Par défaut
    J'utilise des sprites de 20x20, et une résolution de 800x600. Donc oui le rendu est pas mal. Cela dit, c'est en grande partie du à la SFML et à son rendu matériel (en contrepartie, on se coupe des plate-formes les plus anciennes [genre antérieures à 2002/3, donc ça reste bon]).

    Citation Envoyé par LittleWhite Voir le message
    Et finalement, il semble que vous faites en script, ce que je vais bientot faire en C++ ... donc pour le moment, je ne vois pas l'avantage ( bientot, je me pencherai ( sans tomber j'espère ) sur votre code ).
    A court-terme, quasiment aucun (si ce n'est la satisfaction personnelle de la modularité ). Mais après, ça veut dire que on (soi-même ou d'éventuels "modders" peut faire facilement sans recompiler des "modules" à l'aspect et au comportement en jeu très différents. Sans oublier la facilité du "binding" (interfaçage) et les différences de performances absolument négligeables à de nos jours (y compris sur des plate-formes comme la Nintendo DS).

  3. #23
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Pour LUA, j'avais bien compris, mais ... bon ... voilà, je n'y vois pas un besoin :p ( je ne veux pas m'embêter non plus )

    -----

    La dernière fois je vous ai parlé d'un bug avec les hautes montagnes. J'avais proposé comme solution ( qui me semble tout à fait correct ) de dessiner tout d'abord une plain, puis après, de repasser le sprite de la plaine avec celui de la montagne, qui lui, n'aurai pas de fond ( enfin, le fond est transparant ). J'ai trouvé mieux \o/. Il me faut juste changer mon sprite de la montagne en mettant transparant les pixels de plaines dépassant 32 pixels de hauteur ( en partant du bas, 32 parce que c'est la taille d'un sprite normal ). Et hop, le tour est joué ( et je n'ai pas dessiné deux fois pour une tuile, donc c'est une optimisation ).
    Et ça fonctionne!

    Après j'ai réfléchi longuement sur les ombres. Je peux apporter une solution, mais je ne sais pas encore s'il elle me plait suffisament pour opter pour elle. On remarquera que dans le jeu original, les ombres sont toutes orientées sur la droite, cela peut être un indice, sachant que très souvent nous dessinons notre carte de gauche à droite.
    Ma solution actuelle est de:
    - Dessiner toutes les prairies ( comme on peut le remarquer, je reviens sur mon idée première avec les hautes montagnes ... )
    - Dessiner les autres tuiles, de gauche à droite donc. Mais maintenant, ces tuiles auraient un fond transparent, et en plus, une ombre qui ferai qu'elles ne respecteraient pas la taille "normales" de 32x32.

    Je pense que je verrai ce point un peu plus tard ( peut être qu'une fabuleuse idée viendra demain )

    Aujourd'hui, je me penche sur la réalisation de mon éditeur, ainsi que des premières interfaces utilisateurs.
    Effectivement, qui dit éditeur, dit outils pour créer une carte, et l'un d'entre eux, c'est la selection de la tile à copier sur la carte. Je rapelle que je veux une compatibilité si possible avec une console ( GP32X ) et que celle ci, n'a pas de "window manager" donc pas de barre des titres, donc pas de menus et encore moins de boutons.
    De plus, il n'y a pas de souris .

    Pour l'éditeur, j'ai crée un nouveau projet ( donc de nouveaux executables seront crées ) mais je réutilise beaucoup des fichiers du projet principal ( Pas fou! Je ne vais pas réecrire tout le code jusque là ).
    Le premier fichier qui change, c'est qu'à la place d'utiliser le main.cpp, je demande d'utiliser le main_editor.cpp. Celui ci, contient bien sur la fonction main() ( même si, sachez le, on peut indiquer au compilateur que le point d'entrer ne s'appelle pas main() ). En ayant fait deux projets différents, je peux indiquer que certains fichiers seront utilisé par l'un, et jamais pas l'autre. C'est ce qui se passe avec main.cpp / main_editor.cpp. L'un est visible uniquement pour la partie principale du projet, l'autre que pour l'éditeur.
    Cela va aussi me permettre d'avoir des fichiers de code source ( même si pour le moment, je n'en ai pas de prévu ) visible uniquement par un des deux projets.
    Deuxième possibilité, qui va peut être subvenir dans mon globals.h ( un peu fourre tout, ce fichier , les define c'est le mal, je tenais à le dire ), c'est que je vais pouvoir rendre invisible / visible des morceaux de code pour un projet ou pour l'autre ( même si j'utilise dans les deux le fichier )
    Voici un example du code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #ifdef EDITOR
    	#define MON_BEAU_DEFINE_QUE_POUR_L_EDITEUR	42
    #endif
    Et inversement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #ifdef GAME
    	#define MON_DEFINE_TRES_BEAU_QUE_POUR_LE_JEU	42
    #endif
    Comme vous l'avez deviné, la variable du préprocesseur 'EDITOR' ne va être définie que pour le projet de l'éditeur, et la variable 'GAME' que pour le projet du jeu.

    Finalement, j'ai trouvé très rapidement à quoi cela va me servir. Je vais rajouter du code dans ma classe Map afin de pouvoir générer une carte sans même charger le fichier, et puis je vais autoriser le changement de Tile à la volée ( ou presque ) ainsi que la sauvegarde. Tout cela n'a pas besoin de se retrouver dans le binaire du jeu. Du coup je vais protéger ce code avec les conditions du préprocesseur.
    La prochaine chose à faire attention est à la génération de documentation ( entre autre ). En fait, doxygen offre tout plein de possibilités, notamment la possibilité d'utiliser un préprocesseur. On peut remarquer que j'ai rajouté deux fichiers: 'main.h' et 'main_editor.h'. Le premier est inclut dans le projet du jeu, le deuxième dans celui de l'éditeur. En fait, ces fichiers, pour le moment, ne sont uniquement là, que pour la documentation ( la page d'index du projet ). On remarquera aussi que je les ai protégés avec les conditions du préprocesseur.
    On remarquera aussi que comme j'utilise les blocs:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #ifdef EDITOR
     
    #endif
    Le code qui est ainsi présent que dans l'éditeur, n'est aussi présent que dans la documentation de l'éditeur.

    Maintenant que tout est prêt pour l'éditeur, on peut travailler sereinement.

    On remarquera des ajouts dans la classe Map. J'ai rajouté un nouveau constructeur, pour avoir une carte construite selon la taille passée ( elle est remplie par défaut de plaine ).
    Note: Il n'y a pas encore de limite de taille

    J'ai aussi rajouté une fonction pour modifié le type de la tuile à une position donnée. C'est plutot asseze important pour un éditeur .
    Dans cette fonction, il y avait un piège a évité ( je pense ne pas être tombé dedans ( je suis un peu prétentieux ). C'est à propos de la mémoire. Il faut bien pensé à détruire la Tuile que l'on va "recouvrir" par la nouvelle.

    Et, je pense ( j'espère ? ) que vous l'attendiez, la dernière fonction ajouté, est la fonction de sauvegarde de la carte. Le principe est relativement le même ( juste inversé ) que celui de la fonction 'parser()' de la Map.
    De toute façon, la fonction est plutot simple ( comparer à l'analyseur, qui lui, doit éviter de planter des le moindre écart d'espace )
    Ce que j'aime énormément lors du test d'une fonction de sauvegarde, c'est que pour faire un premier test, il n'y a juste qu'à charger un fichier valide, et lui demander de la sauvegarder. Et puis on compare, et c'est fini \o/ ( ou encore, on demande à charger le fichier sauvegardé par son propre programme ( mais pour ça, il faut avoir confiance en son code ).
    Certains pourront dire, que l'on pourrait surcharger les opérateurs << et >> pour ce genre de travail.

    J'ai ajouté une nouvelle fonction dans la classe Keyboard afin de pouvoir vérifié n'importe quelle touche que je veux (en passant la touche pas paramètre). Ce qui a provoqué aussi l'ajout d'un membre, qui est la taille du tableau, afin d'éviter de méchantes ( ) erreurs lorsque l'on passe n'importe quoi à la fonction.

    Maintenant, le réel travail commence. L'interface utilisateur ( et je déteste ça ).
    Le but est de créer une barre, sur le bas de l'écran, qui permette à l'utilisateur de choisir la tuile à copier sur la carte. Simple dans le principe, un peu moins dans la réalisation ( j'ai mis presque une après midi à réussir le foutu carroussel ( je crois que cela s'appelle comme ça ).

    La première partie était de faire une bande noire semi transparante. Pour éviter les problèmes de redimensionnement je ne voulais pas utiliser une image externe, mais généré à la main la bande. Dans la théorie, ce n'est pas dur, dans la pratique, je n'ai toujours pas compris comment fonctionne SDL_SetAlpha().
    D'après moi, la fonction aurait du changer la composante alpha de mon sprite en ce que je voulais, mais je n'ai jamais réussi à lui faire faire . ( Faudra que je regarde le code de la SDL un jour ... )
    Du coup, j'ai changé mon sprite à la main ( Nah! ).

    Après, dans cette interface utilisateur, il y a des petites animations. Premièrement, lorsque l'utilisateur appuie sur une touche, l'interface apparait. S'il appuie sur une autre touche elle disparait. S'il appuie sur gauche ou droite, il change le sprite sélectionné.
    Ma classe défini un enum, pour énuméré les différentes animations en cours:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    enum TileBarState
    	{
    		TBS_Opening,
    		TBS_Opened,
    		TBS_MoveLeft,
    		TBS_MoveRight,
    		TBS_Closing,
    		TBS_Closed
    	};
    Et comme beaucoup l'auront compris, je fais une petite machine à état. Celle ci sert à se retrouver dans les actions ( notamment, je n'ai qu'un update, qui va faire les animations, suivant l'état en cours ).
    Par exemple, lorsque je suis dans l'était Opening, ma fonction update() va juste changer la position sur l'axe des Y de l'interface.
    La méthode draw() utilise aussi se principe, mais d'une façon bien moins approfondie:
    - Si je ne suis pas fermé, je dessine la bande semi transparante
    - Si je suis ouvert, ou alors que je me déplace sur la gauche ( ou droite ) alors je dessine aussi les icones.

    Voilà comment on peut garder les choses simples ( notamment, on peut remarquer certains messages des utilisateurs du forums, que lorsqu'ils veulent faire une animation, il bloque toute leur application tant que l'animation n'est pas fini ).
    La machine à état, permet de sortir de l'animation, et d'y revenir en sachant toujours ce qui doit être fait et aussi ce qui est possible de faire.

    Par exemple, j'ai une méthode open() qui va enclencher l'animation d'ouverture ( apparition de l'interface ), celle ci, vérifie l'état, pour savoir si l'interface n'est pas déjà ouverte. Cela m'évite des embêtements, car je bloque directement les possibilités des utilisateurs ( l'utilisateur ne doit pas être libre! ).

    Après, les états apportent aussi une autre contrainte, pour chaque état / animation il faut une condition d'arrêt ( pour, dans mon cas, retourner dans un état neutre ( closed ou opened ) ). Je dis cela, car par exemple, si on enclenche notre MoveLeft, et que l'on ne sait pas quand s'arrêter, et bah l'interface va toujours aller sur la gauche. Dans mon cas, j'utilise des compteurs. Celui est mis à un nombre, qui est en fait, le nombre de pixel que vont parcourir mes tuiles. Tout en décrémentant cette variable, je vais déplacer mes tuiles, ce qui d'une part, va faire mon animation pile poil le bon déplacement, et que en plus cela va s'arrêter.

    Finalement, il y avait cette histoire de carroussel. J'ai mis plus de temps que prévu, car je ne gardais pas la position de chaque tuile, mais juste de la bande de tuiles. Du coup, en allant en arrière, je crée des bugs graphiques lamentable. Après plusieurs reflexions, j'ai décidé que chaque tuile aller avoir sa position, et que chaque tuile serai déplacé dans la méthode update ( ainsi que la téléportation des tuiles ( le fait que ça boucle ) ). Cela alourdit considérablement la méthode update, mais cela allège la méthode draw(). Je pense que ma solution finale n'est pas trop sale, mais je sais qu'elle est loin d'être parfaite.
    Mais ça marche \o/.

    Enfin, pas tout à fait, il y a un bug, car j'ai un décalage entre la position du curseur, et la tuile qui ira dedans. Ce qui crée un petit défault graphique dans l'animation ( quand je vous dit que les interfaces utilisateurs, c'est pourri ).
    Je corrigerai cela dans la prochaine version.

    En parlant de prochain version, il faudre que je:
    - Corrige les bugs ( 2 de connus ( le premier est graphique, et c'est celui que j'ai cité plus haut ; le deuxième était une stack corruption sur la carte ... mais je n'ai pas encore compris pourquoi ... , surtout qu'elle ne se reproduit pas, et jamais vu dans l'éditeur ) )
    - Amélioré le code de la barre
    - Fasse un système de temps virtuel ( vous allez voir, c'est génial ce truc )


    Note: J'ai mis les captures d'écrans qui montrent:
    - La supression du bug d'affichage avec les sprites haut et la recouvrement
    - La TileBar
    - La TileBar ( encore car j'en suis content :p ) ( et aussi pour montrer que l'on peut aller de gauche à doirte ( et de droite à gauche )
    - Une carte crée avec l'éditeur et chargé dans le jeu ( bon, ok, seul le titre vous indique que c'était dans le jeu, et encore )
    Mais il faut aussi voir la carte m3.txt ( dans le dossier data/maps ) qui a été crée avec l'éditeur.
    Images attachées Images attachées     

  4. #24
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Me revoici, mais cette fois pour de légères modifications dans le code.

    Je ne suis pas sur de moi, et encore moins lorsque dans mon code, le fait de commenter des conditions pour le préprocesseur me cause une "stack corruption". Et oui! c'est l'erreur dont j'avais parlé dans mon message précédent.
    J'avais un grand doute sur le pourquoi d'une telle erreur, dès le début, je me doutais que c'était à cause du code de l'éditeur. Après, Visual Studio me dit gentiment que c'est dans la carte. Certes, mais qu'est ce qui a vraiment changer. D'autant plus que l'éditeur ne plante jamais O_o.
    Et bah, il semblerai que tout le problème vienne de ( roulement de tambour ):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #ifdef EDITOR
    	std::string theme;			/*!< Graphical theme for the map */
    #endif
    Que se passe t'il dans Visual Studio pour que cela plante. Surtout qu'aucun problème de compilation n'est reporté.

    En fait, je n'ai pas trouvé l'erreur, malgré qu'il me semble qu'elle est disparue. Cette fois je ne pourrais pas expliquer ce qui se passe.
    Si je change le nom de la variable ( pour m_theme, selon une notation hongroise pour le C++ ), le problème subsiste. Je dis ça, car un moment je croyais que c'était du à un bug entre les variables locales des fonctions ( elles aussi appelées 'theme' ) et la variable de la classe.

    Mais finalement, pour une autre raison, j'ai changé le code comme suit:
    - Tous les ajouts pour l'éditeur qui ont été faits dans la class Map, sont maintenant dans une classe MapEditor, qui hérite de Map. Cela me permet, entre autre, d'avoir un code plus beau ( plus lisible ), ainsi qu'une séparation plus nette entre le jeu et l'éditeur.
    De plus, cela enlève mon problème \o/

    Maintenant, il faut que je m'attaque à mon problème avec cette facheuse barre de tuiles. Car, comme dit précédemment, j'ai toujours un problème avec l'animation. Enfin, ce n'est pas vraiment l'animation, mais plus la tuile qui devrait être au centre du curseur. De plus, ce problème est trop visible lorsque je suis en 640 x 480.
    En solution, j'ai l'impression que je n'ai fait qu'une suite de petit HACK minable. L'effet est meilleur qu'avant, mais il n'est pas parfait.

    En troisième position, nous avons l'affichage des tuiles sur la carte. Le petit changement, c'est que maintant je dessine toujours une tuile de pleine en dessous de toutes les autres ( qui ne sont pas de la mer, pour ne pas perdre trop de temps la dessus ).
    Mais, comme ne mode debug, c'était déjà en train de ralentir tout le processus, j'ai décidé d'ajouter un flag dans la definition de Tile pour savoir si je devait vraiment dessiner cette tuile. ( Je me demande si je n'ai pas un vrai problème de performance... enfin, de toute façon, y a déjà plus de 60% de la performance qui passe dans le logger )
    On voit que cela marche très bien, comme sur la capture d'écran. La prochaine fois, je me soucirai des ombres, qui maintenant, ne devrait plus être un soucis.

    @fearyourself: Merci pour l'aide à propos du SDL_SetAlpha(), mais même en ajoutant un joli SDL_SRCALPHA dans le SDL_CreateRGBSurface(), ma surface ne prend pas en compte le changement demandé dans le SDL_SetAlpha().
    Bien sur, c'est un truc que je ne peux toujours pas expliquer. Enfin bref, ce n'est pas trop grave pour le moment, puisque je peux contourner :p.
    Images attachées Images attachées  

  5. #25
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Objectif du jour:
    - Faire toute la logique interne de l'éditeur.

    Je suis aussi un peu désolé pour ce qui attendais d'en connaitre plus sur cette histoire de temps virtuel. Pour le moment, je vais dire que je peux m'en passer ( même si d'après moi, le plus tot il est en place, le mieux c'est ).

    Vous avez vu? J'entre directement dans le sujet, sans plus de présentation.
    J'explique tout de même un peu ce que j'entends pas la logique interne de l'éditeur:

    Par exemple, lorsque vous voulez mettre une route, par défault celle ci est horizontale ( comme présenté dans la barre des tuiles ). Simple jusqu'à présent car nous ne mettons que l'identifiant de la route horizontale.
    Mais si l'utilisateur rajoute une route, sur la case d'au dessus de notre première route, à la place d'avoir une route horizontale, il faudra mettre une route verticale. Et encore plus, si notre cher utilisateur veut avoir un croisement ( soit quatres routes autour d'une case route )...
    La logique interne de l'éditeur va définir toutes ces petites règles qui font que le monde d'OpenAWars est cohérent.

    Je vais définir toute cette petite logique dans des fonctions privées de la classe MapEditor. Chaque fonction va être appelé lors de l'appel de setTile selon la tuile que l'utilisateur veut mettre. Certain me diront que je devrais le faire en LUA . Cette fois, je dis que cela me semble une bonne idée, et que ce sera une possible évolution du programme, dans un avenir plus ou moins proche.

    Mais tout d'abord, j'ai décidé de changer un point du rapport entre mes tuiles et ma carte. Effectivement, les tuiles contenaient le pointeur le l'AnimatedSprite a utilisé. Ceci était complètement nul . Plus sérieusement, déja ( malgré que j'ai un avantage avec mon SpriteManager ) j'avais un AnimatedSprite par tuile, alors que techniquement, il n'en faut qu'un par image, tout simplement.
    Deuxièmement, un tel changement ( chargement, et stockage des AnimatedSprite dans la Map ) permet de ne plus avoir ce très moche:
    Surtout que dans le C++ il est dit d'éviter les pointeurs.
    Maintenant, ce n'est plus qu'un:
    Ce qui le rend plus facile d'utilisation, et bon, c'est presque plus beau.

    Finalement, ce changement était nécessaire, car lorsque la logique interne de l'éditeur est appliqué, il y a des cas où, les tuiles d'à coté de la tuile changer par l'utilisateur, doit aussi changer de type. Et la! bah j'allais avoir un problème. Effectivement, sans mon changement, il fallait que je désalloue de la mémoire, que j'en réalloue ( donc source de bug ) mais aussi que je sache quel type correspond à quel fichier ...

    Du coup, la solution que j'ai envisager, c'est d'ajouter un std::map dans ma Map ( non ce n'est pas un jeu de mot ) afin d'associer les TileType à des AnimatedSprite.
    Grace à ça, je n'aurais encore moins de problème avec mon intégration des thèmes envisager, et en plus, je peux, dès que le veux, demander à ma carte quel est le sprite à utiliser pour un type de tuile donné.
    Avec l'ajout de ce membre, j'ai ajouté une fonction de chargement des sprites ( pour remplir le std::map ) j'ai enlevé mon triple pointeur ( comme dit au dessus ) et j'ai enlevé le champs contenant l'AnimatedSprite de la structure Tile. Du coup, ma fabrique de Tile est plus simple.
    Et finalement, j'ai viré un truc que j'avais ajouté juste à la version précédente, qui était la tuile de fond ( prairie, habituellement ) qui servait à mes fondus \o/.

    Le deuxième point que j'ai modifié avant de commencer à faire la logique interne, c'est la fonction getTile de la carte. En effet, elle avait été écrite un peu trop rapidement. Notamment, si la position passée, dépassait la taille du tableau, une erreur de segmentation se serait certainement produite. Du coup, maintenant, je vérifie la position passée, et si celle ci n'est pas valide, je retourne une Tile avec un type de tuile TT_Invalid ( qui est donc une nouvelle valeur dans mon énumération ).
    Grace à cette nouvelle vérification, le code de la logique interne va être un peu plus simple ( notamment, plus de besoin de vérifié si nous sommes sur le bord, et si on y est, bah le type retourné n'aura aucune incidence sur les conditions ( vous allez voir )

    Maintenant que je peux enfin commencer le code de la logique, je me pose une question sur le design. Comment faire pour que cela soit propre et court. Je veux dire que par là, si l'utilisateur place une plaine en plaine mer, les cases alentours doivents changer pour devenir cote. Et cela n'arrive pas seulement lorsque l'on place une plaine, mais aussi lorsque l'on place de la forêt, une montagne, une route. Et puis il y a pleins d'autres cas ..., la question c'est, est ce que je dois géré tout les cas. Ce serait bien .
    Tout d'abord, je vais remplir la structure Tile, pour avoir plus d'indications sans avoir à vérifié à chaque fois le type de la tuile, notamment, nous savons si la tuile est une tuile de route, car les routes sont un cas séparé des autres tuiles.

    Mon principe, c'est de faire plusieurs sous fonctions, pour réduire au maximum les grandes fonctions, et surtout rendre le debugguage plus facile.
    Toutes les sous fonctions sont privées dans la classe MapEditor.
    Au final, j'ai crée le cheminement suivant:

    - setTile(position,type);
    Selon le type
    - setType(position);
    Change le type de la tuile à la position T
    Vérifie si les tuiles alentours sont corrects (ainsi que pour les cas spéciaux, la tuile courante)
    - checkCoherencyAround()
    Qui appele la fonction checkCohenrency() pour les 4 tuiles autour (donc pas de diagonales)
    - checkCoherency()
    Selon le type de la tuile courante
    - checkCoherencyForType()
    ... Défini les règles pour ce type de tuile. Les tests sont faits ici.

    Comme ceci, je penser avoir éviter d'avoir une très grande fonction, ainsi que d'avoir à faire des copié collé. Bien sur je ne dis pas que c'est la meilleure solution, et j'attends même d'avoir vos remarques.
    Pour les routes, cela fonctionne très bien, et vous pouvez le voir sur la première capture d'écran.
    La fonction de test pour les routes, fait les choses suivantes:
    - Compte le nombre de route autour de la case à tester
    - Lève un flag, si c'est à gauche ou à droite ( c'est horizontal )
    - Lève un flag, si c'est en haut ou en bas ( c'est vertical )
    - Lève un flag pour chaque direction.

    Après, selon le nombre de route autour nous testons les flags.
    Pour zero routes:
    - Nous ne faisons pas de test, nous mettons une route horizontale ( défaut )
    Pour 1 routes:
    - Check des flags pour savoir si la route est horizontale ou verticale, puis nous collons la route
    Pour 2 routes:
    - Check si seulement un des deux flags horizontal / vertical est levé, ce qui indiquera que la route est en ligne droite, et nous appliquons donc un type de route droite.
    - Sinon, on test les flags indépendant de direction, pour savoir quel coin utilisé
    Pour 3 routes:
    - Test des flags de direction, pour savoir quel T nous allons appliquer.
    Pour 4 routes:
    - On applique directement la route en croix

    Voilà. Je sais très bien, que j'utilise plus de flags qu'il n'en faut, mais bon, cela rend le code plus lisible. Malgré tout, les flags pour savoir si c'est horizontal ou vertical, peuvent être enlevé ( totalement ) sans perte de lisibilité ... mais là, c'est un choix. Naïvement, je pourrai dire que cela permet de faire des tests en moins , ce qui est vrai, mais trop peu significatif.

    J'arrive maintenant, à la partie la plus dure de la logique interne de l'éditeur. Les mers! Sérieusement, ce fut de la folie, notamment la cause de ma lenteur à vous envoyer des nouvelles ( mais pas seulement ). Les mers, c'est une vingtaine de tuiles différentes! Et il faut géré le tout pour que cela fasse beau, en ayant qu'un éditeur qui indique que l'utilisateur veut poser une case de type 'Mer'.
    Ce n'est pas impossible, loin de là, mais mon code est un ensemble de conditions qui sont un peu compliqué à relire ( personnellement, je n'aime pas ça, mais je ne connais pas ( encore? ) d'alternative.
    J'ai gardé la même logique de conception que pour les routes:

    - setTile(position,type);
    Selon le type
    - setType(position);
    Change le type de la tuile à la position T
    Vérifie si les tuiles alentours sont corrects (ainsi que pour les cas spéciaux, la tuile courante)
    - checkCoherencyAround()
    Qui appele la fonction checkCohenrency() pour les 8 tuiles autour -> Ce qui veut dire que j'ai mis à jour la fonction, et que pour les routes, on regarde aussi les huit tuiles autour, mais sans aucune coincidence.
    - checkCoherency()
    Selon le type de la tuile courante ... qui pour ce cas, sera de la mer
    - checkCoherencyForSee()
    ... Les tests

    Alors, cette fonction qui contient tout les tests, ce base sur le même principe de drapeau que pour les routes. Mais comme vous l'avez deviné, si on a besoin de regarder la cohérence des huit tuiles autour, cela veut dire, que l'on doit aussi regarder les huit tuiles autour de la tuile ou nous sommes.
    Enumération des drapeaux:
    - Compte le nombre de mers autour de la case à tester ( horizontalement et verticalement )
    - Compte le nombre de mers autour de la case à tester ( que celles qui sont en diagonales )
    - Lève un flag, si c'est à gauche ou à droite ( c'est horizontal )
    - Lève un flag, si c'est en haut ou en bas ( c'est vertical )
    - Lève un flag pour chaque direction. ( soit 8 flags ).

    Du coup, on a un grand switch selon le nombre de mers ( horizontalement et verticalement )
    Si 0:
    - On pose une sorte de mer avec des cotes tout autour. Les diagonales n'ont pas d'importance \o/
    Si 1:
    - On regarde quel est la case adjacente qui a de la mer, avec les drapeaux de direction, et on applique la fin de mer convenable selon la direcition. Les diagonales ne sont toujours pas importantes.
    Si 2:
    - Cela peut être une ligne, on regarde comme pour les routes, si nous avons une ligne horizontale, ou une ligne verticale. Selon ces cas, nous définissons la tuile correspondantes.
    - On vérifie chaque combinaison pour les coins
    - Si le case en diagonale du coin est une mer, alors nous mettons un coin pour les mers
    - Sinon, on met un coin pour les mers qui se prennent pour des rivières ( enfin graphiquement, c'est ce que cela donne )
    Si 3: ( le plus compliqué des cas, je pense )
    - Je vérifie pour chaque combinaison de trois directions ( horizontale et vertical ) ( soit pour chaque combinaison de coin, mais avec trois flags )
    - Je met la tuile qui est un T
    - Si il y a plus d'une tuile en diagonale:
    - Si c'est une tuile en diagonal, qui se pose à coté de l'avancé de mer ( la barre verticale du T ) ( y a deux coins à vérifier ), alors je pose un T différent, selon ce que j'ai trouvé
    - Si c'est les deux cases à coté de notre barre verticale du T qui sont en mers, alors je pose juste un cote qui borde tout la longueur ( soit la longueur horizontale du T, pour reprendre mon image )
    Si 4:
    Ici j'ai utilise un switch selon le nombre de tuiles en diagonales:
    Si 0: C'est un X ( comme pour les routes )
    Si 1: Selon celui qui est en mer ( dans les diagonales ), je poserai une tuile correspondante
    Si 2: Selon lesquelles coin qui sont en mer, je poserai la tuile correspondate ( qui contient encore moins de terre que les tuiles précédantes )
    Je vérifié aussi pour les diagonales de terres ( qui forment les détroits ), et ils ont aussi des tuiles ( 2 pour deux diagonales ) différentes ( quand je vous avez dit qu'il y avait tout un tas de cas )
    Si 3: Pour chaque coin ( selon trois directions ) je pose la tuile correspondante
    Si 4: Trop simple cette fois, cela correspond à une mer complète ( toute les tuiles autour, c'est de la mer )

    Voilà. Je met en ligne tout ce code, car finalement, je préfère l'avoir de sauvegarder quelque part, car ça commence à faire pas mal de boulot. Vous pouvez voir sur les captures d'écran que cela fonctionne pas trop mal ( même plutot bien ).
    Les petits décalage de pixel sont du à mes images toutes pourris, mais je n'ai pas de graphistes ( et je n'en suis pas vraiment un :p ), mais là, il n'y a pas de bug de carte, comme j'ai pu en voir lors du codage .

    Remarque ( surtout pour moi même ) : J'aurais du appeler mes sprites, ainsi que les noms des énumérateurs par les points cardinaux au lieu de:
    T -> Top
    B -> Bottom
    L -> Left
    R -> Right
    Je ne vais pas faire le changement, car mon code fonctionne ( enfin ) et qu'une telle modification risque d'apporter plus de bugs qu'autre chose.
    Images attachées Images attachées    

  6. #26
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Beau boulot. Oui c'est toujours chiant ce genre de code. J'ai une tendance à laisser l'utilisateur le soin de faire la carte joliement et laisser le code de gestion être plus simple.

    Moi je laisse ca à l'utilisateur, un jour je ferai peut-être un éditeur, mais je suis encore loin d'un jeu jouable donc...

    Une idée pour te rendre la vie la plus simple serait d'avoir en fait un dictionnaire de transformations :

    Tu as ensuite une map : Tuile départ -> Transformation si les cas sont tels.

    Et ta transformation est définie par une carte 3x3 avec soit les types soit directement les tuiles.

    Ensuite, lorsque tu poses ta tuile, tu passes en revue les transformations et tu fais pareil pour les tuiles à côté (s'il y a un changement, tu continues... attention aux boucles...)

    Mais ta solution fonctionne, c'est juste un code long et embêtant à écrire/lire/corriger...

    Jc

  7. #27
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Merci

    Moi je ne laisse rien à l'utilisateur car c'est un idiot, est tout ce qu'il va faire c'est planté mon beau programme ( moi je le connais l'utilisateur , il a l'air d'être inoffensif, mais dès que l'on a le dos tourné il va se plaindre que les textures ne se chargent plus, car il a déplacer le dossier des données )
    Et puis, le problème n'est pas vraiment de tout laisser faire à l'utilisateur, mais que, nous sommes sur une interface qui se veut simple, et du coup, dans le TileBar, il n'y a qu'une dizaine de tuile différentes, alors que dans le jeu, il y a 80 tuiles différentes.
    Ce qui amène à un problème:
    - Le TileBar avec 80 tuiles, serait trop pénible à utiliser, entre le temps pour trouver la bonne tuile et changer de tuile à chaque fois ... c'est vraiment pénible
    - On perd toute la facilité de l'éditeur, autant que je donne juste comment faire mon fichier de carte avec l'éditeur de texte
    - Et puis, comme nous avons que dix tuiles dans le TileBar, il faut que le jeu sache quelle tuile mettre pour chaque condition.

    Finalement, l'éditeur n'est pas que pour l'utilisateur, mais aussi pour moi, afin d'avoir des cartes ... certes, avoir fait l'éditeur avant le jeu peut sembler idiot ...

    Pour le dictionnaire de transformation, c'était une de mes idées, en fait, sauf que je voulais faire juste deux fonction:
    - Une pour faire la transformation ( "vers le haut" ) soit une route verticale, peut devenir un T
    - Une pour faire la transformation ( "vers le bas" ) soit une route en X peut devenir un T

    Mais, je trouvais cela vraiment beaucoup plus compliquer que ma solution actuelle, et puis votre dictionnaire, cela revient à faire ce que j'ai fait ( encore heureux ) mais en spécifiant tout les cas ... à la main ? O_o

    Sinon, j'ai pensé hier ( oui! je pense ), que pour simplifier le code, on pouvait faire un système de classe / foncteurs, avec un lourd héritage, et comme ça on aurait une classe qui vérifierai les routes, une les mers, et toute ces classes seraient appelé par le MapEditor. Mais je crois que pour que ça soit très bien fait, les connaissances en C++ que j'ai soit un peu limite.

  8. #28
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Aujourd'hui c'est encore de la logique interne de l'éditeur ( y avait du rab ).
    Je vais essayer de ma m'étendre comme dans mon précédents message... surtout que cette fois, il n'y a rien de nouveau, juste que je complète ce que j'ai fait avant.

    J'ai commencé par les rivières. J'ai une raison . Pour les rivières, c'est facile, en fait, c'est exactement comme les routes ... enfin pas tout à fait, mais c'est très proche.
    Par rapport aux routes, les rivières ont deux cas spéciaux:
    - On ne peut pas placer une rivère si la case est déjà entouré par un coin de rivières, ou si la case et une case de mer.
    - Il existe une tuile spécial pour les cases allant de la rivière à la mer, les 'embouchures'. Ces cases, sont des cases de rivières, mais aussi des mers... ( cela a une implication pour la logique interne de l'éditeur, sinon, si ce n'était qu'une rivière, alors les cotes de la mer planterai )

    Mais tout d'abord, je dois rajouter un drapeau dans la structure Tile, pour que je sache rapidement que la tuile est une rivière.

    Une fois cela fait, j'ai juste fait un copier - coller ( bouh! ) de mes tests pour la cohérence des routes. Bien sur, j'ai changer les type des tuiles à mettre dans la carte.
    Et bien sur, cela fonctionne ... , mais, il reste les deux cas spéciaux à implémenter.

    Le premier amène à l'insertion d'une possibilité de bloquer les tuiles que veut mettre l'utilisateur. Pour l'instant, je ne vais pas faire de grand changement, je vais juste préparé le terrain. Ce qui veut dire, que certes l'utilisateur sera bloqué, mais, il n'aura aucun répère visuel de cette notion.
    Pour que ce blocage ne soit pas pénalisant par la suite, je rajoute une nouvelle fonction dans ma classe ( qui commence à ce faire très grande ), afin de tester si on peut mettre une tuile d'un type passé en paramètre à cette endroit.
    C'est fonction s'appelle ( roulement de tambours ):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    //! Test if this type tile can be put at the position
    /*!
      \param position the position where the tile would be put
      \param tileType the type of the tile that the user wants to put
    */
    bool testTile(const UVec2& position, const TileType tileType)const;
    ( Oui, aujourd'hui, je vous montre même les commentaires ( pas sur que mon anglais soit très bon ) mais au moins, vous aurez vu comment on documente une fonction, avec doxygen ( voir les exemple de doxygen est une bonne chose, tout de même ) )

    Actuellement, je ne rajoute qu'une fonction, qui fera les tests ( je sais qu'il n'y en aura pas beaucoup ), mais en théorie, il faudrai pour chaque type de tuile une fonction interne où seraient implémentés les tests ( pour que cela soit plus clair ).
    Voici la fonction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    bool MapEditor :: testTile(const UVec2& position, const TileType tileType)const
    {
    	switch ( tileType )
    	{
    	case TT_Plain:
    		return true;
    		break;
    	case TT_Tree:
    		return true;
    		break;
    	case TT_Mountain_1:
    	case TT_Mountain_2:
    		return true;
    		break;
    	case TT_Road_H:
    		return true;
    		break;
    	case TT_See:
    		return true;
    		break;
    	case TT_River_H:
    		{
    			// Check if it's a see tile
    			if ( this->getTile(position).isSee )
    			{
    				return false;
    			}
     
    			// Check Top Left corner
    			if ( this->getTile(UVec2(position.x-1,position.y)).isRiver && // Left
    				 this->getTile(UVec2(position.x,position.y-1)).isRiver && // Top
    				 this->getTile(UVec2(position.x-1,position.y-1)).isRiver ) // TopLeft
    				 return false;
     
    			// Check Top Right corner
    			if ( this->getTile(UVec2(position.x+1,position.y)).isRiver && // Right
    				 this->getTile(UVec2(position.x,position.y-1)).isRiver && // Top
    				 this->getTile(UVec2(position.x+1,position.y-1)).isRiver ) // TopRight
    				 return false;
     
    			// Check Bottom Left corner
    			if ( this->getTile(UVec2(position.x-1,position.y)).isRiver && // Left
    				 this->getTile(UVec2(position.x,position.y+1)).isRiver && // Bottom
    				 this->getTile(UVec2(position.x-1,position.y+1)).isRiver ) // BottomLeft
    				 return false;
     
    			// Check Bottom Right corner
    			if ( this->getTile(UVec2(position.x+1,position.y)).isRiver && // Right
    				 this->getTile(UVec2(position.x,position.y+1)).isRiver && // Bottom
    				 this->getTile(UVec2(position.x+1,position.y+1)).isRiver ) // BottomRight
    				 return false;
    		}
    		return true;
    		break;
    	default:
    		assert(0);	// If we are here, something goes hardly wrong, or I have forgotten to code something
    		break;
    	}
    }
    Dans le code on remarquera le cas avec le default, qui est constitué que d'un assert(0). L'assert(0) bloque le programme immédiatement ( que en mode débug ) et lorsque le debuggueur est présent, a le même role qu'un point d'arrêt. Du coup, si un jour j'oublie de faire un morceau de code ( un rajout d'un type de tuile, dans dix ans ou autre ) et bah le programme "plantera" directement et me dira que je suis bête d'avoir oublié ça . Et puis ça supprime les warnings du compilateur pour tout les case non traités ( bien que je n'ai vu encore aucune warning de ce genre sous Visual Studio ... GCC le fait pourtant ).
    Qui est bien sur appelé au début de la fonction setTile().
    Cela fonctionne \o/ ... comme vous pouvez le voir dans la première capture d'écran. Je n'ai jamais laché la touche qui insère les tuiles de rivière... donc voit bien que le test fonctionne bien

    Maintenant, il faut que je rajoute les tests pour que les rivières se déversent dans les mers.

    Ces tests doivent être dans le code qui regarde la cohérence pour les mers, mais, aussi pour les rivières. Car en fait, la case qui doit être changer, et une case de type mer. Mais après elle devient de type rivière, et si la règle ne se retrouve pas dans le cas de la rivière, bah alors, la fonction va placer une rivière normale et non une embouchure.
    Le test en lui même, est, que si on a une case de rivière ( dans une des 4 directions ) et que les 5 autres cases ( à l'opposé de la première case ) ( donc avec les diagonales ) sont des mers, bah alors, c'est une embouchure.
    Mais dans la cohérence de la mer, le test est un peu différent, car c'est dès que l'on c'est que nous allons avoir une bordure linéaire, alors nous faisons le test afin de savoir si nous avons une rivière sur les deux prochaines cases, là où on voulais mettre la cote.

    Le résultat en image dans la deuxième capture d'écran.
    Images attachées Images attachées   

  9. #29
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Dans le code on remarquera le cas avec le default, qui est constitué que d'un assert(0). L'assert(0) bloque le programme immédiatement ( que en mode débug ) et lorsque le debuggueur est présent, a le même role qu'un point d'arrêt. Du coup, si un jour j'oublie de faire un morceau de code ( un rajout d'un type de tuile, dans dix ans ou autre ) et bah le programme "plantera" directement et me dira que je suis bête d'avoir oublié ça . Et puis ça supprime les warnings du compilateur pour tout les case non traités ( bien que je n'ai vu encore aucune warning de ce genre sous Visual Studio ... GCC le fait pourtant ).
    Oui je fais pareil.


    pour la boulot, ca avance bien !
    Jc

  10. #30
    Membre averti
    Homme Profil pro
    Lycéen
    Inscrit en
    Novembre 2008
    Messages
    86
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2008
    Messages : 86
    Points : 355
    Points
    355
    Par défaut
    Salut, impressionnant!

    Je bloque aussi sur ce problème de "transitions", j'ai choisi de tout laisser au "scripteur"

    PS : Pour tes pointeurs sur pointeurs, je recommande les SmartPointers de Boost, juste quelques headers à inclure

  11. #31
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Citation Envoyé par pierreyoda
    PS : Pour tes pointeurs sur pointeurs, je recommande les SmartPointers de Boost, juste quelques headers à inclure
    euh, je ne pense que Boost soit disponible pour un GP32X ou un AmigaOS
    Sinon, habituellement, je me recommenderai aussi les pointeurs intelligents, mais en réalité, je n'en ai jamais utilisé ( mais je les connait grace à la théorie )

  12. #32
    Membre averti
    Homme Profil pro
    Lycéen
    Inscrit en
    Novembre 2008
    Messages
    86
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2008
    Messages : 86
    Points : 355
    Points
    355
    Par défaut
    Bon, pour les transitions de tiles, après avoir retourné le problème durant près d'une semaine (j'étais parti plus sur l'idée de boucles de fearyouself) j'ai regardé de près la solution que tu emploies ici, et elle est vraiment très élégante!
    Je l'ai donc transposée dans mon script, et ça marche parfaitement (quelques bugs ici et là mais bon...)

    Donc que dois-je mettre comme crédit? (je suis en GPL3).

    PS : si le résultat en Lua t'intéresse, je peux le poster

    EDIT : pour Boost, étant donné que ce sont des headers à includes (pas comme filesystem, qui lui ne serait pas portable) directement, je ne vois pas pourquoi ça ne marcherait pas...

    EDIT2 : ah nan, aucun bug, c'était juste une petite faute de frappe, le système est bel est bien ultra robuste.

  13. #33
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Bon, pour les transitions de tiles, après avoir retourné le problème durant près d'une semaine (j'étais parti plus sur l'idée de boucles de fearyouself) j'ai regardé de près la solution que tu emploies ici, et elle est vraiment très élégante!
    Je l'ai donc transposée dans mon script, et ça marche parfaitement (quelques bugs ici et là mais bon...)

    Donc que dois-je mettre comme crédit? (je suis en GPL3).
    Déjà, je suis très heureux que l'on reprenne mon code ( que moi, je ne trouvais pas si élégant que cela ).
    Dans tout les cas, je suis content que cela marche aussi pour vous
    Pour les licences, il ne doit pas y avoir de problème, normalement, si j'ai bien compris, il faut juste une mention que le code vient de mon projet, avec le nom de l'auteur ( qui est marqué sur chaque fichier de code de mon projet ). Voilà tout ( Lisez le fichier LICENCE.txt )

    Pour Boost, c'est à voir ... mais ... euh ... je verrais

  14. #34
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Aujourd'hui je fini la logique interne de l'éditeur ( enfin \o/ ).

    Pour ce faire j'implémente la gestion des ponts.
    Pour ceux ci, je fais comme pour les autres tuiles, une fonction de test, et une fonction pour appliquer la tuile.
    La fonction de test aura la plus grande partie du travail, car un point ne peut être mis que sur une rivière horizontale ou verticale, et que sur les bords de mer, ou sur les mers, mais si un peu est adjacent.

    Du coup, beaucoup de possibilité vont être limité juste par la fonction de test. Après la fonction qui regarde la cohérence du monde, va juste faire en sorte, que le pont soit verticale ou horizontale selon la circonstance.
    Par contre, les ponts apportent encore une fois, un changement dans la structure des tuiles. Effectivement, je vais rajouter un boolean pour savoir si c'est un pont.
    En fait, pour garder la logique de l'éditeur, un pont est une route ( ça nous en sommes sur ), mais peut aussi être une rivière, ou une mer, selon la case ou nous mettons notre pont. Mais que si on met que c'est une route, alors les cases adjacente, vont changer de forme comme si le pont n'était pas une continuité de la mer, ou de la rivière.
    La solution retenue est donc d'ajouter un boolean pour savoir si c'est un peu. Une autre qui me semble possible est que selon la case recouverte, ma tuile de pont, aurait été modifié en tant que rivière, ou mer selon la case recouverte. Mais ça casse la logique, qui va aussi nous servir pour le jeu, ainsi que le principe de la fabrique.

    Du coup, le code de cohérence des routes / rivière / mers va prendre en compte les ponts, comme étant des leurs. ( C'est ici, que je me suis rendu compte que mes tests utilisant des drapeaux intermédaires, c'était une bonne idée, car cela sépare la partie récupération de données ( que je viens de changer ) avec la partie logique en elle même ).

    Pour les ponts, la fonction qui place un pont ( setBridge() ), défini elle même si le pont est horizontal ou vertical.
    Voici le code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    bool MapEditor :: setBridge(const UVec2& position)
    {
    	switch(this->getTile(position).tileType)
    	{
    		// Rivers
    		case TT_River_V:
    			map[position.y][position.x] = TileFactory(TT_Bridge_H);
    			break;
    		case TT_River_H:
    			map[position.y][position.x] = TileFactory(TT_Bridge_V);
    			break;
    		// Sees
    		case TT_Coast_V:
    		case TT_Coast_L:
    		case TT_Coast_R:
    			map[position.y][position.x] = TileFactory(TT_Bridge_H);
    			break;
    		case TT_Coast_H:
    		case TT_Coast_B:
    		case TT_Coast_T:
    			map[position.y][position.x] = TileFactory(TT_Bridge_V);
    			break;
    		case TT_See:
    			if ( this->getTile(UVec2(position.x-1,position.y)).isBridge || this->getTile(UVec2(position.x+1,position.y)).isBridge )
    			{
    				map[position.y][position.x] = TileFactory(TT_Bridge_H);
    			}
    			else if ( this->getTile(UVec2(position.x,position.y-1)).isBridge || this->getTile(UVec2(position.x,position.y+1)).isBridge )
    			{
    				map[position.y][position.x] = TileFactory(TT_Bridge_V);
    			}
    			else
    			{
    				assert(0);	// If we are here, it's because the test function is wrong
    			}
    			break;
    		default:
    			assert(0);	// If we are here, it's because the test function is wrong
    			break;
    	}
     
    	checkCoherencyAround(position);
     
    	return true;
    }
    Les tests n'ont rien de compliquer :p. On voit que cette fonction, qui ressemble énormément à la fonction qui test si on peut coller la tuile de pont, permet dans un sens de vérifié si la fonction de test est juste. Cela n'est pas vraiment un bénefice, lors de la création des deux fonctions ( qui plus est, je vais surement faire un copier coller ... ) mais lors des évolutions futures du jeu, on pourra faire en sorte, que les deux fonctions suivent des évolutions similaires. Quoiqu'il en soit, mieux vaut se donner le meilleur avantage dès le début. Mais bon, je n'ai pas cédé à la tentation du copier coller .
    Par rapport aux autres fonction, on peut remarquer que je ne vérifie pas la cohérence de la carte actuelle. Ce qui est normal, car j'applique directement la tuile adéquate.

    Voici la partie rajouté de la fonction de test ( testTile() ):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    case TT_Bridge_H:
    		switch ( this->getTile(position).tileType )
    		{
    		case TT_River_H:
    		case TT_River_V:
    		case TT_Coast_H:
    		case TT_Coast_V:
    		case TT_Coast_T:
    		case TT_Coast_R:
    		case TT_Coast_B:
    		case TT_Coast_L:
    			return true;
    			break;
    		case TT_See:
    			if ( this->getTile(UVec2(position.x-1,position.y)).isBridge ||	// Left
    				 this->getTile(UVec2(position.x+1,position.y)).isBridge ||	// Right
    				 this->getTile(UVec2(position.x,position.y-1)).isBridge ||	// Top
    				 this->getTile(UVec2(position.x,position.y+1)).isBridge )	// Bottom
    			{
    				 return true;
    			}
    			break;
    		default:
    			return false;
    			break;
    		}
    		break;
    Je n'ai pas vraiment de remarque à faire sur celle ci.

    Comme je vous le montre dans la première capture d'écran cela fonctionne plutot très bien . ( De toute façon, je ne montre pas les trucs qui ne marchent pas )

    Comme j'avais décidé d'être plutôt sympa avec l'utilisateur de l'éditeur, je vais rajouter un petit morceau de code dans la fonction setRoad() afin qu'en appliquant des routes sur les rivières, cela applique des ponts directement ( au lieu de supprimer la rivière ).
    Voici ce que devient la fonction avec le changement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    bool MapEditor :: setRoad(const UVec2& position)
    {
    	if (this->getTile(position).tileType == TT_River_H || this->getTile(position).tileType == TT_River_V)
    	{
    		// We use the function setBridge, because, the function has all the logic needed (I don"t want to rewrite it ;))
    		return setBridge(position);
    	}
     
    	map[position.y][position.x] = TileFactory(TT_Road_H);
     
    	checkCoherencyForRoad(position);
    	checkCoherencyAround(position);
     
    	return true;
    }
    Maintenant que les ponts sont appliqués correctement, je vais m'interesser au cas des plages. Les plages, sont un endroit, dans le jeu, ou les navires du joueur, mais aussi les unités terrestres, peuvent allés.
    Pour que je ne sois pas embété, je vais encore rajouté un boolean dans ma structure Tile pour reconnaitre directement les plages ( cela m'aidera dans l'éditeur ainsi que dans le jeu ).
    Les plages ont la cohérence graphique ( ou logique ) que les mers. Cela donne une grande indication .

    En fait, maintenant que les mers ont leur logique de fait, la mise en place des plages est simple.
    Je vais faire deux choses:

    - La fonction de cohérence des mers, va, avant d'appliquer une tuile de mer, regarder si la tuile d'avant était une plage, ou une mer ( tout du moins, que dans les cas, ou une case plage est possible ), si c'est une case de mer, on applique comme nouvelle tuile une tuile de mer, mais si c'était une tuile de plage, on va appliquer une tuile de plage. Voilà comment je reprends facilement toute la logique des mers, pour les plages \o/
    - Amélioration de la fonction de test ( testTile() ). Celle ci doit vérifié si on peu appliquer une tuile de plage ... tout simplement.

    La fonction qui applique les plages ( setBeach() ) va faire exactement la même chose que pour les mers ... soit ... copier la tuile de plage, et effectué les tests de cohérence. Hop le tour est joué ... les plages sont en place, sortez les bikinis, les serviettes et les parasols \o/

    Je ne montre pas les changements de la fonction de cohérence des mers, car déjà, c'est très simple, et puis, la fonction est grande. Je n'ai que rajouté un drapeau pour savoir si la case actuel est une plage, et puis des tests tout au long comme expliqué precedement.
    Par contre, je vous donne le code rajouté dans la fonction qui test si on peut mettre une tuile à une position donnée ( testTile() ):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    case TT_Beach_T:
    		switch ( this->getTile(position).tileType )
    		{
    			case TT_Coast_B:
    			case TT_Coast_T:
    			case TT_Coast_R:
    			case TT_Coast_L:
    			case TT_Coast_ER:
    			case TT_Coast_EL:
    			case TT_Coast_EB:
    			case TT_Coast_ET:
    			case TT_Coast_BL:
    			case TT_Coast_BR:
    			case TT_Coast_TR:
    			case TT_Coast_TL:
    				return true;
    				break;
    			default:
    				return false;
    				break;
    		}
    		break;
    Comme vous pouvez voir sur la deuxième capture d'écran, cela fonctionne convenablement.

    Pour les récifs ... il ne peuvent être mis que sur des cases de mer, et non pas vraiment d'importance ( à part du point de vue tactique dans le jeu )

    La dernière chose qu'il me reste à faire à propos de la logique interne c'est d'appliquer les batiments. Je pense que c'est la partie la plus facile.
    Le seul cas spécial est pour le quartier général. Effectivement, il ne doit y en avoir qu'un seul sur la carte.

    Je rajoute deux fonctions ( toujours privés ) une pour rajouter un batiment, et une autre pour rajouter un quartier général.
    En fait, la fonction qui ajoute un batiment, regarde si le type passé est un quartier général. Si s'en est un, on appelle la fonction spécifique.

    Comme on peut voir dans la troisième capture, il est maintenant possible d'avoir une assez proche reproduction de la première carte du jeu originale. J'ai toutefois une légère différence avec l'original, mais cela importe vraiment très peu.
    La prochaine fois, je ferai en sorte que l'on puisse poser des batiments d'une factions ennemies :p ( mais je ne veux pas faire la guerre moi, je suis pacifiste )

    Dans les petits changement que j'ai fait aujourd'hui, il y a qu'une tuile invalide, est, par défaut, une tuile de mer. Pourquoi ce changement? Pour faire que si l'utilisateur met de la mer sur les bords de la carte, cela ne fasse pas comme une fin du monde trop abrupte, mais une mer infinie . Par contre, cela m'oblige à rajouter un test pour la fonction checkCoherency() afin d'éviter de traiter les tuiles invalides ( qui sont tout de même des mers, d'ou le nouveau problème ). Mais comme j'avais rajouter un enum spécialement pour les tuiles invalides, ce n'est qu'un leger contre temps ( une broutille :p ). ( J'ai mis une capture d'écran pour montrer le resultat. Notre carte ressemble à une ile )
    Images attachées Images attachées     

  15. #35
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Aujourd'hui je fini la logique interne de l'éditeur ( enfin \o/ ).


    Pour ce faire j'implémente la gestion des ponts.
    Pour ceux ci, je fais comme pour les autres tuiles, une fonction de test, et une fonction pour appliquer la tuile.
    La fonction de test aura la plus grande partie du travail, car un point ne peut être mis que sur une rivière horizontale ou verticale, et que sur les bords de mer, ou sur les mers, mais si un peu est adjacent.
    Logique :-)

    Du coup, beaucoup de possibilité vont être limité juste par la fonction de test. Après la fonction qui regarde la cohérence du monde, va juste faire en sorte, que le pont soit verticale ou horizontale selon la circonstance.
    Par contre, les ponts apportent encore une fois, un changement dans la structure des tuiles. Effectivement, je vais rajouter un boolean pour savoir si c'est un pont.
    Je comprends le pourquoi mais fait simplement attention, trop de booléens rendent les choses nettement plus compliqué plus tard. Ne commence pas à ajouter un booléen par type spécial ;-) (je dis cela, je ne dis rien)

    En fait, pour garder la logique de l'éditeur, un pont est une route ( ça nous en sommes sur ), mais peut aussi être une rivière, ou une mer, selon la case ou nous mettons notre pont. Mais que si on met que c'est une route, alors les cases adjacente, vont changer de forme comme si le pont n'était pas une continuité de la mer, ou de la rivière.
    Oui c'est pour cela que normalement, ce qu'on fait c'est on a plusieurs plan : le fond (plaine, eau, plage, ...), le haut (immeuble, pont, etc). Et indifférement de cela, on a un flag qui dit : accessible ou non.

    Donc de l'eau sans pont -> non accessible
    De l'eau avec pont -> accessible.

    Et si tu veux aller plus simple, tu fais un vecteur de tuiles, le type pour ton rendu est en fait le premier élément, ensuite tu as les tuiles "décors".

    Faudrait ajouter des choses pour les ponts, mais bon...

    C'est plus générique que ce que tu fais et tu l'as déjà implémenté donc ce n'est pas grave ;-)

    La solution retenue est donc d'ajouter un boolean pour savoir si c'est un peu. Une autre qui me semble possible est que selon la case recouverte, ma tuile de pont, aurait été modifié en tant que rivière, ou mer selon la case recouverte. Mais ça casse la logique, qui va aussi nous servir pour le jeu, ainsi que le principe de la fabrique.
    On est d'accord je préfére ta solution ;-)


    -> Remarque : mer -> sea et non see...

    Par rapport aux autres fonction, on peut remarquer que je ne vérifie pas la cohérence de la carte actuelle. Ce qui est normal, car j'applique directement la tuile adéquate.
    J'ai une tendance à être parano dans le code comme ca sauf s'il est critique aux performances.


    Comme j'avais décidé d'être plutôt sympa avec l'utilisateur de l'éditeur, je vais rajouter un petit morceau de code dans la fonction setRoad() afin qu'en appliquant des routes sur les rivières, cela applique des ponts directement ( au lieu de supprimer la rivière ).
    Logique aussi, bien joué ;-)

    Pour que je ne sois pas embété, je vais encore rajouté un boolean dans ma structure Tile pour reconnaitre directement les plages ( cela m'aidera dans l'éditeur ainsi que dans le jeu ).
    Les plages ont la cohérence graphique ( ou logique ) que les mers. Cela donne une grande indication .
    Encore un booléen ;-)

    Dans les petits changement que j'ai fait aujourd'hui, il y a qu'une tuile invalide, est, par défaut, une tuile de mer. Pourquoi ce changement? Pour faire que si l'utilisateur met de la mer sur les bords de la carte, cela ne fasse pas comme une fin du monde trop abrupte, mais une mer infinie . Par contre, cela m'oblige à rajouter un test pour la fonction checkCoherency() afin d'éviter de traiter les tuiles invalides ( qui sont tout de même des mers, d'ou le nouveau problème ). Mais comme j'avais rajouter un enum spécialement pour les tuiles invalides, ce n'est qu'un leger contre temps ( une broutille :p ). ( J'ai mis une capture d'écran pour montrer le resultat. Notre carte ressemble à une ile )
    Pourquoi ne pas mettre que la tuile invalide peut être aussi terrestre ou plage, cela ajouterait un peu de généricité à ton jeu, non ?

    Jc

  16. #36
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Le but de la séance d'aujourd'hui est d'ajouter le possibilité de changer la couleur des batiments. Pour être plus précis, ce n'est pas vraiment que l'on change la couleur, puisque j'ai une images pour chaque couleur, mais que l'on va donner la possibilité à l'utilisateur de mettre des batiments d'une autre couleur.

    Du point de vue utilisateur, pour changer la couleur, le curseur de la barre des tuiles doit être sur un batiment et il doit appuyer sur haut ou bas.

    Je vais aussi parler de la construction de la TileBar, car celle ci est lamentable. Je le dis, même si c'est mon code... Effectivement, dans le constructeur de la TileBar le code est comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    tilesList.push_back(
    		std::pair<TileView, TileType>(
    			TileView(
    				new AnimatedSprite(sm,"./data/gfx/tiles/classic/plain.png",32,32,NORMAL_SPEED,true),
    				static_cast<int>(Scaler::getXScaleFactor() * TILE_BAR_XMARGIN)), 
    			TT_Plain)
    		);
    Pour rappel, la structure qui est utilisé est la suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    struct TileView
    	{
    		AnimatedSprite* pASprite;
    		int positionX;
     
    		TileView(AnimatedSprite* pASprite, const int positionX):pASprite(pASprite),positionX(positionX) {}
    	};
     
    	std::vector<std::pair<TileView, TileType> > tilesList;		/*<! The tiles with their TileType */
    Ce qui fait que c'est plutot assez compliqué et je n'en suis pas content.

    Surtout que la ligne avec le push_back est atroce, car pour chaque éléments, je change les coordonées ( la deuxième ligne du constructeur du TileView() ) ... surtout que cela peut être optimisé.
    Et puis, mon prochain problème, certes, je n'y suis pas encore mais j'approche à une vitesse ahurissante ( ou pas ) c'est que je vais avoir une deuxième barre de Tuile mais pour les unités ( si si, mon éditeur pourra placer des unités ). Donc la structure + le constructeur doivent être changés.

    Première amélioration:

    Au lieu d'utilise un std::pair, on va tout mettre dans la structure.

    Pour chaque changement, je vais faire jouer le compilateur, afin de savoir ou faire les changements pour que mon code redevienne juste rapidement.
    Je fais une petite parenthèse:
    En programmmation vous avez deux amis pour la Vie:
    - Le compilateur
    - Le debuggueur

    Pour le compilateur c'est assez facile, mais il est toujours possible de lui facilité la tache, afin de s'éviter un tas d'erreurs ( les conditions du préprocesseur, et certaines techniques / règles / astuces du C++ ( dans le genre, faire un if ( 9 != var ) ( ou le nombre est au début ) ).
    Pour le debuggueur, c'est une question de maitrise. Normalement, tous sont équivalents ( du moins, GDB et le debuggueur de Visual le sont ( quelques différences, mais peu embarassantes ) ). Ce qui importe, c'est que vous sachiez vous en servir. Une fois que vous savez, vous pouvez trouver tout les bugs que vous voulez très rapidement.
    Voilà pour la parenthèse, car là, je m'aide du compilateur

    Deuxième amélioration:

    Cette fois cela importe sur ce qui va être affiché ( donc en prévision du TileBar pour les unités ). Je vais passer une liste composé des pointeurs sur les AnimatedSprite ( venant directement de la carte ) et de leur type ( TileType ). Ainsi, je pourrai généré le contenu de mon TileSprite dynamiquement ( simplification du code ).
    Voici le nouveau prototype:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    TileBar(SpriteManager& sm, const Window& win, const std::vector<std::pair<AnimatedSprite*, TileType> > listTiles);
    Voici le code qui construit la barre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    // Load all the animation needed by the TileBar
    	for ( std::vector<std::pair<AnimatedSprite*, TileType> >::const_iterator itTile = listTiles.begin() ; itTile != listTiles.end() ; ++itTile )
    	{
    		tilesList.push_back(TileView(
    										itTile->first,
    										static_cast<int>(Scaler::getXScaleFactor() * TILE_BAR_XMARGIN) * (1 + i * 2) + static_cast<int>(Scaler::getXScaleFactor() * 32 * i)),
    										itTile->second));
     
    		counter++;
    	}
    Le deuxième avantage d'une telle méthode, c'est que lorsque les theme des cartes seront intégré, bah il n'y aura pas d'incohérence entre l'éditeur et la carte demander, car les AnimatedSprite viennent directement de la carte.
    Mais pour que cela marche complètement, il a fallu que je rajoute une méthode pour récupéré l'AnimatedSprite associé à un type passé en paramètre dans la classe de la carte ( Map ).
    La dedans aucune magie:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    AnimatedSprite* Map :: getAssociatedSprite(const TileType type)
    {
    	if ( tilesASprite.find(type) == tilesASprite.end() )
    	{
    		return NULL;
    	}
     
    	return tilesASprite[type];
    }
    Je pourrai même aller plus loin, en retournant le std::pair dont j'ai besoin ...

    Ce qui fait que je dois rajouter ceci avec de construire ma TileBar ( donc dans le main_editor.cpp ):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    // Prepare the data to put in the TileBar for building
    std::vector<std::pair<AnimatedSprite*, TileType> >buildingTiles;
    {
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Plain),TT_Plain));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Tree),TT_Tree));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Mountain_1),TT_Mountain_1));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_See),TT_See));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_River_H),TT_River_H));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Beach_T),TT_Beach_T));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Reef),TT_Reef));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Road_H),TT_Road_H));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Bridge_H),TT_Bridge_H));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_HQ),TT_Red_HQ));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_City),TT_Red_City));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_Factory),TT_Red_Factory));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_Port),TT_Red_Port));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_Airport),TT_Red_Airport));
    }
     
    TileBar buildingTB(sm,win,buildingTiles);
    Ce qui est vraiment plus beau qu'avant ( si si ).

    Malheureusement, je vais mettre à mal cette beauté, en ajoutant le support des différentes factions ... ( snif )
    Effectivement, je vais en rajouter encore une couche. La structure finale est la suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    std::vector<std::vector<TileView> > tilesList;
    Et oui, ce n'est pas très beau, mais grace à cette structure, je peux mettre plusieurs éléments à la même position, et je vais pouvoir afficher celui que je veux, vous allez voir .

    La variable membre de la classe TileBar 'current' est renommé 'current_x' ( mais à la même fonction )
    J'ajoute aussi une nouvelle variable 'currentY' afin de savoir lequel est sélectionné sur la deuxième dimension ( donc la couleur en fait ).

    Encore une fois, après ce changement j'utilise le compilateur pour savoir que changer.

    Maintenant le constructeur ressemble à ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    TileBar(SpriteManager& sm, const Window& win, const std::vector<TileView> listTiles);
    Et comme vous pouvez le voir, cette fois on prend directement une liste de TileView.
    En fait, la position sur l'axe des X qui est contenue dans la tileView, n'est pas la position en coordonnées écran de la tuile mais la position logique sur la barre. Mais l'utilisateur peut mettre deux fois un, ce qui fera en sorte que les deux tuiles sont à la même position est pourront être changer avec les flèches haut et bas, donc l'objectif est atteint sans vraiment détruire la beauté du constructeur.
    Mais, à cause que j'ai voulu garder cette beauté du code, si je puis me permettre l'expression, et bah j'ai perdu en sécurité du code. Le code de construction est le suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    // Search the maximum positionX to know the size of the vector
    for ( std::vector<TileView>::const_iterator itTile = listTiles.begin() ; itTile != listTiles.end() ; ++itTile )
    {
    	if ( maximumX < itTile->positionX )
    		maximumX = itTile->positionX;
    }
    assert(maximumX+1<=listTiles.size());
    // Resize the vectore to contain the good number of elements
    for ( unsigned int i = 0 ; i < maximumX+1 ; i++ )
    {
    	tilesList.push_back(std::vector<TileView>());
    }
     
    // Load all the animation needed by the TileBar
    for ( std::vector<TileView>::const_iterator itTile = listTiles.begin() ; itTile != listTiles.end() ; ++itTile )
    {
    	tilesList[itTile->positionX].push_back(TileView(
    											itTile->pASprite,
    											itTile->type,
    											static_cast<int>(Scaler::getXScaleFactor() * TILE_BAR_XMARGIN) * (1 + itTile->positionX * 2) + static_cast<int>(Scaler::getXScaleFactor() * 32 * itTile->positionX)));
    }
    L'assertion permet d'éviter des erreurs abérantes, mais, je ne trouve toujours pas ce code assez sain. Car en fait, de passer une TileView à la TileBar, on pourrait croire qu'il l'utilise comme tel, mais non.
    Ainsi que la construction ( remplissage ) qui est fait par la deuxième boucle, me dérange un peu...

    Sinon, le code dans le main est devenu le suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    // Prepare the data to put in the TileBar for building
    					std::vector<TileView> buildingTiles;
    					{
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Plain),TT_Plain,0));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Tree),TT_Tree,1));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Mountain_1),TT_Mountain_1,2));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_See),TT_See,3));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_River_H),TT_River_H,4));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Beach_T),TT_Beach_T,5));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Reef),TT_Reef,6));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Road_H),TT_Road_H,7));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Bridge_H),TT_Bridge_H,8));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Red_HQ),TT_Red_HQ,9));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Blue_HQ),TT_Blue_HQ,9));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Green_HQ),TT_Green_HQ,9));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Yellow_HQ),TT_Yellow_HQ,9));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Red_HQ),TT_Red_HQ,9));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Red_City),TT_Red_City,10));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Blue_City),TT_Blue_City,10));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Green_City),TT_Green_City,10));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Yellow_City),TT_Yellow_City,10));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Neutral_City),TT_Neutral_City,10));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Red_Factory),TT_Red_Factory,11));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Blue_Factory),TT_Blue_Factory,11));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Green_Factory),TT_Green_Factory,11));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Yellow_Factory),TT_Yellow_Factory,11));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Neutral_Factory),TT_Neutral_Factory,11));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Red_Port),TT_Red_Port,12));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Blue_Port),TT_Blue_Port,12));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Green_Port),TT_Green_Port,12));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Yellow_Port),TT_Yellow_Port,12));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Neutral_Port),TT_Neutral_Port,12));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Red_Airport),TT_Red_Airport,13));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Blue_Airport),TT_Blue_Airport,13));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Green_Airport),TT_Green_Airport,13));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Yellow_Airport),TT_Yellow_Airport,13));
    						buildingTiles.push_back(TileView(m.getAssociatedSprite(TT_Neutral_Airport),TT_Neutral_Airport,13));
    					}
     
    					TileBar buildingTB(sm,win,buildingTiles);
    Ce qui n'est pas trop moche.

    Maintenant je peux rajouter les fonctions afin de pouvoir changer currentY ( donc moveUp() et moveDown() ) qui elle, ne comportent rien de particulier.
    Puis le code dans le main, pour la gestion des touches ( je n'ai pas besoin non plus de vous montrer ).
    Le seul code qui change, c'est celui de dessin ( pour la TileBar ), qui est maintenant dépendant de currentY.

    Voyez les captures d'écran, j'ai un résultat très convenable.
    Pour la prochaine fois je rajouterai des animations pour ce changement

    @fearyourself: Je répondrai ( et reflechirai ) à vos remarques un peu plus tard, si possible, car aujourd'hui, je ne peux pas prendre ce temps. Désolé, et encore merci pour les retours ... ( j'ai honte pour le 'sea' et non 'see' )
    Images attachées Images attachées    

  17. #37
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Pour le compilateur c'est assez facile, mais il est toujours possible de lui facilité la tache, afin de s'éviter un tas d'erreurs ( les conditions du préprocesseur, et certaines techniques / règles / astuces du C++ ( dans le genre, faire un if ( 9 != var ) ( ou le nombre est au début ) ).
    Pour ceux qui n'ont jamais tenté cela :
    Tentez de compiler cela. C'est une bonne habitude mais j'ai une tendance à compiler avec -Wall -Wextra et cela choppe ce genre de souci.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    // Prepare the data to put in the TileBar for building
    std::vector<std::pair<AnimatedSprite*, TileType> >buildingTiles;
    {
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Plain),TT_Plain));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Tree),TT_Tree));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Mountain_1),TT_Mountain_1));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_See),TT_See));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_River_H),TT_River_H));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Beach_T),TT_Beach_T));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Reef),TT_Reef));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Road_H),TT_Road_H));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Bridge_H),TT_Bridge_H));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_HQ),TT_Red_HQ));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_City),TT_Red_City));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_Factory),TT_Red_Factory));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_Port),TT_Red_Port));
    	buildingTiles.push_back(std::pair<AnimatedSprite*, TileType>(m.getAssociatedSprite(TT_Red_Airport),TT_Red_Airport));
    }
     
    TileBar buildingTB(sm,win,buildingTiles);
    Pourquoi ne pas avoir une boucle qui part du premier élément et va jusqu'au dernier (en ajoutant un TT_FIRST et TT_LAST dans ton enum) ?

    @fearyourself: Je répondrai ( et reflechirai ) à vos remarques un peu plus tard, si possible, car aujourd'hui, je ne peux pas prendre ce temps. Désolé, et encore merci pour les retours ... ( j'ai honte pour le 'sea' et non 'see' )
    Oki :-)

  18. #38
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Citation Envoyé par fearyourself Voir le message
    Je comprends le pourquoi mais fait simplement attention, trop de booléens rendent les choses nettement plus compliqué plus tard. Ne commence pas à ajouter un booléen par type spécial ;-) (je dis cela, je ne dis rien)
    Je suis d'accord ... mais je crois que cela m'arrange ( et surtout, m'évite de faire des tests trop compliqué ... enfin ... je crois ... )
    Ok, le test je peux le mettre dans une fonction, et on y touche plus ... mais cela ne m'offre pas de protections, comme le font les switch case des fabriques de Tile ...

    Citation Envoyé par fearyourself Voir le message
    Oui c'est pour cela que normalement, ce qu'on fait c'est on a plusieurs plan : le fond (plaine, eau, plage, ...), le haut (immeuble, pont, etc). Et indifférement de cela, on a un flag qui dit : accessible ou non.

    Donc de l'eau sans pont -> non accessible
    De l'eau avec pont -> accessible.

    Et si tu veux aller plus simple, tu fais un vecteur de tuiles, le type pour ton rendu est en fait le premier élément, ensuite tu as les tuiles "décors".

    Faudrait ajouter des choses pour les ponts, mais bon...

    C'est plus générique que ce que tu fais et tu l'as déjà implémenté donc ce n'est pas grave ;-)
    Et un boulean 'isGroundAccessible' et un autre 'isSeaAccessible' ? ( Non là je plaisante, je suis sensé pourvoir me débrouiller autrement ).
    Et personnellement, je n'aime pas vraiment l'idée ...

    Citation Envoyé par fearyourself Voir le message
    -> Remarque : mer -> sea et non see...
    Comme 'peu' à la place de 'pont' dans mon message ...

    Citation Envoyé par fearyourself Voir le message
    J'ai une tendance à être parano dans le code comme ca sauf s'il est critique aux performances.
    Pareillement ... sauf que là ... je ne sais plus, en plus j'avais pensé à le laisser pour qu'il vérifie ce qu'il y a autour ... et que comme ça les bugs sont vites capturés ...

    Citation Envoyé par fearyourself Voir le message
    Pourquoi ne pas mettre que la tuile invalide peut être aussi terrestre ou plage, cela ajouterait un peu de généricité à ton jeu, non ?
    Oui pourquoi ?
    Je ne sais pas pour la généricité ... je ne vois pas quels sont les avantages, et les inconvénients ... donc je testerai si je vois un avantage ... mais je doute un peu ... et je ne vois pas la généricité ajoutée ...

    Citation Envoyé par fearyourself
    Tentez de compiler cela. C'est une bonne habitude mais j'ai une tendance à compiler avec -Wall -Wextra et cela choppe ce genre de souci.
    Oui, enfin que sur une version récente de GCC, cela choppe en Warning ( je pense 4.3 ou peut être même 4.2 ( ou alors je ne fais pas l'erreur souvent )
    Mais je doute pour Visual Studio ( déjà que le switch d'un enum, il ne semble pas le faire ( ou alors j'écris du bon code du premier coup, mais je doute ))

    Citation Envoyé par fearyourself
    Pourquoi ne pas avoir une boucle qui part du premier élément et va jusqu'au dernier (en ajoutant un TT_FIRST et TT_LAST dans ton enum) ?
    J'aimerais, mais en fait , les enums dans la TileBar ne se suive pas ... ou alors il faut que je face un tableau constant ... mais bon ...
    Mais par rapport au code actuel, il n'y a pas moyen d'avoir un truc avec une boucle avec le disposition actuel des enum.
    Sinon ... je dois réapprendre la programmation

  19. #39
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 894
    Points : 219 532
    Points
    219 532
    Billets dans le blog
    124
    Par défaut
    Bonjour à tous,

    Voilà, je reviens enfin pour présenter les dernières nouvelles. Tout d'abord, j'ai mis en ligne quelques fichiers que j'avais oublié :red:. Maintenant le projet est compilable même pour quelqu'un d'extérieur, vu que aujourd'hui j'étais cette personne externe.
    Effectivement, j'ai changé de station de travail, et je peux me rendre compte que SVN c'est trop bien :p.

    Sinon, tout d'abord on va ajouter une image ( AnimatedSprite ) lorsque nous avons la possibilité de changer le propriétaire des batiments.
    Le code qui détermine si on affiche ce sprite, n'est qu'un test pour savoir si la taille de la liste pour cette position est supérieur à 1, et hop nous avons une UI convenable
    Et pour que tout soit parfait, je vérifie aussi si la barre à stopper son mouvement, pour mettre les flèches:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    // Draw the arrow if needed
    if ( tilesList[currentX].size() > 1 && state == TBS_Opened )
    {
    	r.drawTile(*pBarArrows,cursorPosition);
    }
    Comme sur mon ordinateur ( plus puissant mais bon ) le programme est plus lent, j'ai décidé d'alléger un peu les sorties de débugguage. Pour cela, j'ai entouré certaines entrées dans le log ( surtout celle du renderer ) par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #ifdef VERBOSE
    	LDebug() << ...
    #endif
    Je pense que je peux me le permettre car je sais maintenant qu'il y a du code de stable .

    Par contre la modification de la carte pour enlever un niveau de pointeur ( Pointeur sur Tile ) avait provoqué un bug dans les sorties de debugguage. Effectivement le nombre de Tuile allouées et détruites ne correspondait plus à rien. J'ai du du coup rajouter les constructeur par copie, et autre pour compter toute les allocations faites.
    Mais finalement, comme je sais que je n'ai plus du tout de pointeur, je vais retiré cette variable de debugguage.

    Elle m'a permet tout de même de me rendre compte que je faisais trop de chose, et que du coup, j'ai optimisé le code qui change les tuile dans la MapEditor. Effectivement, cette fois, si on veut plaquer une tuile du même type que la tuile en dessous, bah on ne fait pas les changements ( qui sont intutiles ).

    J'ai fait aussi en sorte que un quartier général soit connecté au route ( comme on peut voir dans la capture d'écran avec le quartier général rouge ) ( à comparer avec les anciennes captures )

    Et pour finir, j'ai enlevé ma faute avec 'See' et 'Sea' ( que je prononce de la même façon O_o )

    Voilà pour aujourd'hui
    Images attachées Images attachées  

  20. #40
    Membre éprouvé
    Avatar de Ange_blond
    Homme Profil pro
    Ingénieur développement en 3D temps réel
    Inscrit en
    Mars 2007
    Messages
    902
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur développement en 3D temps réel
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2007
    Messages : 902
    Points : 1 179
    Points
    1 179
    Par défaut
    Que ferait-on sans SVN ?

    Pour la question des output de débug, tu n'aurais pas eu meilleur temps de mettre les #ifdef #endif dans la définition de LDebug() ? ça t'évite de corriger tout ou partie de ton code.

    Bon courage

Discussions similaires

  1. [Projet en cours] Strategy(nom provisoire) - Advance wars like
    Par TiranusKBX dans le forum Projets
    Réponses: 17
    Dernier message: 29/09/2016, 15h46
  2. [VB6] [ADO] Like sur base Access
    Par dlpxlid dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 24/01/2003, 11h03
  3. Créer un interpréteur de langage inspiré du Basic
    Par Picasso dans le forum Algorithmes et structures de données
    Réponses: 4
    Dernier message: 11/05/2002, 17h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo