Crunchez vos adresses URL
|
Rejoignez notre discord
|
Hébergez vos photos
Page 1 sur 2 12 DernièreDernière
Affichage des résultats 1 à 30 sur 56
  1. #1
    Salut à tous,

    Il y a quelques temps (le 6 février en fait) j'ai commencé à développer mon petit jeu "indé" pendant mon temps libre. J'ai la chance de bosser en télétravail et d'avoir des horaires flexibles, ce qui fait que j'en ai finalement pas mal, de temps libre.

    Il s'agira donc d'un RTS-like avec un aspect gestion plus développé que dans les RTS moyens, pour se rapprocher pourquoi pas d'un Settlers. À ce stade, je n'ai même pas encore décidé dans quel univers il se déroule (médiéval-fantastique, deuxième guerre mondiale, post-apo) ni la liste des ressources ni le niveau de zoom, et ainsi de suite

    Mais l'important est que je ne sois pas bloqué dans mon développement. Pour l'instant je bosse sur des trucs de base : affichage de la map, des personnages, etc. Il faut que je me décide rapidement sur les aspects évoqués plus haut, mais pour le moment j'ai encore du taf'

    Quelques caractéristiques que je me suis fixées :
    - pas de moteur 3D/moteur de jeu, tout est codé à la main
    - les personnages sont affichés sous forme de sprites 2D
    - le jeu doit être portable

    Pas de moteur 3D
    Cela signifie que je ne vais pas utiliser Ogre, Unity Engine, CryEngine, etc. mais tout coder à la main avec OpenGL.
    Il est mille fois plus gratifiant de tout faire soi-même, d'autant que cela me permettra d'avoir le contrôle total sur mon appli
    Pour l'instant j'utilise les librairies suivantes (mais cela peut changer) qui sont toutes gratuites : Boost (plein de trucs utiles de manière général), OpenGL (pour communiquer avec la carte graphique, bien que ce ne soit pas une lib), OpenAL (pour le son), FreeImage (charger des images), FreeType (charger des polices de caractères), libavcodec (lire des vidéos), DirectInput (les contrôles)

    Personnages sous forme de sprites
    La modélisation 3D et l'animation sont de vastes domaines qui sont incroyablement prise de tête. J'ai donc décidé d'afficher les personnages à l'écran sous forme de sprites, comme dans Populous 3 ou Theme Park World
    Non seulement c'est plus simple à faire, mais c'est également plus rapide à l'exécution

    Portable
    Je développe le jeu pour Windows 32 bits. Mais le jeu doit être portable, ce qui veut dire que si je décide du jour au lendemain de faire une version Linux ou une version XBox, les modifications à apporter doivent être les plus petites possibles


    Cela fait donc 7 semaines et 2 jours que je bosse dessus pendant mon temps libre, et j'en suis là :



    Vous pouvez donc voir l'ébauche de terrain (pour l'instant les reliefs sont générés aléatoirement) ainsi qu'un personnage au milieu de l'écran
    Evidemment j'en suis pas très loin et ça semble un peu à des screens de kevin12ans qui vient montrer son MMO qui va révolutionner la planète. Néanmoins ce qui me bloque le plus c'est le temps, et je serais déjà beaucoup plus avancé si j'étais 35h/semaines dessus

    J'envisage de poster de temps en temps des comptes rendus/mini-tutos sur comment j'ai fait tel ou tel aspect, n'hésitez pas à me dire si vous voulez savoir comment j'ai fait ceci-celà
    Dernière modification par Tomaka17 ; 27/03/2012 à 19h47.
    Rust fanboy

  2. #2
    Je ne peux que te souhaiter bien du courage!

    Moi aussi je m'étais crée un petit moteur avec mes petites mimines, OpenGL, GLSL avec gestion minimal de la physique (accélération, force, mais pas les rotations), et de mon expérience, je ne peux que te conseiller de passer pas mal de temps à dessiner des graphes UML de toute sorte pour bien définir le bouzin et à faire de l'héritage de PARTOUT.

    Car retconné ton code toute les semaines simplement parceque tu as oublié quelque chose qui pourtant aurait du te sauter aux yeux pendant la phase de conception, cela risque de vite te souler.

    Après, je ne connais pas ton niveau en programmation donc je m'excuse si je donne des conseils qui peuvent paraitre évident mais bon.

    Aussi, pour la génération de terrain aléatoire, je ne peux que te conseiller de jeter un oeil sur l'algorithme de diamant-carré, les fractales, ou le bruit de perlin http://en.wikipedia.org/wiki/Fractal_landscape
    http://en.wikipedia.org/wiki/Diamond-square_algorithm
    Dernière modification par Cuthalion ; 27/03/2012 à 21h10.

  3. #3
    En fait j'ai déjà un peu d'expérience vu que j'ai déjà bossé sur des gros projets non-jeux ainsi que sur des petits jeux fait moi-même
    Alors évidemment faire des petits jeux c'est différent de faire un gros truc bien fignolé, mais j'ai déjà une idée assez précise de comment tout cela va se ficeler

    Je vois ce que tu veux dire en parlant de modifier son code chaque semaine parce qu'on a oublié un truc, mais j'ai tendance à coder de grosses classes avec peu de dépendances et avec très rarement de l'héritage, ce qui m'évite souvent ce genre de problèmes.

    Sinon pour le terrain, en fait pour l'instant j'ai juste fait un bête "vertex.z = rand()", mais comme c'est un domaine que je ne maîtrise pas vraiment, je sais déjà que ça va être un gros morceau
    Rust fanboy

  4. #4
    Après, chacun code à sa façon, mais si tu souhaites réellement faire quelque chose de portable et de facilement extensible, je ne peux que te conseiller de faire beaucoup, beaucoup de polymorphisme.

    Par exemple, tu pourrais créer une classe de base abstraite dont hérite tous les objets de ton jeu (sprites, terrain, primitives etc), qui, dans ses propriétés, dispose d'un objet de la classe abstraite "DisplayMode" qui dispose d'une méthode virtuelle pure "Display()" pour afficher l'objet dans ta scène.
    Ainsi, tu pourras créer autant de classe fille de "DisplayMode" que tu souhaites, pour chaque moteur ou façon d'afficher ton objet (glBegin/end, VBO, shaders, différentes version de OpenGL, DirectX, moteur maison etc) sans avoir besoin de rajouter des switch case illisible dans tout les sens.

    Bon, après, c'est vraiment juste mes 2 cents, je m'emporte un peu car moi aussi j'ai déjà un petit moteur fait main en c++ qui sommeille un peu pour l'instant, et que je reprendrai peut être plus tard si j'arrive à percer dans le milieu ou à devenir self entrepreneur (rêve).

    Edit : Et je te comprends tout à fait dans ta démarche et je suis content de voir que je ne suis pas le seul qui aime mettre les mains dans le cambouis et qui trouve ca bien plus gratifiant que d'utiliser des trucs déjà fait /o/
    Dernière modification par Cuthalion ; 27/03/2012 à 21h24.

  5. #5
    Sinon pour revenir au sujet, deux/trois trucs qui n'apparaissent pas mais que j'ai déjà codé : (oui parce que j'ai pas mis 2 mois pour afficher un pauvre truc vert avec un dessins dessus)
    - le son, je peux charger n'importe quel fichier musical (supporté par libavcodec) et le jouer
    - de manière plus générale, je peux jouer une vidéo, également grâce à libavcodec qui me décode ça
    - charger n'importe quel format d'image
    - charger une police de caractères et l'envoyer sous forme de texture où tous les caractères disponibles sont côte à côte ; l'affichage du texte n'est pas encore fait mais ce sera vite fait
    - j'ai commencé le code pour tout ce qui est GUI et menus
    Rust fanboy

  6. #6
    Tu as certainement plus d'expérience que moi j'en aurai jamais dans la création des jeux from scratch. Néanmoins, je trouve de mon humble point de vue que c'est plutôt dangereux que tu "finalises" les graphismes avant le gameplay. Je ne parle pas de la coiffure de ton héros, mais plutôt du type d'animation que ton sprite aura, sa grosseur, etc. Pour moi, ton gameplay et les mécanismes de ton jeu vont décider des graphismes. OK, tu vas faire du 2D/3D isométrique ou quelque chose approchant Populous 3, mais malgré tout la qualité des sprites, leurs animations etc vont dépendre du moteur. Pour prendre un exemple récent Sim City V va sortir l'année prochaine, et l'équipe des dèvs a d'abord développé le moteur de simulation. Ils ont fait des graphismes à l'arrache pour tester, n'empêche que la gueule des bâtiments, c'est ce qui va être fait en dernier (voire en DLC...).

    Il me semble que si tu choisis d'abord le thème de ton jeu, la grandeur des cartes et le nombre de pnj à l'écran, les ressources à allouer aux graphismes vont couler de source, tout comme la manière de programmer tout ça. Je crains de mon point de vue de noob que si tu t'occupes trop de l'affichage et de l'interface au départ et qu'elle s'avère inadaptée au milieu du développement cela risque fort de te faire rager.

    Mais ce n'est que mon avis, bonne chance dans tous les cas!
    I am Moanaaaaaaa !!! (et en version legit!)

  7. #7
    Tu utilises une API quelconque pour l'affichage avec OpenGL, ou bien tu codes ça from scratch?
    Citation Envoyé par Snakeshit Voir le message
    Mais comme on me l'a appris dans la Marine, plus les choses sont automatisées, moins ça consomme de cases plus vous en avez de libre pour choses utiles, comme penser à des filles dénudées .

  8. #8
    Citation Envoyé par Teto Voir le message
    Tu as certainement plus d'expérience que moi j'en aurai jamais dans la création des jeux from scratch. Néanmoins, je trouve de mon humble point de vue que c'est plutôt dangereux que tu "finalises" les graphismes avant le gameplay. Je ne parle pas de la coiffure de ton héros, mais plutôt du type d'animation que ton sprite aura, sa grosseur, etc. Pour moi, ton gameplay et les mécanismes de ton jeu vont décider des graphismes. OK, tu vas faire du 2D/3D isométrique ou quelque chose approchant Populous 3, mais malgré tout la qualité des sprites, leurs animations etc vont dépendre du moteur. Pour prendre un exemple récent Sim City V va sortir l'année prochaine, et l'équipe des dèvs a d'abord développé le moteur de simulation. Ils ont fait des graphismes à l'arrache pour tester, n'empêche que la gueule des bâtiments, c'est ce qui va être fait en dernier (voire en DLC...).

    Il me semble que si tu choisis d'abord le thème de ton jeu, la grandeur des cartes et le nombre de pnj à l'écran, les ressources à allouer aux graphismes vont couler de source, tout comme la manière de programmer tout ça. Je crains de mon point de vue de noob que si tu t'occupes trop de l'affichage et de l'interface au départ et qu'elle s'avère inadaptée au milieu du développement cela risque fort de te faire rager.

    Mais ce n'est que mon avis, bonne chance dans tous les cas!
    Oui tu as évidemment raison
    Je ne comptais pas finaliser les graphismes maintenant, mais je sais que quelque soit ma décision sur l'univers du jeu il me faudra un code pour dessiner un perso, un code pour dessiner un terrain, etc. et c'est sur ça que je bosse. Comme tu peux le voir la gueule de mon perso c'est un truc dessiné en 20 secondes sous paint.
    De même pour le GUI, je ne suis pas en train de choisir l'emplacement des boutons (c'est impossible, je ne sais même pas quels boutons je vais mettre) mais de créer un système qui va me permettre de créer facilement des boutons par la suite (d'ailleurs si mon système fonctionne, vous allez être gueule béante ). Au temps les éléments de gameplay (terrain, personnages, bâtiments, etc.) sont des choses qui changent rarement, au temps l'interface et les menus ce sont des trucs qui doivent pouvoir se modifier en deux temps trois mouvements

    Citation Envoyé par war-p Voir le message
    Tu utilises une API quelconque pour l'affichage avec OpenGL, ou bien tu codes ça from scratch?
    J'utilise juste GLEW pour me faciliter la tâche (j'ai oublié de le mentionner plus haut)

    En fait j'ai juste codé un "wrapper" (pour rendre transparentes certaines opérations) et mes différents éléments à afficher créent eux-mêmes leur vertexbuffer, indexbuffer, shaders et textures (avec évidemment une gestion des ressources pour ne pas charger deux fois la même texture)
    Ça paraît complexe, mais en fait pas du tout
    Dernière modification par Tomaka17 ; 28/03/2012 à 09h35.
    Rust fanboy

  9. #9
    Oui oui je vois!
    Citation Envoyé par Snakeshit Voir le message
    Mais comme on me l'a appris dans la Marine, plus les choses sont automatisées, moins ça consomme de cases plus vous en avez de libre pour choses utiles, comme penser à des filles dénudées .

  10. #10
    1 - Les outils de travail

    Comme je l'ai dit plus haut, j'envisage de faire de temps à autres des mini-"tutos" sur tels aspects du jeu, histoire de satisfaire l'instinct de professeur qui sommeille en moi
    Voilà donc le premier, sur tous les outils que j'utilise dans ce projet. C'est peut être un peu orienté "noob", donc désolé pour ceux qui ont déjà un peu d'expérience là dedans

    Je code donc ce jeu uniquement grâce au langage de programmation nommé C++

    Je créé des fichiers contenant des lignes de code, puis que je vais demander à un logiciel qu'on appelle un compilateur de transformer ces lignes en un vrai programme (opération nommée la compilation).

    Sublime Text 2

    Pour taper mon code, n'importe quel programme comme le bloc-note suffirait. Dans la pratique néanmoins il est très utile d'avoir quelques fonctionnalités comme le code qui se met en couleur (afin de repérer plus facilement les différents éléments), l'indentation automatique (encore une fois pour s'y retrouver), un rechercher-remplacer robuste (qui utilise les expression régulières), plusieurs onglets, etc.

    Ainsi j'utilise Sublime Text 2, qui est un éditeur assez peu connu mais très complet.
    Le choix de l'éditeur c'est surtout une question de feeling personnel, et moi j'aime bien celui-ci. Il propose pas mal de raccourcis sympas, il a un superbe look, permet de sélectionner plusieurs choses simultanément, et bien d'autres encore.

    Visual C++ 2010

    La prochaine étape est de compiler le projet. Pour cela, je donne tous mes fichiers à un compilateur, je lui indique ce qu'il doit faire avec, et il me sort un programme en .exe prêt à être exécuté.

    Il existe de nombreux compilateurs C++ sur le marché, mais pour ma part j'utilise Visual C++ 2010 (dont la version express est gratuite). Celui-ci a l'avantage d'être très performant et de proposer un débuggeur (logiciel permettant de diagnostiquer les erreurs dans un programme) très simple à utiliser.

    La version 11 de Visual C++ est actuellement en beta. Je compte passer à cette version dès qu'elle sortira en version définitive.

    CMake

    Dans le processus de création d'un fichier exécutable, il ne suffit pas simplement d'exécuter le compilateur, mais il faut également configurer celui-ci selon certaines préférences. Par exemple je peux demander au compilateur d'inclure ou non les informations permettant de débugger le programme, ou alors lui demander d'activer certaines optimisations ou non.

    Le problème c'est que chaque compilateur se configure différemment, il n'existe pas de "fichier de configuration universel" qui fonctionnerait avec plusieurs compilateurs. Si jamais je voulais changer de compilateur (par exemple Visual C++ n'existe pas pour Linux, donc si je voulais compiler pour Linux il faudra que j'en change) il faudrait donc que j'écrive un deuxième fichier de configuration propre à ce second compilateur.

    Pour outrepasser ce problème, j'utilise CMake. Le principe est simple : j'écris un fichier de configuration en "langage CMake", et le logiciel CMake va le lire et créer un fichier de configuration pour le compilateur de mon choix.

    Si je donne mon code source à quelqu'un pour qu'il le compile, la première chose qu'il va devoir faire c'est lancer le logiciel CMake et ouvrir un fichier nommé "CMakeLists.txt" qui est fourni avec mon code source et qui contient toute la configuration de mon projet.

    CMake va alors demander à la personne quel compilateur il souhaite utiliser, puis va générer un fichier qui pourra alors être lu par le compilateur choisi.

    Git

    Il vous est certainement déjà arrivé de vouloir modifier un fichier sur un réseau, et de ne pas pouvoir le faire car quelqu'un d'autre a déjà ouvert ce même fichier. Comment font alors les studios de développement pour que plusieurs personnes puissent travailler sur le même projet ?

    Tout simplement ils utilisent un logiciel de gestion de versions. Un logiciel de ce type permet synchroniser de manière intelligente les fichiers. Plusieurs personnes peuvent modifier le même fichier simultanément, et le logiciel va essayer de fusionner les modifications. Cela ne fonctionne évidemment que pour les fichiers texte (cela tombe bien, le code source est du texte) et pas sur les images ou les vidéos par exemple.

    Évidemment cet aspect ne me sert pas à grand chose puisque je suis seul à travailler dessus. Néanmoins, les logiciels de ce type ont d'autres gros avantages, comme la conservation de tout l'historique de modification de chaque fichier, une copie de sauvegarde (évitant de perdre son travail si jamais on supprime un fichier par erreur), ainsi qu'un système de branches, qui permet par exemple de maintenir plusieurs versions différentes d'un projet

    Git est le logiciel de gestion de versions que j'utilise, car c'est actuellement celui qu'on considère le "plus abouti".

    GIMP et Inkscape

    Il va me falloir dessiner des choses en 2D, et comme je n'ai pas de budget , j'utilise GIMP et Inkscape, les deux logiciels de dessin gratuits les plus connus.

    Peut être d'autres...

    J'ai peut être oublié d'autres outils, auquel cas je les rajouterai ici


    Comment ça se passe en pratique ?

    Pour commencer mon projet, je créé donc un petit fichier texte "monProjet.cpp". J'ouvre celui-ci avec Sublime Text 2 et je tape mon code en langage C++.

    Je créé ensuite un fichier CMakeLists.txt qui contient toute la configuration du projet. J'ouvre ensuite le logiciel CMake, j'ouvre CMakeLists.txt, je choisis "Visual C++ 2010" et CMake me créé plein de fichiers que je peux ouvrir avec Visual C++. Je clique alors sur "compiler" Visual C++, et il me sort un fichier .exe correspondant à mon programme.

    Content de mon travail, je créé un "repository" Git (un espace de stockage) et je sauvegarde mes fichiers dessus. Ces fichiers sont désormais en lieu sûr.

    Dernière modification par Tomaka17 ; 30/03/2012 à 17h01.
    Rust fanboy

  11. #11
    Pourquoi tu codes pas directement sous visual studio? T'aime pas l'interface?
    Citation Envoyé par Snakeshit Voir le message
    Mais comme on me l'a appris dans la Marine, plus les choses sont automatisées, moins ça consomme de cases plus vous en avez de libre pour choses utiles, comme penser à des filles dénudées .

  12. #12
    Citation Envoyé par war-p Voir le message
    Pourquoi tu codes pas directement sous visual studio? T'aime pas l'interface?
    L'interface est bien, mais alors qu'est ce c'est lent à démarrer et qu'est ce que ça bouffe comme mémoire
    Et toutes les 10mn j'ai un popup "visual c++ packet manager has crashed"
    C'est une grosse usine à gaz

    De plus j'utilise également sublime text 2 pour d'autres trucs (comme le web developpment) et donc je préfère utiliser un seul éditeur
    Rust fanboy

  13. #13
    Okay! Bon, perso j'utilise visual studio 2010 ultimate, et j'ai pas de soucis particulier, l'intérêt étant principalement de développer avec .NET, après, c'est vrai que si tu fais que du C++, ça te sert à rien.
    Citation Envoyé par Snakeshit Voir le message
    Mais comme on me l'a appris dans la Marine, plus les choses sont automatisées, moins ça consomme de cases plus vous en avez de libre pour choses utiles, comme penser à des filles dénudées .

  14. #14
    Sinon en alternative à Visual studio, il y a Qt Creator qui est bien plus léger, dispose de tout ce qu'on est en droit d'attendre d'un IDE moderne, possibilité (forcément) d'utiliser ses cmake ou makefile assez facilement, et une interface que je trouve plutôt bien pensé pour coder en C++

    Son principal inconvénient à mon sens se trouve dans son nom, Qt Creator, et dans l'absence d'onglets qui peut rebuter au début.
    Dernière modification par Cuthalion ; 30/03/2012 à 20h18.

  15. #15
    Juste pour préciser, si je poste rien c'est pas que je glande, c'est juste que j'ai rien à montrer

    J'ai dit plus haut que je bossais sur le système de GUI et que vous alliez être gueule béante
    En fait c'est tout simplement parce que je suis en train d'écrire un petit moteur de rendu CSS (et que celui-ci commence à fonctionner)
    Une fois terminé je ferai un petit compte-rendu sur comment il fonctionne
    Rust fanboy

  16. #16
    Après un peu de réflexion, je suis parti sur les éléments suivants
    Il y a des chances que je change en court de route, mais dites moi ce que vous en pensez pour l'instant

    - le jeu se déroule dans un univers médiéval-fantastique
    - chaque joueur commence avec une citadelle (un bâtiment), la perte de la citadelle c'est le game over
    - dans la plupart des RTS, les parties sont des escarmouches, par exemple dans age of empires le scénario pourrait être "les français et les espagnols se battent pour le contrôle de ces terres fertiles", alors que dans ce jeu ce serait plus un conflit global, c'est à dire "france vs espagne, il n'en restera qu'un"

    La map :
    - le jeu se jouera "de loin" au niveau du zoom, c'est à dire plus comme RUSE que comme SC2/AoE
    - la map comportera des montagnes, des fleuves et des arbres ; les montagnes empêchent le passage, les fleuves et les forêts sont uniquement décoratifs
    - on y trouvera des emplacements prédéterminés où peuvent se construire des mines, avec des emplacements facilement accessibles par chaque joueur, et d'autres emplacements plus contestés

    Les unités :
    - contrairement à warcraft 3, ici les unités seront nombreuses et fragiles, un peu comme les zerglings de starcraft 2
    - les unités : villageois, infanterie, archer, cavalier, catapulte, l'espion
    - l'infanterie se bat à pied, coûte très peu
    - l'archer est très fragile, coûte un peu plus cher et a l'avantage de tirer à distance
    - le cavalier coûte cher mais pas excessivement, en faible nombre ils peuvent harass, en grand nombre ils font office d'infanterie sous stéroïdes
    - une catapulte coûte très cher
    - les unités ne sont pas directement contrôlables mais doivent être liées à une armée dirigée par un général
    - les généraux sont recrutables dans la citadelle
    - je ne sais pas encore comment vont se présenter les batailles, mais j'ai quelques idées que je détaillerai en temps et en heures
    - peut être une autre unité : la milice ; on pourrait convertir toute la population en milice pendant une courte durée

    Bâtiments :
    - il y a deux types de bâtiments : les gros bâtiments (citadelle par exemple) qui sont chères et ne se construisent qu'une fois chacun, et les petits bâtiments qu'on construit de nombreuses fois
    - les gros bâtiments sont accessibles directement via raccourcis, comme dans company of heroes
    - le centre de commerce (gros bâtiment) permet d'échanger d'acheter du fer à prix exorbitant ou recruter des soldats contre de l'or
    - la caserne (gros bâtiment) permet de recruter les unités
    - la forge (gros bâtiment) permet d'upgrader les unités contre du fer et de l'or
    - le centre d'espionnage (gros bâtiment) permet de recruter des espions
    - la banque (gros bâtiment) fournit chaque seconde un petit montant d'or ; en payant une grosse somme on peut "upgrader" la banque pour augmenter le montant gagné
    - le petit bâtiment "mine de fer" ne se construit que sur les emplacements de mine de fer (merci captain obvious) et ramène du fer
    - la maison (petit bâtiment) se construit par pelletées et ne coûte presque rien
    - des tours de garde (petit bâtiment)
    - peut être des murs

    Les ressources :
    - l'or, la population (c'est une ressource qui monte et qui descend, pas juste un maximum d'unité), le fer
    - à intervalle régulier, la population augmente d'un montant égal au nombre de maisons que possède le joueur
    - chaque seconde, l'or augmente d'un montant égal à la quantité de population moins le nombre de soldats que l'on possède
    - l'or augmente également si le joueur possède une banque
    - il y a deux styles d'économie : celle basée sur de grosses quantités d'or (on recrute ses soldats au centre de commerce) et celle basée sur l'or et le fer
    - les unités coûtent soit un peu d'or, de pop et de fer dans la caserne, soit beaucoup d'or dans le centre de commerce
    - les bâtiments consomment de l'or et de la population pour se construire

    Divers :
    - j'aimais bien le système de decks de AoE3, c'est à voir
    Rust fanboy

  17. #17
    Dans tes choix de game design tu parles de conflit global + RUSE. Tu vas faire un système de conquête de zone, ou bien, ça se passera autrement?
    Citation Envoyé par Snakeshit Voir le message
    Mais comme on me l'a appris dans la Marine, plus les choses sont automatisées, moins ça consomme de cases plus vous en avez de libre pour choses utiles, comme penser à des filles dénudées .

  18. #18
    Jvais faire ça comme dans les RTS classiques, si l'armée ennemie détruit ta mine ben t'as moins de ressources et si elle arrive dans ta base principale ben t'es un peu dans la merde
    Rust fanboy

  19. #19
    On va pouvoir convertir les catapultes à une quelconque religion comme dans age of empire ?

  20. #20
    J'ai doucement commencé à travailler sur le terrain ce matin, mais mon debugger OpenGL plante quand j'essaye de voir les textures, du coup je suis obligé de travailler à l'aveuglette
    Rust fanboy

  21. #21
    Bon ben cet après-midi j'ai fait le système d'inputs bruts, c'est à dire que je peux maintenant détecter quand on appuie/relâche une touche du clavier, quand on bouge/scroll/clique avec la souris ou quand on appuie sur un bouton/bouge un axe d'un joystick/joypad/volant quelconque, même si ce dernier aspect ne servira pas à grand chose pour un RTS

    En fait j'ai découvert que DirectInput (le truc que je voulais utiliser au départ) est déconseillé pour les nouveaux projets, et qu'ils recommandent plutôt d'utiliser l'API raw input, du coup c'est ce que j'ai fait

    À noter que la détection des inputs (c'est à dire les boutons appuyés et les mouvements de souris) et les inputs de texte sont deux choses différentes. Par exemple quand j'appuie sur 'A', je reçois un message qui me dit qu'on a appuyé sur la touche numéro je-sais-pas-combien (chaque touche du clavier a un numéro appelé "scancode"), puis un autre message qui me dit qu'on a écrit la lettre 'a'

    Si maintenant j'appuie sur '`' par exemple je vais recevoir un seul message me disant qu'on a appuyé sur la touche numéro je-sais-pas-combien. Ensuite si j'appuie sur 'U' je vais recevoir un message me disant qu'on appuyé sur la touche numéro trucmuche et un deuxième message me disant qu'on écrit un 'ù'.


    Heureusement on peut toujours demander à windows de nous indiquer le nom correspondant à un scancode (afin d'afficher ce nom dans la fenêtre de configuration par exemple), et à ce moment là windows va répondre différemment selon si c'est du qwerty, du azerty ou autre chose


    Au niveau du terrain j'étais parti sur un truc à base de heightmap, mais je me demande si je vais pas tout bêtement indiquer les emplacements/tailles des montagnes, des rivières, etc. dans un simple fichier et générer le terrain pseudo-aléatoire au moment du lancement de la map (évidemment la seed serait toujours la même pour éviter que la map soit différente à chaque chargement)
    Dernière modification par Tomaka17 ; 07/04/2012 à 22h38.
    Rust fanboy

  22. #22
    Le moteur de rendu CSS

    Comme dit plus haut, je suis en train de coder un moteur de rendu CSS pour ce jeu, en mode "mains dans le cambouis"
    Ça peut paraître très complexe vu comme ça, mais en découpant bien les différentes facettes, on s'en sort très bien

    Le moteur prend donc en entrée un document XML, va lire les styles qui lui sont associés, va prendre en compte les mouvements/clics de la souris, et va dessiner le tout à l'écran

    (note : si vous lisez ce post sans avoir jamais fait de XHTML ou CSS, vous n'allez rien comprendre)


    La hiérarchie XML

    Un document XML est une hiérarchie de noeuds (ce n'est pas moi qui le dit, c'est le DOM). Chaque noeud est soit un élément, soit un attribut, soit un texte, soit un CDATA, soit un commentaire. On va ignorer les attributs et les commentaires, et traiter les CDATA et le texte indifféremment. Il ne reste donc plus que deux types de noeuds : les éléments et le texte.

    Par exemple dans le code XML suivant "<p>Je m'appelle <strong>Tomaka</strong></p>" on a :
    * Un noeud de type élément <p>
    * Cet élément a deux fils : un noeud texte "Je m'appelle " et un noeud élément <strong>
    * L'élément <strong> a lui même un fils, un noeud texte "Tomaka"

    J'ai une classe "XMLDocument" qui se charge d'analyser le code XML et de le découper en différents noeuds. Parser du XML c'est assez "simple" à condition de passer beaucoup de temps sur les expressions régulières.


    Lire les styles

    L'une des choses que devra faire notre moteur de rendu est de déterminer quels styles sont appliqués à quels éléments. Le standard définit de manière très précise d'où viennent les propriétés appliquées à un élément.

    On va donc coder une fonction que j'appelle "buildSpecifiedValues" qui va prendre comme paramètre un noeud XML et qui va renvoyer un tableau de toutes les valeurs CSS spécifiées par le créateur du XML ou de la feuille de style.
    Cette fonction va commencer par lire l'attribut "style" du noeud, s'il existe. Elle va également lire tous les sélecteurs de la feuille de style et filter ceux qui correspondent à notre noeud. J'ai pour cela une autre fonction nommée "doesSelectorMatch" qui va, grâce à des expression régulières, dire si oui ou non le noeud XML correspond au sélecteur demandé. Pour vous donner une idée, voici le début du code source de cette fonction (pas encore terminée).

    Une fois l'attribut "style" et les sélecteurs lus, la fonction calcule la "spécificité" de chaque élément, puis nous créé un tableau (de type "std::unordered_map<CSSProperty,CSSValue>" avec CSSProperty une énumération) associant à chaque propriété CSS une unique valeur. Seules les propriétés CSS ayant été écrites par l'utilisateur se trouve dans les tableaux générés par la fonction.


    Parser les valeurs des propriétés CSS

    Mais on va devoir à un moment donné utiliser ces valeurs, c'est à dire les interpréter pour appliquer leur effet. Si je vous dis "3px solid black", vous en tant qu'humain comprenez que c'est une bordure de 3px, de style "solid" et de couleur noire. Mais il peut y avoir énormément de combinaisons possibles, par exemple "attr(width px) solid hsl(160,50,50)". Il nous faut trouver une méthode pour interpréter cela.

    Nous avons d'abord ce que j'appelle une "valeur CSS", par exemple "3px solid black". Cette valeur est composée de trois "valeurs individuelles" (c'est le nom que je leur donne) séparées par des espaces : "3px", "solid" et "black".
    On peut remarquer qu'en CSS, à l'exception d'une seule propriété qui utilise le slash (je ne sais plus laquelle), toutes les valeurs peuvent s'écrire "x" ou "x x x x" ou encore "x x x, x x x, x, x x" avec 'x' une valeur individuelle.
    En C++, j'ai donc un type "CSSValue" qui est un "std::vector<std::vector<CSSIndividualValue>>" . Il s'agit d'un tableau à deux dimensions, la première dimension est pour la séparation par virgule, et la deuxième est pour la séparation par espaces.
    J'ai codé une fonction nommée "parseValue" qui prend comme paramètre une string et qui me renvoie une CSSValue.

    Quant aux valeurs individuelles, cela peut être "5%", "15px", "rgb(50,50,50)", etc. J'ai donc une fonction "parseIndividualValue" (utilisée par parseValue notamment) qui va m'analyser ce truc et me sort un objet de type "CSSIndividualValue".

    Mais comment faire quand on a "attr(width px)" par exemple, qui pour rappel signifie "la valeur de l'attribute width en pixels" ? Et bien l'objet "CSSIndividualValue" est composé de plusieurs objets de type "std::function", en fait une fonction par type de donnée possible (longueur, couleur, image, durée, etc.)
    Pour connaître le type d'une valeur individuelle, il suffit de regarder quelles fonctions sont définies, puis d'appeler la fonction en question en passant le noeud concerné comme pointeur.

    Par exemple si j'appelle parseIndividualValue("5px");, la fonction va me renvoyer un objet CSSIndividualValue. Cet objet a tous ses membres à 0, mis à part la fonction "toLength", ce qui permet d'informer du type de la valeur. Ensuite j'appelle valeur->toLength(nodePtr); et la fonction me renvoie 5. De cette façon, je peux traiter tous les cas particuliers à base de "attr", "calc" ou "cycle".

    Les mots-clés sont gérés de la même façon, avec une fonction "toKeyword" qui renvoie une valeur prise dans un enum de tous les mots-clés du CSS.


    Specified, computed et used values

    Chaque propriété CSS passe par trois étapes. Lorsqu'on lit la valeur d'une propriété depuis la feuille de style, on appelle cela une "specified value" (en français : valeur spécifiée).

    La deuxième étape consiste à convertir toutes les specified values en "computed values" (en français : valeurs calculées). Cela signifie concrètement que l'on va remplacer tous les mots-clés "inherit" et "initial" par leur véritable valeur, qu'on va remplacer toutes les valeurs relatives (par exemple les valeurs en em) par des valeurs absolues, ainsi que différents autres traitements en fonction de la propriété.

    Toutes les propriétés qui ne sont pas dans la liste des propriétés "specified" sont ajoutées, afin que les computed values soient l'ensemble des propriétés CSS existantes. On supprime également les raccourcis (comme "border", "background", "font", "margin", etc.) en les découpant.

    En fait vous remarquerez que pour créer la liste des "computed values", on a besoin des computed values du noeud parent. C'est là même la définition des "computed values", c'est la transformation des valeurs en prenant en compte les valeurs du noeud parent.

    La troisième étape consiste à convertir les "computed values" en "used values" (en français : valeurs utilisées). Pour calculer les used values, on va avoir besoin des used values des enfants cette fois-ci, ce qui signifie qu'il faut calculer les used values de bas en haut.

    La plupart des propriétés ne changent pas de valeur lors de la conversion en used values. Ce sont principalement les propriétés "width", "height", "margin", etc. quand elles sont mises sur auto qui sont calculées à ce moment là.

    Le calcul des valeurs se fait donc en plusieurs étapes : d'abord on créé la liste de toutes les specified values pour chaque noeud. Ensuite on descend la hiérarchie en calculant tous les computed values. Enfin on fait un deuxième passage, mais à l'envers cette fois (de bas en haut), pour calculer les used values.

    Dans la pratique, plutôt que de calculer tous les computed values d'un coup et tous les used values d'un coup, j'ai codé des fonctions "getComputedValue" et "getUsedValue" qui vont calculer la valeur d'une seule propriété à la fois. Une fois le calcul effectué, elles stockent le résultat dans un cache de façon à ce que la prochaine demande soit beaucoup plus rapide. Il ne faut en revanche pas oublier de vider le cache lorsque le document XML est modifié.


    La hiérarchie dans notre moteur CSS

    L'autre tâche que devra effectuer notre moteur de rendu CSS, c'est de transformer la hiérarchie XML qu'on lui donne en une hiérarchie pouvant être dessinée à l'écran.

    Il y a deux types de noeuds XML (élément et texte) et il y a également deux types de noeuds CSS : block-level et inline-level. Un noeud texte en XML est forcément inline-level, et un noeud élément peut être les deux (cela dépend de la valeur de sa propriété display).
    Par exemple les éléments de type <strong> sont typiquement inline-level, alors que les <div> sont typiquement block-level. À noter que les noeuds dont la propriété est "inline-block" sont inline.

    Il y a un troisième type de noeud CSS : les lignes. Ces noeuds ne sont pas visibles et servent en fait à nous faciliter la tâche.

    Voici les deux règles importantes à suivre :
    - un noeud block-level ne peut contenir que des enfants de type block-level ou que des enfants de type ligne, mais jamais de inline-level ou de mix de plusieurs types.
    - un noeud ligne ne peut contenir que des enfants inline-level

    Si on reprend l'exemple plus haut "<p>Je m'appelle <strong>Tomaka</strong></p>", cela sera découpé comme suit :
    - un noeud block-level qui correspond au <p>
    - celui-ci contient un noeud ligne
    - le noeud ligne contient deux enfants inline : le "Je m'appelle " et le <strong>

    Si jamais on redimensionne la fenêtre pour la rendre toute petite, on pourra créer d'autres noeuds lignes, et mettre le <strong> dans le deuxième noeud ligne plutôt que dans le premier.

    Si on prend un autre exemple : "<div>je suis texte <p>je suis block</p></div>"
    Que va contenir le <div> ? Des blocks ou des lignes ? Et bien on va en fait englober "je suis texte " dans un block anonyme (c'est à dire qui ne correspond à aucun noeud XML).
    Le <div> contiendra donc deux enfants de type block : le premier anonyme qui contient des lignes qui contiennent "je suis texte ", et le second qui correspond à <p>, qui contient lui aussi des lignes qui elles contiennent "je suis block".

    Les blocks anonymes servent également aux pseudo-éléments CSS, comme ::before, ::after, ou pour rajouter le petit marqueur devant les éléments d'une liste <ul> ou <ol>.

    J'utilise le même principe de cache que pour les valeurs, c'est à dire que j'ai une variable nommée "childrenAreUpToDate", qui si elle est mise sur false, forcera mon programme à recréer la liste des enfants d'un noeud.

    Pour créer cette hiérarchie de noeuds CSS, on utilise la récursivité. On créé d'abord les enfants d'un noeud en particulier, puis on appelle la même fonction pour chaque enfant. Ainsi si je suis de type "block-level" et que je n'ai pas d'enfant XML de type block, je vais m'orienter vers des lignes. Si en revanche j'ai un enfant de type block, je vais m'orienter vers des blocks.
    Lorsque la fonction est appelée sur une ligne, celle-ci calcule sa longueur, et si celle-ci est trop élevée, se divise en deux.

    Cette méthode de découpage permet également de gérer le cas du ":first-line"


    Le dessin

    Dessiner la hiérarchie de noeuds CSS est la dernière étape qui n'est pas tout à fait au point dans mon moteur, d'où l'absence de screens.

    Il y a quatre choses à dessiner pour chaque noeud : l'arrière-plan, les bordures, le contenu, et l'outline (j'ai zappé ce dernier pour le moment, ça ne sert pratiquement à rien).
    Il suffit pour cela d'interpréter les used values des propriétés concernées et de créer des fonctions de dessin correspondantes. Encore une fois j'utilise un système de cache avec mes std::function. Lorsque le noeud change, je mets la fonction à 0, ce qui force un rafraichissement.


    Interaction avec la souris

    Ça je n'ai pas encore commencé
    En fait grâce aux matrices il est facile de déterminer sur quel élément se trouve la souris.
    Il suffit alors de vider le cache de toutes les valeurs et de recréer les specified values, en prenant cette fois en compte le :hover.
    Évidemment je vais stocker dans une variable une information selon si oui ou non il y a un changement quand la souris passe sur tel élément, sinon les performances risquent de chûter.


    Conclusion

    J'en suis à 835 lignes de code pour mon .cpp et 331 pour mon .hpp
    Je pense avoir bien cerné le sujet, c'est à dire que je ne pense pas me retrouver bloqué devant un obstacle

    D'ici la fin de la semaine je pense avoir un rendu correct (même si certains aspects comme les marges qui se collapsent, les images, etc. ne fonctionneront pas encore) mis à part peut être le texte, puisque celui-ci ne fonctionne même pas encore en stand-alone

    Bonne lecture

    ---------- Post added at 16h43 ---------- Previous post was at 16h05 ----------

    Par contre je sens que je vais devoir faire péter du screenshot si jveux ramener du monde sur ce topic
    Rust fanboy

  23. #23
    En tout cas, il y a l'air d'avoir un sacré boulot derrière. C'est clair que pouvoir faire des GUI sur base de HTML/CSS, ca m'aurait fait gagner pas mal de temps sur certains projets. Je ne pourrait que te conseiller de faire de ton parser/afficheur une bibliothèque séparée redistribuable.

  24. #24
    Ça peut se faire, mais il faut déjà que ça marche tout court

    Pour l'instant je suis en train de repenser l'algo qui créé la hiérarchie CSS, parce qu'il y a pas mal de cas qui bug à mort actuellement, comme "<em>Ceci est du <p>texte</p></em>", c'est à dire un block dans un inline

    J'hésite entre utiliser un seul shader pour bordure + arrière plan, ou bien faire deux dessins l'un au dessus de l'autre, mais ça c'est des détails
    Rust fanboy

  25. #25
    Par contre je rencontre un petit problème, c'est que ma méthode de gros bourrin est carrément vachement lente
    Alors certes je compile en debug et j'ai quelques grosses couilles niveau perf dans mon code, mais là ça met environ une demi seconde pour parser cette feuille de style qui n'est pourtant pas bien grande
    Et pire, ça met environ 2 secondes à créer toute l'arborescence et les fonctions d'affichage pour "<html>Je m'appelle <em>Tomaka</em></html>"

    Je crois qu'il va falloir que je fasse un peu de benchmark et que je multiplie les caches

    Bref, en attendant je pense que je vais passer sur autre chose, parce que mine de rien ça commence à faire beaucoup de temps passé là dessus

    EDIT : en fait j'ai déjà corrigé 2 "grosses couilles" à l'instant et c'est déjà beaucoup plus rapide
    Dernière modification par Tomaka17 ; 17/04/2012 à 00h01.
    Rust fanboy

  26. #26
    Tu pourrais montrer le bout de code avant et après optimisation? (Je suis très intéressé par des méthodes de parsing maison efficaces...)
    Citation Envoyé par Snakeshit Voir le message
    Mais comme on me l'a appris dans la Marine, plus les choses sont automatisées, moins ça consomme de cases plus vous en avez de libre pour choses utiles, comme penser à des filles dénudées .

  27. #27
    En fait les grosses couilles dont je parle sont assez simples

    J'avais un bout de code comme ça :

    Code:
    static CSSPropertiesSet defaultValues;
    static std::once_flag onceFlag;
    
    std::call_once(onceFlag, [&]() {
            defaultValues.insert(std::make_pair(CSS_PROPERTY_BACKGROUND_ATTACHMENT, parseValue("scroll")));
            defaultValues.insert(std::make_pair(CSS_PROPERTY_BACKGROUND_CLIP, parseValue("border-box")));
            defaultValues.insert(std::make_pair(CSS_PROPERTY_BACKGROUND_COLOR, parseValue("transparent")));
            etc.
    });
    
    return defaultValues.at(valeurDemandee);
    Normalement le système avec le once_flag fait que les insert sont appelés une seule fois pour remplir le CSSPropertiesSet une bonne fois pour toutes, et on utilise le même tout au long du programme

    Mais Visual C++ a un bug qui fait que ça ne marche pas (bug de compilation parce qu'il ne reconnaît pas mon énumération CSS_PROPERTY_machin, bref c'est un vieux bug à la con)
    Du coup j'ai temporairement viré mon système de once_flag, et les insert se faisaient à chaque fois ainsi que les appels de "parseValue"

    J'avais le même problème sur une autre fonction, et rien qu'en mettant un "if (properties.empty())" autour, le parsing de la feuille de style dure maintenant grosso modo un dixième de seconde (on sent un tout petit accrochage, j'ai pas mesuré précisément)

    Pour ce qui est de la création de la hiérarchie qui dure 2 secondes, j'ai pas encore regardé ce qui causait ça, mais ça me surprend car mine de rien je n'ai pas tant d'opérations que ça à faire


    Sinon niveau parsing là j'utilise pas mal d'expressions régulières qui sont ultra-lentes
    Mais je vais par exemple écrire un petit dérivé de find_if qui va me trouver le premier caractère demandé qui ne soit pas à l'intérieur de guillemets/parenthèses/crochets, et ça va me permettre de remplacer pas mal d'expressions régulières par ça

    Je vais aussi remplacer tous les paramètres "const std::string&" par "std::string::const_iterator, std::string::const_iterator" comme ça je lis à chaque fois la même chaîne de caractères, plutôt que de créer des string
    C'est toujours ça de gagné

    De toutes manières la méthode la plus rapide pour parser un truc c'est d'avancer lettre par lettre dans la chaîne de caractères, tu peux pas faire mieux je pense
    Rust fanboy

  28. #28
    Mais pour ceux que ça intéresse, le code source actuel du mon moteur CSS
    Rust fanboy

  29. #29
    En tout cas j'ai fait un petit profiling
    99.27 % du CPU consommé par mon moteur CSS c'est pour les regexp, au moins maintenant j'en suis certain
    Rust fanboy

  30. #30
    Bon ben après avoir supprimé toutes les expressions régulières : ~90ms pour parser la feuille de style et ~40ms pour créer toute la hiérarchie des trucs à afficher
    En fait plus haut j'ai dit que ça mettait 2 secondes à créer la hiérarchie, mais il y avait en réalité un bug, et une fois corrigé je me suis rendu compte que ça durait en réalité une trentaine de secondes (oui j'ai bien dit secondes, pas millisecondes), ce qui est colossal ; à mon avis l'implémentation des expressions régulières est foireuse parce que là c'est juste pas possible

    Je pense que les temps que j'ai maintenant sont pas mauvais ; je compile en debug (ce qui ralentit l'exécution) mais en contrepartie j'ai 4 Ghz dans le moteur, donc j'imagine que sur un PC un peu plus vieux les deux s'annulent
    EDIT : cela dit en release c'est tellement rapide qu'il me dit "0.00 secondes" pour le tout, la précision n'est pas assez grande
    L'inconvénient maintenant c'est que je vais devoir écrire quelques tests pour voir si tout est bien parsé comme prévu, car là c'est un peu plus acrobatique

    Faut voir aussi que là j'ai tous les caches que j'ai mis en place qui sont vides, c'est à dire que je créé tous les specified values, computed values, used values, etc. de tous les éléments. Lorsqu'on bougera simplement la souris il n'y aura pas tout à modifier, et donc avec un peu de bidouillage supplémentaire j'ai des chances d'atteindre le temps idéal qui serait de 3-4ms max lorsqu'on bouge la souris ou qu'on clique (une frame dure 16.6ms pour rappel)


    Mais quoiqu'il en soit je crois que je vais mettre ça en pause et commencer vraiment l'apparence du terrain, histoire d'avoir un truc visuel
    Dernière modification par Tomaka17 ; 17/04/2012 à 22h48.
    Rust fanboy

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •