1.Introduction

Grâces aux vacances suivant la dernière soutenance, le projet a pu être développé à plusieurs à raison de 24 heures par jour. Mais la rentrée a fait diminuer nos quotas de code se limitant à 10 heures par nuits. Des nouvelles fonctionnalités permettent de voir une évolution journalière comme les collisions, le tir, la création des bâtiments et unités, les ressources, et bien d'autres choses encore.

2. Guillaume
 
 

1) Editeur de cartes e42

L’éditeur de cartes est maintenant globalement terminé. Il en est à un stade ou la structure de base est solidement encrée, les améliorations possibles étant l’ajout de nouveaux décors, nouvelles unités, et d’un autre tileset.
Je vais ici vous présenter ce qui a été fait depuis la dernière soutenance.

Les montagnes (Higher Ground)

Pour pouvoir simuler le relief tridimensionnel dans 42 Minutes Pour Vivre, il est indispensable d’avoir une autre sorte de sol. J’ai choisi de reprendre le système de Patchs (présentes a la dernière soutenance) pour faire des montagnes, ou plutôt des hauts plateaux, sur lesquels nos unités pourront évoluer.
Comme pour les patchs d’herbes, j’ai choisi de reprendre des tiles de StarCraft (ils sont si biens). Mais cette fois ci, le système n’etait pas tout à fait aussi simple que pour les patchs d’herbe. En effet, au lieu d’avoir 6 zones de 4 tiles, les patchs de ‘higher ground’ comportent seulement 2 zones de 4 tiles ( C et D ), ainsi que 4 zones de 6 tiles :

Ce changement apporta deux difficultés a la réalisation du système :

  1. Beaucoup plus de tiles a extraire.
  2. Trouver les inter-dependances.
Pour ce qui est de l’extraction des tiles, j’ai fait cela régulièrement pendant la 1ere semaine des vacances, augmentant la taille de mon jungle.bmp (le fichier des tiles utilises par 42 Minutes Pour Vivre) de plusieurs centaines de kilo-octets tous les jours. Ce fut un travail long, répétitif et pénible, mais le résultat en vaut largement la peine.
Une fois tous les tiles nécessaires dans mon jungle.bmp, et indexes dans mon Edit42.dat (je rappelle que chaque tile a un index dans le fichier jungle.bmp, et que les différentes configuration de zones possibles sont donc regroupes dans un fichier txt, qui est lu au démarrage de l’éditeur dans un tableau ‘spécial patchs’), j’ai peu commencer à implémenter l’algo qui poserai des patchs sur la carte.
En fait, l’algo ressemble de très près a celui des patchs d’herbes : pour chaque zone, on regarde l’index d’un tile référence en dehors de cette zone, si l’index appartient aux indexes de high-ground, c’est que la zone est en train d’être pose à cote de tiles qui sont déjà en high-ground, et donc il ne faudra pas les modifier.
Mais en fait, on se rend vite compte qu’il y a des cas ou il faudra les modifier. Certaines configuration créent des petites zones de collisions entre les tiles :

Les zones rouges sur cette image sont les zones de collisions entre C-A et D-B.
Il faut dans ce cas avoir d’autres tiles sont en fait la combinaison des deux tiles en collision. Et ceci pour chaque configuration, car il y a beaucoup plus de collisions possibles, selon l’orientation des deux patchs, et je n’ai malheureusement pas eu le temps a consacrer à traiter tous les cas. Donc dans l’éditeur, certaines configurations génèreront des tiles qui casseront l’image générale. Cependant, j’ai fait en sorte de choisir les configurations qui viennent le plus souvent quand on fait des cartes.
Quand l’algo détermine qu’aucune des zones de tiles doivent être modifiées, les 4 tiles du milieu sont mis en Walkable et Closed, pour que l’on puisse mettre des unités dessus, et qu’elles ne puissent pas en sortir.
Dans StarCraft, on trouve un objet appelé Rampe que l’on peut poser sur le bord d’un high ground pour que les unités puissent en descendre et monter dessus sans avoir recours au transports aériens. Je n’ai encore ajoute cet objet, mais si le temps le permet, il sera la pour la soutenance finale.
Donc pour l’instant, si l’on pose des unités sur du high ground dans l’éditeur, elles n’ont aucuns moyens de descendre (grâce au superbe algo de recherche de cheminement d’Alexandre, qui gère les surfaces de tiles dites Closed, ou fermées du reste), et les unités en dehors ne pourront pas monter.
Par contre, les tiles en high ground on la propriété High, et donc les unités seront avantages si elles tirent de cette position vers des unités ennemis sur le sol.
Et il ne faut surtout pas oublier que le décor est un point important dans les tile-based-games, contribuant à créer une atmosphère réaliste, ce qui donne le joueur plus envie d’y participer.

Closed Surfaces

En parlant des surfaces Closed, j’ai aussi ajoute une option pour choisir des rectangles de tiles qu’on veut mettre à Closed, par exemple, pour fermer une zone entourée d’arbres. Si ce n’est pas fait, l’algo de cheminement du jeu ne sera pas content, et des figages peuvent avoir lieu pendant que l’ordinateur essaye de calculer le chemin sur toute la carte…

Création de bâtiments

Nous avons maintenant plusieurs bâtiments, et chaque bâtiment a été incorpore dans l’éditeur de carte.
J’ai développé un algo qui permet de voir si on a le droit de poser un bâtiment sur la carte.
Quand l’utilisateur choisi de créer des bâtiments, et qu’il bouge la souris au-dessus de la carte, le bâtiment est dessine, avec un quadrille vert ou rouge par-dessus chaque tile qu’il occupe.
Ceci est exactement identique a la méthode StarCraft.
Les quadrilles sont en fait deux petites images de 32x32 pixels, un quadrille vert sur du rose (couleur de transparence) et un quadrille rouge. Le bâtiment est rendu en dehors de la boucle de rendement normale, et on blit ensuite les quadrilles au-dessus des tiles. Pour déterminer si le quadrille utilise est le rouge ou le vert, on regarde tout simplement si le tile est Walkable ou non, ou s’il contient des unités.

Et, non cette image n’est pas une capture de StarCraft, mais de l’éditeur 42 Minutes Pour Vivre :) Cependant il est toujours plus facile de copier que de créer, mais comme l’expression artistique n’est pas vraiment prise en compte dans ce projet… De toute façon ce projet est sensé être un StarCraft-Like donc…
 

2) Direct3d

A la dernière soutenance, je prévoyais l’incorporation de Direct3d dans le projet pour pouvoir exploiter du matériel vidéo accéléré, notamment ma Voodoo3.
Direct3d est, comme DirectDraw, très puissant et facile a utiliser, une fois les frustrations et confusions engendrées par l’initialisation passées.
Mon but etait tout d’abord de pouvoir afficher une texture par dessus ma surface DirectDraw principale.
D’abord, il faut initialiser Direct3d :

DirectDraw.QueryInterface(IID_IDirect3D7,Direct3d);
Direct3D.CreateDevice(IID_IDirect3DHALDevice,BackSurface,Direct3dDev);

Ceci va m’attacher le device Direct3d a ma surface BackSurface, pour qu’au moment ou je fasse du rendu avec Direct3d, le résultat soit imprimé dans BackBuffer, que je flip ensuite sur l’écran. Le Direct3dHALDevice, c’est le Hardware Abstraction Layer device, c’est à dire l’abstraction de matériel. C’est le driver qu’il faut utiliser pour pouvoir accéder au capabilitées 3d accélérées de la carte vidéo.

Une texture Direct3d est tout simplement une surface DirectDraw, mais si on veut qu’une surface puisse servir de texture a Direct3d, il faut attribuer certains paramètres a son SurfaceDescripteur :

SurfaceDesc.dwFlags := DDSD_CAPS or DDSD_HEIGHT or DDSD_WIDTH or DDSD_TEXTURESTAGE or DDSD_CKSRCBLT;
SurfaceDesc.ddsCaps.dwCaps := DDSCAPS_TEXTURE;
SurfaceDesc.ddsCaps.dwCaps2 := DDSCAPS2_TEXTUREMANAGE;

Problème a noter, certaines cartes vidéo ne supportent pas tous ces paramètres. Si le temps le permet, je ferai une vérification du matériel 3d pour que l’utilisateur puisse choisir le meilleur rendu possible.
Ensuite, pour dire à Direct3d que c’est avec cette surface que l’on veut texturiser au prochain appel de la fonction de rendu, il faut appeler la fonction :

Direct3dDev.SetTexture(0,ShadowSurface);

Ensuite, avant de pouvoir dessiner, il faut définir :

Comme liste de vertex, j’ai tout simplement utilise un tableau de 4 points, vu que je veux afficher un sprite avec Direct3d, tous les composants Z sont nuls, et les vertex représentent donc un carré.
Pour les matériaux, rien de spécial, les défauts utilisés dans un exemple, c’est en fait un moyen de contrôler les couleurs émissives et ambiantes du sprite. Je mets toutes les valeurs a 1.
Une chose très importante quand on veut rendre des sprites avec Direct3d :
Le viewport. Sa hauteur et largeur doivent être égales a celles du sprite en question. Et il faut noter que Direct3d n’accepte que des textures qui ont des puissances de 2 pour ces valeurs (j’ai mis du temps a trouver cette information qui n’etait pas dans les docs du SDK).

Donc comme viewport j’utilise :

ViewPort.dwX := X;
ViewPort.dwY := Y;
ViewPort.dwWidth:= 128;
ViewPort.dwHeight:= 128;
ViewPort.dvMinZ := 0.0;

ViewPort.dvMaxZ := 1.0;
Direct3dDev.SetViewport(viewport);

Avec X et Y le point sur ma surface ou je veux dessiner le sprite, une image de 128x128 pixels.
J’avais besoin d’utiliser du matériel accéléré pour pouvoir faire du alpha-blending rapidement, mon algorithme en assembleur n’etait pas assez rapide avec DirectDraw.
Donc pour faire marche l’alpha-blending :

Direct3dDev.SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, 1 );

Voilà, maintenant je peux rendre mon image en ‘ouvrant une scène’, ensuite en appelant la fonction DrawPrimitive, et finalement ‘fermant la scène’. La fermeture de la scène envoyée la texture directement sur ma BackSurface.

Direct3dDev.BeginScene();
Direct3dDev.DrawPrimitive( D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX,ImgVertex, 4,0);
Direct3dDev.EndScene();

Les ombres avec DrawPrimitive

Contrairement a ce que j’avais réussi à faire manuellement en assembleur, il s’avérait impossible d’utiliser les fichiers images des hélicoptères directement pour faire des ombres sur le sol. Cédric m’a donc fait 2 fichiers d’ombres qui contenaient en fait les Une chose m’ayant pris un bon moment a trouver, c’etait les bons paramètres pour pouvoir transformer l’alpha-blending en système d’ombrage. Après une multitude d’essais, j’ai trouve qu’il faut mettre les paramètres du D3dRenderState: SrcBlend a 1 et DestBlend a 4.
Cela produit en fait, l’image négative de la source (l’ombre), mixé avec la destination (le décor), ce qui donne un assombrissement de l’image destination, mais juste dans les confinement de l’image source, ce qui nous donne l’ombre de l’hélicoptère sur le sol.

Direct3dDev.SetRenderState(41, 1);
Direct3dDev.SetRenderState( D3DRENDERSTATE_SRCBLEND, 1 );
Direct3dDev.SetRenderState( D3DRENDERSTATE_DESTBLEND, 4 );

Le premier SetRenderState allume la fonction Color-Keying, ce qui permet d’utiliser un sprite avec des contours transparents (rose).
Pour plus de details voir mon d3d.htm

Autres Utilisations

En jouant un peu avec les matrices de transformations et les viewports, j’ai réussi à faire un système qui dessine un sprite en boucle, tout en agrandissant ce sprite.
En fait, l’impression d’agrandissement est due au zoom dans l’axe z que je fais subir à l’image.
Avec cette méthode j’ai pu créer une espèce d’explosion bulle, qui sera en fait le tir orbital de du satellite SOL utilise par les américains dans le jeu (vous avez vu AKIRA ? Non ? Vous devriez :).
J’ai aussi crée une petite procédure qui fait un alpha blend additive sur tout l’écran, créant ainsi un flash, d’une intensité réglable en paramètre. Je l’utilise à la fin de l’explosion, pour tout faire fondre au blanc, et ce sera aussi utilise pour simuler des éclairs.

3) Effets Environnementaux

En parlant d’éclairs (pas ceux au chocolat, ni au café), je vais maintenant vous parler du petit système d’effets environnementaux que j’ai crée.

La Pluie

Pour simuler la pluie, j’ai tout simplement crée un tableau d’éléments ‘raindrops’.
Chaque raindrop contient des coordonnées écran x, y, et une longueur.
Dans une procédure Rain, qui est appelé dans la boucle de rendu principale, on ajoute à tous les raindrops.y leur longueur, et on les dessine à l’écran. Ce sont en faite tout simplement des petites lignes. Certaines plus longues que d’autres, les plus longues tombant plus vite que les plus petites, créant ainsi une impression de profondeur tridimensionnelle.
Pour chaque raindrop, si random(200) > 180, alors on réinitialise sa position sur l’écran, pour simuler la chute sur le sol. Ainsi, chaque raindrop a une probabilité de 20/200 = 10/100 = 1/10 = 0.1 de disparaître pour apparaître quelque part d’autre a la prochaine itération de la boucle de rendu. Ceci crée un effet assez réaliste de pluie, bien qu’il puisse être beaucoup amélioré..

La Neige

Pour simuler la neige, j’ai utilise presque exactement la même méthode que pour la pluie.
Sauf que, au lieu d’augmenter raindrops.y, j’augmente ou diminue aussi leur x, de façon aléatoire, créant des flocons qui tombent globalement vers la droite, mais pas de façon linéaire. En ce qui concerne la façon de les dessiner, ce sont pas des lignes ici mais deux ou trois pixels (aléatoire aussi) par flocon.

Les Eclairs

Ne sont pas encore actifs, mais le code Direct3d pour faire un flash est en place, il me reste plus qu’a faire des flashs de temps en temps.

4) Système d'écriture

Pour que le jeu puisse communiquer avec le joueur, il fallait mettre en place un système d’écriture, gérant une police de caractères qui pourrait être rendu sur la backsurface facilement a partir d’une chaîne.
Pour respecter notre tradition StarCraft-Like, j’ai lance StarCraft, et j’ai tapé tous les caractères ascii pour les voir à l’écran, puis j’ai fais une capture d’écran. J ‘ai ensuite mis tous ces caractères dans un fichier bitmap. J’ai donc un tableau de Trect qui contient tous les rectangles sur ce bitmap de tous les caractères. Donc pour afficher du texte, je prends la valeur ascii de chaque caractère avec la fonction ord, et j’utilise cette valeur comme index dans mon tableau de Trect, qui me renvois donc le rectangle source sur l’image du caractère en question. Ensuite il me suffit de le blit vers BackSurface

5) Le Panel

Le panel aussi a été extracté de StarCraft. Il est redessine à la fin de la boucle principale de rendement. Ensuite est dessine la minimap dans sa case réservée sur le panel, et ensuite les 9 icônes a droite du panel. Les icônes sont des petites images de 32x32 pixels.
Ils sont en fait gérés par un tableau de 9 bytes. Quand une unité est sélectionnée, ce tableau est mis à jour par rapport aux actions possibles de cette unité (ou du bâtiment sélectionné)
Quand le joueur click dans le panel, une procédure vérifie si la souris est au-dessus d’un des icônes, si c’est le case, on lance la procédure qui est demande par l’index de l’icône a cet endroit.
Par exemple si le Main Bunker est sélectionné et qu’on click sur l’icône du soldat, la procédure de création de soldats sera lancée.
 
 

6) Collisions d’unités

Alexandre ayant été un peu trop ambitieux dans sa gestion des collisions des unités, j’ai essaye de l’implémenter moi-même.
J’ai applique une méthode qu’il avait suggère mais que je croyais inefficace vu sa nature fortement calculatoire. C’etait ce principe :
Si une unité collisionée avec une autre (intersection des GroundRects), alors on recalcule le chemin avec l’algo de cheminement, en traitant le tile contenant l’unité de collision comme un tile non-walkable.
J’ai trouve un compromis pour ce système.
Il s’agit en fait de créer un nouvel état ‘attente’ pour les unités.
Le principe est le suivant :
Si l’unité de collision est immobile, alors on recalcule le chemin.
Si elle est en mouvement, alors, on met l’unité en attente.
Dans l’etat attente, l’unité va augmenter un compteur attente, et essayer de se déplacer.
Si elle arrive à se déplacer sans entrer en collision avec une autre unité, alors on la remet à l’état de marche, sinon, elle reste en attente, jusqu'à ce que le compteur arrive à une limite pre-definie au début du programme. A ce moment la, on met l’état de l’unité a 1, l’arrêt.
Ce système fonctionne correctement mais il y a encore présence de quelques bugs d’implémentation.

7) Les ressources

Les ressources sont gérées par trois indicateurs :

  1. Le pétrole
  2. L’électricité
  3. Les rations
Le joueur commence avec 1000 rations. Chaque unité consomme 1 ration quand un certain delay est atteint dans la boucle principale
Un petit icône rations est dessine en haut a droite de l’écran indiquant le nombre de réserves et le nombre delta, c’est à dire le changement a chaque delay. Par exemple s’il y a 5 unités, le delta sera –5.
Le même système est utilise pour l’électricité et le pétrole.
Chaque bâtiment consomme de l’électricité, sauf les générateurs, qui eux donnent un delta positif.
Il y a possibilité d’éteindre les bâtiments, en cliquant ON ou OFF dans les icônes.
Cela permet d’économiser de l’électricité pour pouvoir construire d’autres bâtiments par exemple.
Pour avoir plus de pétrole, on doit construire des camions citernes, qui iront chercher le pétrole a l’extracteur, puis le ramèneront.
 

8) Conclusion personnelle

Voilà, je sais que c’est un peu court et compresse cette fois, donc si vous avez des questions sur n’importe lequel de ces sujets, ou autre chose du projet, n’hésitez pas a m’envoyer un petit mail.