Le rapport de la soutenance finale
Transcription
Le rapport de la soutenance finale
Rapport de projet Julien BIRENE Anthony DESVERNOIS Pierre MARIAGE Kévin MATHIEU SimKingdom Rapport de projet Table des matières 1 Introduction 3 2 Le planning 2.1 Soutenance 1 . . . 2.2 Soutenance 2 . . . 2.3 Soutenance 3 . . . 2.4 Soutenance finale . . . . 3 4 4 4 5 . . . . . . . . . . . . . . . . . 6 6 6 10 10 11 16 17 20 20 24 25 27 28 29 30 31 32 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gameplay 3.1 Introduction . . . . . . . . . . . . . . . . . . . . . 3.2 Le menu . . . . . . . . . . . . . . . . . . . . . . . 3.3 L’interface . . . . . . . . . . . . . . . . . . . . . . 3.3.1 Introduction . . . . . . . . . . . . . . . . . 3.3.2 Les éléments permanents . . . . . . . . . 3.3.3 Les fenêtres . . . . . . . . . . . . . . . . . 3.3.4 Remplissage des fenêtres des conseillers . 3.4 Les sous-coeurs du jeu . . . . . . . . . . . . . . . 3.4.1 CoreBatiments . . . . . . . . . . . . . . . 3.4.2 CoreBudget . . . . . . . . . . . . . . . . . 3.4.3 CoreCreature . . . . . . . . . . . . . . . . 3.4.4 CoreCS . . . . . . . . . . . . . . . . . . . . 3.4.5 CoreMinistere . . . . . . . . . . . . . . . . 3.4.6 CorePopulation . . . . . . . . . . . . . . . 3.4.7 CoreScenario . . . . . . . . . . . . . . . . 3.4.8 CoreTech . . . . . . . . . . . . . . . . . . . 3.5 Le Coeur principal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Bâtiments 33 5 Site Web 35 6 Son 36 7 Moteur graphique 37 7.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 8 Moteur physique 41 Page 1 SimKingdom 9 Créatures 9.1 Introduction . . . . . . . . . . . . . 9.2 Parseur mdl . . . . . . . . . . . . . 9.2.1 Structure simplifiée du mdl 9.2.2 Le loader de créature (.mdl) 9.3 Format des bones . . . . . . . . . . 9.4 Attachement des bones aux vertex 9.5 Principe d’animation . . . . . . . . 9.6 Procédure principal de la créature 9.7 L’a-star . . . . . . . . . . . . . . . . 10 Editeur de cartes Rapport de projet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 44 45 45 46 50 53 53 58 60 69 11 Editeur de bâtiments 73 11.0.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . 73 11.0.2 De l’intérêt de la POO . . . . . . . . . . . . . . . . . . 73 11.0.3 La fonctionnalité avant tout . . . . . . . . . . . . . . . 76 12 Editeur de scénarios 76 13 Conclusion 78 14 Tableau récapitulatif : qui a fait quoi 79 Page 2 SimKingdom 1 Rapport de projet Introduction Jamais 3 sans 4 ! Nous voila déjà à la soutenance finale, qui va cloturer ce projet de première année de l’Epita. Ce rapport finale a pour but de décrire notre jeu, les différentes parties qui le compose, explique nos choix, les difficultés que nous avons rencontré et comment nous y avons pallié (ou pas), et ce tout au long de l’année. Nous l’avons organisé de la manière suivante : chaque partie du jeu est décrite par les membres du groupe qui ont aidé à sa réalisation (faut assumer dans la vie). Ainsi, chaque sous-partie indique qui à fait quoi dans le jeu. Afin de vous facilité la vie, un tableau récapitulatif est disponible à la fin de ce rapport. Info sur le rapport Pour plus de clarté (si si), les différents membres du projets seront nommés par leur user id (UID). A titre indicatif (au cas où vous ne connaîtriez pas nos UID par coeur) : BIRENE Julien DESVERNOIS Anthony MARIAGE Pierre MATHIEU Kévin 2 80018 80048 80120 80122 Le planning + ⊕ * Légende des tableaux Non abordé Ebauché Avancé Terminé Ajout au projet initial Page 3 SimKingdom 2.1 Soutenance 1 Gameplay Bâtiments Site Web Son Moteur graphique Moteur physique Créatures Editeur de cartes* Editeur de batiments* Editeur de scénarios* 2.2 Anthony Julien - Kévin Pierre ⊕ - - - ⊕ Soutenance 2 Gameplay Bâtiments Site Web Son Moteur graphique Moteur physique Créatures Editeur de cartes* Editeur de batiments* Editeur de scénarios* 2.3 Rapport de projet Anthony Julien + + + ⊕ ⊕ + Kévin Pierre Anthony ⊕ Kévin Pierre ⊕ + ⊕ + - Soutenance 3 Gameplay Bâtiments Site Web Son Moteur graphique Moteur physique Créatures Editeur de cartes* Editeur de batiments* Editeur de scénarios* ⊕ Julien ⊕ + ⊕ ⊕ ⊕ ⊕ + + ⊕ ⊕ + + ⊕ Page 4 SimKingdom 2.4 Rapport de projet Soutenance finale Gameplay Bâtiments Site Web Son Moteur graphique Moteur physique Créatures Editeur de cartes* Editeur de batiments* Editeur de scénarios* Anthony Julien ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ Kévin Pierre ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ Page 5 SimKingdom 3 Rapport de projet Gameplay 3.1 Introduction Le gameplay est une partie fondamentale du jeu, qui comprend le menu, l’interface, les différents sous-coeurs du jeu et enfin le coeur principal. Nous allons décrire dans cette partie comment ont été codées ces différentes parties du jeu et leurs rôles exacts. 3.2 Le menu Le menu du jeu à été réaliser de manière relativement simple. La position dans le menu est contenu dans une variable de type entier dénommée "menu" (dingue). Suivant la valeur de cette variable, on affiche une image différentes à l’écran. Ici par exemple on passe de l’image correspondant à la valeur menu = 1 (menu principal) à l’image correspondant à la valeur menu = 8 (crédits). F IG . 1 – le menu 1 F IG . 2 – le menu 8 Pour récuperer les clics de l’utilisateur, on a tout simplement mappé les Page 6 SimKingdom Rapport de projet différentes images. En fonction de la variable menu on applique les règles de mappage correspondante. Ici par exemple vous pouvez voir les règles de mappage pour le menu principal. if menu = 1 then {menu base} begin if ((xs>=135+((w-800)/2)) and (ys>=110+((h-600)/2)) and (xs<=380+((w-800)/2)) and (ys<=195+((h-600)/2))) then menu := 2; // jeu libre if ((xs>=420+((w-800)/2)) and (ys>=110+((h-600)/2)) and (xs<=690+((w-800)/2)) and (ys<=195+((h-600)/2))) then menu := 5; // scénario if ((xs>=420+((w-800)/2)) and (ys>=320+((h-600)/2)) and (xs<=690+((w-800)/2)) and (ys<=405+((h-600)/2))) then menu := 3; // options if ((xs>=135+((w-800)/2)) and (ys>=450+((h-600)/2)) and (xs<=380+((w-800)/2)) and (ys<=535+((h-600)/2))) then menu := 8; // crédits if ((xs>=420+((w-800)/2)) and (ys>=440+((h-600)/2)) and (xs<=690+((w-800)/2)) and (ys<=535+((h-600)/2))) then menu := 4; // quitter if ((xs>=120+((w-800)/2)) and (ys>=320+((h-600)/2)) and (xs<=380+((w-800)/2)) and (ys<=405+((h-600)/2))) then menu := 10; // charger if ((xs>=420+((w-800)/2)) and (ys>=205+((h-600)/2)) and (xs<=690+((w-800)/2)) and (ys<=295+((h-600)/2))) then menu := 12; // scores if ((xs>=135+((w-800)/2)) and (ys>=205+((h-600)/2)) and (xs<=380+((w-800)/2)) and (ys<=295+((h-600)/2))) then menu := 13; // reseau end Le mappage initial du menu (soutenance 2) à été réaliser par 80018. La quasi totalité des images ont été réalisé par 80122 aidé de 80120. Toutefois, certains éléments du menu nécessitant une interaction avec l’utilisateur plus poussé qu’un simple clic, nous avons réaliser des composants à l’image des composants proposé par delphi, mais utilisable sous OpenGL. Sur l’image qui suit vous pouvez voir le menu des options avec les composants t_listbox, les composants t_trackbar et le composant t_checkbox. Sur l’image suivante vous pouvez voir le menu jeu libre avec à nouveau le composant t_listbox et le composant t_edit. Page 7 SimKingdom Rapport de projet F IG . 3 – Le menu des options F IG . 4 – Le menu jeu libre Page 8 SimKingdom Rapport de projet Ainsi, le menu des options, le champ pseudonyme, ainsi que les listes des cartes et sauvegardes disponibles ont été réalisé à partir d’occurences de nos composants. Bien évidement, nos composants ont été programmé en objet. Le code qui suit est la déclaration du composant t_checkbox. T_CheckBox = Class private Position_x : integer; Position_y : integer; public Valeur : boolean; constructor create(mavaleur:boolean); procedure tracer(pos_x,pos_y:integer); procedure checked(cgauche:boolean;h,xs,ys:integer); end; L’ensemble des composants ainsi que leur implémentation à été réaliser par 80048. Le clic jouer (peu importe son origine) affiche toujours la même image de fond, invitant l’utilisateur à appuyer sur entrer pour lancer la partie. Par contre, le clic à modifier la valeur d’une variable booléenne. Si l’on était en jeu libre, la variable jouer passe à vrai, si l’on était en chargement, la variable charger_jeu passe à vrai, . . . . Lorsque l’utilisateur appuie sur la touche entrée, la classe du jeu est créer. La création de la classe différe en fonction du mode de jeu souhaité. La musique du mode jeu est lancé et la fonction principal de la classe jeu est lancée. Le code ci-dessous correspond au différent lancement possible du jeu en fonction de la variable booléenne correspondante. if jouer then begin Stop_Musique; jouer_musique('music/maps/map1.wma'); // Lance le Jeu Jeu := TJeu.Create(Login.Text); Jeu.Principal(ExtractFilePath(Application.ExeName) + 'Cartes/' + liste_map[ListMap.Selection]); Exit; end; Page 9 SimKingdom Rapport de projet if jouer_scenar then begin Stop_Musique; jouer_musique('music/maps/map1.wma'); Objectif := GetObjectif(ligneScenar[ListeScenario.Selection]); Jeu := TJeu.Create(Objectif,Login.Text); Jeu.Principal(ExtractFilePath(Application.ExeName) + 'Cartes/' + Objectif.Map+'.map'); Exit; end; if charger_jeu then begin Stop_Musique; jouer_musique('music/maps/map1.wma'); load.Charger( LigneSave[ListeSave.Selection ] ); Jeu := TJeu.Create(Load,Login.Text); Jeu.Principal( Load.Map ); Exit; end; Bien évidemment, le constructeur de la classe TJeu existe en plusieurs exemplaires via le mot clef "overload". La liaison entre le clic jouer et le jeu à été réaliser par 80122 et 80048. 3.3 3.3.1 L’interface Introduction L’interface du jeu est une sur-matrice du moteur graphique, c’est-à-dire que toute l’interface est dessiner dans un bloc compris entre glPushMatrix et glPopMatrix. Cette matrice à part est un espace bidimensionnel (j’aime bien ce mot) définit de cette manière : glfwGetWindowSize(width,height); glViewport(0,0,width,height); glPushMatrix(); glMatrixMode(GL_PROJECTION); glLoadIdentity; glOrtho(0, width, 0, height, -100,100); glMatrixMode(GL_MODELVIEW); glLoadIdentity; Page 10 SimKingdom Rapport de projet gluLookAt(0,0,0,0,0,-100,0,1,0); 3.3.2 Les éléments permanents Les éléments de l’interface sont des occurences des composants de l’interface, à savoir le composant minimap, ressource, selactiv (indique des informations sur l’objet selectionné dans la matrice 3D), tab_icone (menu d’icones déroulant), temps et vitesse. Le fond de l’interface quant à lui est une image, réalisé par 80120 et 80122. F IG . 5 – L’interface L’ensemble des composants de l’interface ont été réalisés par 80048. Tab Icones Le code qui suit est la déclaration du composant tab_icones. Tab_Icone = class private x,y,w,h,Ligne : integer; public NbElts : integer; Liste : TListe_Icone; constructor Create(py,pw,ph,pnb:integer); Page 11 SimKingdom Rapport de projet function Focus(ha,xs,ys:integer):boolean; function Action(ha,xs,ys:integer;pleft:boolean):integer; procedure Afficher(la,ha,xs,ys:integer;test:boolean); end; Le composant tab_icone prend donc une liste d’icones (de type TListe_Icone), les affiches et retourne (fonction Action) le numéro de l’élement cliqué, si élement il y a (elle renvoie -42 sinon). TMiniMap Le composant minimap permet l’affichage d’une minimap (halucinant). Il y a 2 affichages possibles pour la minimap : – L’affichage classique, qui fait apparaitre les batiments du joueurs en rouge, les arbres en vert, l’eau en bleu, la créature en blanc,. . . F IG . 6 – La minimap – L’affichage criminalité, qui fait apparaitre les habitations ou le potentiel de criminalité est élevé en rouge, en vert s’il est faible et en bleu les batiments administratifs. F IG . 7 – La minimap en mode criminalité Voici à présent la déclaration de ce composant : TMiniMap = class private x,y,tc:integer; Page 12 SimKingdom Rapport de projet carte : array [0..100]of array[0..100] of a_case; public procedure ajout_objet(px,py,tac:integer;type_objet:string); procedure set_crim(px,py:integer;b:boolean); procedure supprimer_objet(px,py:integer); procedure afficher_normal(px,py:integer;taille_carte:integer); procedure afficher_crim(px,py:integer;taille_carte:integer); procedure afficher_cadre(angle_z,x2,y2,zoom:real); function focus(ha,xs,ys:integer):boolean; end; SelActiv Le composant SelActiv est un simple composant qui affiche le type de batiments (créature, ressource) et des informations le concernant. Voici ce que ca donne pour un arbre, une maison, une mine d’or, une tour et une menuiserie. F IG . 8 – La sélection d’un arbre F IG . 9 – La sélection d’une maison Le composant SelActiv à été complété (créature) par 80122. Ressources Le composant ressources affiche l’état du trésors et le nombre d’habitant ainsi que la population maximal. Page 13 SimKingdom Rapport de projet F IG . 10 – La sélection d’une mine d’or F IG . 11 – La sélection d’une tour F IG . 12 – La sélection d’une menuiserie F IG . 13 – La barre des ressources F IG . 14 – La barre de temps (date) Page 14 SimKingdom Rapport de projet Temps Le composant temps permet l’affichage de la date (si si je vous jure). Voici sa déclaration : T_Timer = class private w,h,x:integer; mois,annee : integer; dmois : integer; public constructor Create(l,ha,year:integer); procedure Afficher(x,hauteur:integer;var temps:real;unite_temporel:integer); function GetMonth():integer; function GetYear():integer; procedure SetT(m,a:integer); end; Son fonctionnement est basé sur la fonction glfwGetTime et sur la valeur de la variable unite_temporel, qui est déterminée par le composant vitesse. F IG . 15 – Le composant vitesse Vitesse Le composant vitesse permet de régler la valeur de l’unité temporel, et donc la vitesse à laquelle vont défiler les mois et années. Voici sa déclaration et son implémentation dans l’unité de jeu. tvitesse = class private x:integer; ha : integer; defaut : integer; liste : array[1..4] of string; public constructor create(def:integer); procedure afficher(xx,h:integer); function focus(hauteur,xs,ys:integer;cgauche:boolean):boolean; function action(hauteur,xs,ys:integer;cgauche:boolean):integer; Page 15 SimKingdom Rapport de projet end; // vitesse.afficher(w-100,124); vtime := vitesse.action(h,x,y,pleft); case vtime of 1: MODE_PAUSE := true; 2: unite_temporel := 10; 3: unite_temporel := 5; 4: unite_temporel := 2; end; 3.3.3 Les fenêtres La totalité des conseillers du jeu sont accessibles via l’interface et sont affiché via des fenêtres dans l’interface. Etant donné le nombre de conseiller, et pour nous faciliter l’existence, nous avons crée un composant fenetre dont voici la déclaration : Tfenetre = class protected x,y : integer; largeur,hauteur : integer; titre : string; icone : GLuint; public ouvert : boolean; constructor create(ptitre:string;picone:gluint); overload; constructor create(ptitre:string); overload; function deplacer(h,pxs,pys,xs,ys:integer):T2i; procedure fermer(h,xs,ys:integer;cgauche:boolean); function focus(h,xs,ys:integer):boolean; procedure afficher(px,py,pw,ph:integer); function getPos():T2i; function getTaille():T2i; end; Ainsi, chacun de nos conseillers est associé à une fenêtre. Le remplissage de celle-ci est spécifique à chaque conseillers (unité conseillers.pas) et est géré de la manière suivante (pour le conseiller au culte) : Page 16 SimKingdom [...] Rapport de projet // Fenêtre Culte if fen_culte.ouvert then begin dopick := false; fen_culte.afficher(200+dfen_culte.i1,150+dfen_culte.i2,600,450); fen_culte.fermer(h,x,y,pleft); dtfen_culte := fen_culte.deplacer(h,pxs,pys,x,y); dfen_culte.i1 := dfen_culte.i1 + dtfen_culte.i1; dfen_culte.i2 := dfen_culte.i2 + dtfen_culte.i2; Ministere.PrintConseil(m5.texte,'e',222+dfen_culte.i1,480+dfen_culte.i2); Cons_Culte(TCulte,dfen_culte,Eglise,Cathedrale,graph_c1); end; De la même manière, le menu pause permettant de quitter le jeu, sauvegarder et revenir au menu est une fenêtre. F IG . 16 – Le menu échap La classe fenêtre à été élaborée par 80048. 3.3.4 Remplissage des fenêtres des conseillers Chaque conseiller affiche des données statistiques correspondant a son ministère. Ces données sont souvent synthétisé sous forme de graphique. Le système de graphique est ici encore un composant, dont voici la déclaration. TGraphique = class Page 17 SimKingdom Rapport de projet private x,y,largeur,hauteur:integer; titre,et_ab,et_or:string; maximum : real; procedure max(start,nbpts:integer); function focus(ha,xs,ys:integer):boolean; procedure vsouris(ha,xs,ys,C:integer;PH,U,r,g,b:real); procedure zpix(x,y:integer;r,g,b,val:real); public liste : array of Array of real; constructor create(ptitre,pab,por:string); procedure afficher(px,py,pw,ph:integer;lr,lg,lb:real); procedure ajouter_pts(courbe:integer;valeur:real); procedure dessiner_courbe(r,g,b:real;courbe,start,nbpts:integer); procedure reset_courbe(); end; On trouve aussi dans les fenêtre conseillers des composants tel que la t_checkbox et la t_listbox, comme ici : F IG . 17 – Le premier conseiller ou encore un t_compteur, qui permet (ici) à l’utilisateur de régler le taux d’imposition. Cette partie du remplissage à été effectué par 80048. Enfin, chaque conseiller affiche un conseil en fonction de la situation. La gestion de ce message (pré-enregistré) s’effectue dans l’unité CoreMinistere.pas. Cette partie du jeu à été codé par 80018, 80120 et 80122. Page 18 SimKingdom Rapport de projet F IG . 18 – Le Baron Rodot F IG . 19 – La conseillère auprès de la populace Page 19 SimKingdom 3.4 Rapport de projet Les sous-coeurs du jeu On dénombre 8 sous-coeurs : 1. CoreBatiments 2. CoreBudget 3. CoreCreature 4. CoreCS 5. CoreMinistere 6. CoreScenario 7. CoreTech 3.4.1 CoreBatiments Le core batiments et le sous-coeur qui gère les batiments (quelle surprise). L’ensemble des actions du jeux sur les batiments, de leurs constructions à leur destructions ainsi qu’un grand nombre de statistiques utilisent cette unité, qui comprend donc un grand nombre de procédure et de fonctions. On distingue dans notre jeu 4 type de batiments, codé sous forme de classe et disposons de caractéristiques communes et de caractéristiques spécifiques. L’ensemble de ces 4 batiments sont des enfants d’un type TBts (caractéristiques communes). Voici leurs déclarations : TBts = class protected icone : GLUint; nom : string; Prix : UPrix; function Check(tabcarte : TCarte ; t:string):integer; public tx,ty : integer; function GetIcone():GLUint; function GetName():String; function GetPrice():UPrix; function Construct(x,y,tc:integer):boolean; procedure ConstrucRemp(x,y,tc: integer; typeB : string); procedure SetTaille(l,h:integer); function VRessource(g,p,b:integer):boolean; end; TMaison = class(TBts) Page 20 SimKingdom Rapport de projet private nbh, capacite,unite,nbm : integer; public constructor create(G,P,B:integer;Capu,l,h:integer); procedure Add(); procedure Del(); procedure SetHab(t:integer); function GetOccupationRate():integer; function GetHabitants():integer; function GetCapacity():integer; function GetMaison():integer; end; TBatProd = class(TBts) private Workers : integer; NbBtsProd,Production,ValRessource : Integer; ListeBtsProd : TabBtsProd; ListeRessource : TabRessource; R :integer; function GetRessource(x,y,r:integer):integer; public NeedW,RW:integer; function PreP(x,y:integer):integer; constructor Create(G,P,B,ValProd,W,Rr:integer); procedure Add(x,y:integer); procedure Del(x,y:integer); function SetWorkers(t:integer):integer; procedure Pos_Ressource(ressource:string;TabCarte:TCarte); function GetProd():integer; function GetBtsProd():integer; procedure CheckAll(S,S2,S3:String); end; TBatUp = class(TBts) private Puissance,nb : integer; public constructor Create(G,P,B,Pp:integer); function GetP():integer; procedure Add(); procedure Del(); function GetCoef:integer; Page 21 SimKingdom Rapport de projet function GetNb:integer; end; TChateau = class(TBts) private Capacite : integer; R, Influence : integer; place : integer; ListeBts : TabBtsProd; ListeRessource : TabR2; procedure Pos_Ressource(); public function PreP(x,y:integer):integer; constructor Create(G,P,B,C,Rr,I,la,lo:integer); procedure Add(x,y:integer); procedure Del(x,y:integer); procedure Appliquer(MiniMap:TMinimap;nb_cases:integer); function GetProd():integer; function GetNb():integer; function GetC():integer; end; Nous avons donc 1. TMaison : les habitations : (a) Maison (b) Manoir (c) Villa 2. TBatProd : les batiments producteurs (a) Mine d’or (or) (b) Tailleur de pierre (pierre) (c) Bucheron (bois) (d) Eglise (mana) (e) Cathedrale (mana) 3. TBatUp : les batiments améliorant les productions (a) Fonderie (or) (b) Maconnerie (pierre) Page 22 SimKingdom Rapport de projet (c) Menuiserie (bois) 4. TChateau : les batiments administratifs (réducteur de criminalité) (a) Chateau I (b) Chateau II (c) Tour (d) Epita Pour être utilisé, le coeur principal crée à chaque partie une occurence d’un objet du core batiments pour chaque batiments. Cela donne donc dans moteur_graphique_map : Maison := TMaison.create(0,10,75,10,1,1); Maison2 := TMaison.create(40,160,175,10,2,2); Maison3 := TMaison.create(250,200,300,5,2,2); Chateau1 := TChateau.Create(0,300,300,20,5,1,6,6); // 1 Chateau2 := TChateau.Create(1000,600,600,40,8,1,6,6); // 1 Epita := TChateau.Create(15000,15000,20000,0,10,1,2,4); // 1 Tour := TChateau.Create(0,150,150,0,3,1,1,1); Bucheron := TBatProd.Create(0,25,200,1,6,2); MineOr := TBatProd.Create(50,50,200,1,6,2); MinePierre := TBatProd.Create(0,50,200,1,6,2); MinePierre.SetTaille(2,2); Eglise := TBatProd.Create(300,100,500,1,6,3); Eglise.SetTaille(2,2); Cathedrale := TBatProd.Create(2000,2000,1000,1,20,6); // 1 Cathedrale.SetTaille(6,3); Menuiserie := TBatUp.Create(50,50,300,2); Menuiserie.SetTaille(2,3); Maconnerie := TBatUp.Create(50,100,300,2); Maconnerie.SetTaille(2,2); Fonderie := TBatUp.Create(200,200,300,2); Fonderie.SetTaille(2,2); Le core batiment a été réalisé par 80048. Page 23 SimKingdom 3.4.2 Rapport de projet CoreBudget Le sous-coeur Budget est le coeur qui gère les ressources et qui permet de récolter les impôts. Toute la gestion du budget est réaliser à partir de la classe TTresors. Voici (pour changer) sa déclaration : TTresors = class private Impot : integer; Gold,Pierre,Bois,Priere : integer; TabRessource : array[1..6] of TabGraph; TabRessource2 : array[1..6] of TabGraph; public constructor Create(G,B,P:integer); function Get(t:integer):integer; procedure Add(t,v:integer); function Del(g,p,b,m:integer):boolean; function GetGraph(t:integer;t2:integer):TabGraph; procedure AddGraph(t:integer;v,v2:integer); procedure SetImpot(t:integer); function GetImpot():integer; procedure RecolterImpot(pop:integer); end; Le coeur budget permet par exemple de determiner si la construction d’un batiments et possible de le payer (Del), d’ajouter des ressources au trésors (Add), de récuperer les statistiques concernant le trésors, . . . Voici plusieurs exemples de son utilisation dans le coeur principal : – Pour la barre des ressources Ressource.Afficher((w div 2) - 250,h,Tresors.Get(1),Tresors.Get(3), Tresors.Get(2),Population.GetPop,Maison.GetCapacity+Maison2.GetCapacity +Maison3.GetCapacity+Chateau1.GetC+Chateau2.GetC,Tresors.Get(4)); – Pour la construction d’un batiment if Liste[indice_icone].val = 'Maison1' then begin CONSTRUCT_OK := (Maison.Construct (TabPick[ObjSelected].x,TabPick[ObjSelected].y, nb_cases))and(Tresors.Del(Maison.GetPrice.G,Maison.GetPrice.P, Maison.GetPrice.B,0)); if CONSTRUCT_OK then begin Maison.Add(); Page 24 SimKingdom Rapport de projet Maison.ConstrucRemp (TabPick[ObjSelected].x,TabPick[ObjSelected].y,nb_cases,'Maison1 end; end; Le core budget a été réalisé par 80048. 3.4.3 CoreCreature Le core Creature est le système qui gère tous les pouvoirs, bonus et influence de la créature. Ce core est activé lorsqu’une créature est affichée sur la carte. Sa déclaration est la suivante : TCoCreature = class private bonus : integer; public CreatureCore : TChateau; name : string; constructor Create(n: string; x,y,b, r: Integer); function GetBonus(batprod:TBatProd; Tresors: TTresors) : integer; function Pouvoir(cost, pv : integer; Tresors:TTresors) : boolean; end; Bonus La fonction GetBonus permet de calculer le bonus de production à ajouter aux batiments producteurs (Mine d’or, camp de mineurs ou bucheron) pour ensuite l’ajouter au trésor (or, pierre, bois) chaque mois : function TCoCreature.GetBonus(batprod:TBatProd; Tresors: TTresors) : integer; begin result := ceil(batprod.GetProd * (bonus/100)); end; Pouvoirs La fonction Pouvoir effectue l’un des pouvoirs de la créature : ajouter 50 de bois, ajouter 50 de pierre, ajouter 50 de bois. On obtient ces pouvoirs en cliquant sur la créature puis en cliquant sur l’un des 3 boutons (voir cidessous). On diminue bien sûr la mana en fonction du coup du pouvoir. Page 25 SimKingdom Rapport de projet function TCoCreature.Pouvoir(cost, pv : integer; Tresors: TTresors):boolean; begin if Tresors.Del(0,0,0,cost) then begin Tresors.Add(pv,50); result := true; end else result := false; end; F IG . 20 – Les pouvoirs de la créature Les icones sont affichés via un autre module : "composant_selactive.pas" qui gère la petite fenêtre de droite. Si on clique sur la créature, on affiche son nom puis les 3 images des 3 pouvoirs (non ? !), si l’on passe sur un icone avec le curseur, une bulle d’aide apparaît et indique la ressource qui va être ajouter au trésor. Influence Le principe d’influence de la créature est exactement celui du chateau. En effet, CreatureCore : TChateau permet de traiter la gestion de l’influence en reprenant un composant déjà codé en POO pour plus de facilité. . . (après tout un programmeur c’est feignant. . . ). Donc la créature peut être assimiler un chateau de taille 1, 1 avec une influence (rayon d’action) fixé à partir de la créature choisit. Il suffit d’appeler à chaque mois les fonctions suivantes pour gérer l’influence de la créature : if CreatureActiv then GestionC.CreatureCore.Appliquer(MiniMap,nb_cases); if CreatureActiv then Population.SetDCriminel(Chateau1.GetProd + Chateau2.GetProd + Epita.GetProd + Tour.GetProd + Page 26 SimKingdom Rapport de projet GestionC.CreatureCore.GetProd) else Population.SetDCriminel(Chateau1.GetProd + Chateau2.GetProd + Epita.GetProd + Tour.GetProd); Le core créature à été réalisé par 80122 et 80048. 3.4.4 CoreCS Le core CS est le système qui s’occupe du chargement et des sauvegardes. Plus précisement, cette unité contient la classe CS, qui permet la sauvegarde, le chargement et l’affichage du contenu de la fenêtre sauvegarde. Sa déclaration est la suivante : CS = class public Map,Pseudo : string; S:string; nb_cases : integer; Chaine : Array_of_string; Gold,Pierre,Bois,Priere,Impot, Hab,Mois,Annee : integer; Objectif : TObjectif; Scenario : boolean; function sauvegarder(filename,map,pseudo:string; nb_cases:integer;Tresors:TTresors; Population:TPopulation;Timer:T_Timer; mode_scenario:boolean;Objectif:TObjectif):boolean; procedure Charger(filename:string); function checkfile(x,y:integer;var Edit : t_edit;ha,xs,ys:integer;pleft:boo nb_cases:integer;Tresors:TTresors;Population: TPopulation;Timer:T_Timer;mode_scenario:boolean; Objectif:TObjectif;p_back:boolean):boolean; end; Le système de sauvegarde se base sur l’écriture dans un fichier *.sav dont la structure est la suivante : nom de la carte pseudo du joueur {------} x y type_du_batiment [...] Page 27 SimKingdom Rapport de projet {------} Or Pierre Bois Mana Tx_imposition Habitant {------} Mois Annee {SCENARIO} // seulemment en cas de scénario Objectif_Or Objectif_Pierre Objectif_Bois Objectif_Priere Objectif_Habitant Objectif_Bucheron ... Le système de chargement parse ce fichier et remplit ses variables publics en conséquences. Pour rappel, en mode chargement le jeu est crée avec une variable de type CS en paramètre. Cette variable seras ensuite utilisé dans le jeu pour le chargement de la carte, des différents bâtiments, du trésors,. . . Le coeur CS a été réalisé par 80048 avec l’aide de 80122. 3.4.5 CoreMinistere Le core ministère est le système permettant de générer des conseils adapté à la situation. Il se base sur les données recueillis des années précédentes (via les graphiques). Sa structure est la suivante : TCoreMinistere = class private public function Kristel(Population : TPopulation; maison, villas, manoir, bonheur function Benalli(Antresor, tresor, Anpierre, pierre, Anbois, bois : integer function Rodot(Antresor, tresor, taux : integer) : TProb; function Nath(maison, manoir, villa, bucheron, mineor, minepierre : integer function Petit(mana : integer) : TProb; function Boullay(m1, m2, m3, m4, m5 : boolean) : string; procedure PrintConseil(chaine,typ:string;x,y:integer); end; Voici un exemple de test réalisé en son sein (M. Rodot, conseiller des finances) : if (tresor <= 0) then begin str := str + 'Augmentez les impots ou construisez plus de mines d''or. Je vou bool := true; Page 28 SimKingdom Rapport de projet end else if (tresor < Antresor) then begin str := str + 'Votre tresorerie m''inquiete... construisez plus de bâtiments d bool := true; end else if (tresor > Antresor) and (taux >= 50) then begin str := str + 'Le taux d''imposition est tres eleve.. vous devriez songer a le bool := true; end else if (taux <= 50) then begin str := str + 'Le taux d''imposition est tres bas... Je suis pas specialement bool := true; end; Chaque fonction est appelé respectivement quand on appelle la fenêtre du conseiller, elle renvoie un enregistrement : TProb = Record texte : string; prob : boolean; end; Le texte est affiché dans le cadre du conseiller et prob permet d’afficher un message adapté avec la conseiller principal. Il a été réalisé par 80018, 80120 et 80122. 3.4.6 CorePopulation Le core population est, ô surprise, le système gérant la population. Voici la déclaration du composant TPopulation qui le peuple (facile) : TPopulation = class private FBonheur : integer; TBonheur,TDeces : integer; TabPop : TabGraph; TabBon : TabGraph; Chomeurs:integer; Page 29 SimKingdom Rapport de projet PCriminel,RCriminel:integer; public ICap : integer; NbH : integer; constructor Create(nb,bonheur,deces:integer); function Get:integer; procedure Cycle(capacite:integer); function GetPop:integer; function GetBon:integer; function GetGraphBonheur:TabGraph; function GetGraphPopulation:TabGraph; procedure SetBonheur(v:integer); procedure AddGraphe(v,v2:integer); function GetChomeurs():integer; procedure SetChomeurs(t:integer); procedure SetDCriminel(t:integer); function GetRCriminel:integer; end; On y trouve des fonctions et procédures essentielles du jeu tel que la procedure Cycle, qui permet chaque mois (elle est appelé chaque mois) d’augmenter ou diminuer la population en fonction du bonheur. La classe TPopulation contient aussi le nombre de criminel potentiel et réelle, le nombre de chomeurs,. . . Le core population a été réalisé par 80048. 3.4.7 CoreScenario Le core scénario. . . Enfin bref, il contient l’enregistrement TObjectif et la classe TMission. L’enregistrement TObjectif stock les objectifs de la mission (ce qui est somme toute logique), voici sa déclaration : TObjectif = record Gold,Pierre,Bois,Priere,Habitants : integer; NbBucheron,NbMineOr,NbTailleur : integer; NbEglise,NbCathedrale,NbEpita : integer; NbChateau1,NbChateau2 : integer; Temps : integer; Map : string; end; La classe TMission permet : Page 30 SimKingdom Rapport de projet 1. l’affichage des objectifs du scénario 2. vérifier si le joueur à gagné 3. vérifier si le joueur à perdu Voici son code : TMission = class private Accompli : Array[1..13] of Boolean; public Objectif : TObjectif; procedure Init(); function TempsRestant(Tps_ecoule:integer):integer; function Check(t,v:integer):boolean; function CheckAll(Tresors:TTresors;Population:TPopulation;Bucheron,MineOr, MinePierre,Eglise,Cathedrale:TBatProd;Chateau1,Chateau2,Epita:TChateau) :boolean; procedure Afficher(dfen_obj:T2i;tps_ecoule:integer); end; Le sous-coeur scénario à été réalisé par 80048 avec l’aide de 80120. 3.4.8 CoreTech Le Core Tech. . . ah le core tech, tout une histoire. . . mais là n’est pas le propos. C’est dans ce fichier qu’est implémenté l’arbre des technologies, que voici : C’est donc le core tech qui gère l’affichage du composant tab_icones, en fonction de la situation (conformément à l’image). Voici un fragment de son code illustrant son fonctionnement : if (Chateau2.GetNb = 1) then begin inc(I); liste[I].texte := 'Villa '+IntToStr(Maison3.GetPrice.G)+' O, '+IntToStr(Maison3.GetPrice.P)+' P, '+IntToStr(Maison3.GetPrice.B)+'B'; liste[I].val := 'Maison3'; liste[I].id := 17; liste[I].bd := 22; end; inc(I); Page 31 SimKingdom Rapport de projet liste[I].texte := 'Tour '+IntToStr(Tour.GetPrice.G)+' O, ' +IntToStr(Tour.GetPrice.P)+' P, '+IntToStr(Tour.GetPrice.B)+'B'; liste[I].id := 2; liste[I].val := 'Tour'; liste[I].bd := 21; if (Chateau1.GetNb = 0)and(chateau2.GetNb = 0) then begin inc(I); liste[I].texte := 'Chateau I '+IntToStr(Chateau1.GetPrice.G)+' O, '+IntToStr(Chateau1.GetPrice.P)+' P, '+IntToStr(Chateau1.GetPrice.B)+'B'; liste[I].id := 3; liste[I].val := 'Chateau1'; liste[I].bd := 20; end; La fonction contenu dans ce sous-coeur (GetLS_Icone) renvoie donc un enregistrement TListe_Icone. Ce coeur a été réalisé par 80048 et 80122. 3.5 Le Coeur principal Le coeur principal du jeu se situe dans l’unité moteur_graphique_map dans la procedure Jeu de la classe TJeu. En réalité, il n’y a pas vraiment de Page 32 SimKingdom Rapport de projet coeur principal. Il s’agit plutôt de ligne de code dans la boucle principal de GLFW qui joigne les différents sous-coeurs ensembles, et qui font tourner le jeu. . . Donc bon, y en a de partout, et ca fonctionne, donc c’est super. . . Le "Coeur principal", enfin, moteur_graphique_map catégorie TJeu.Jeu à été codé par 80048, 80120 et 80122. 4 Bâtiments Le développement des bâtiments a principalement commencé entre la première et la deuxième soutenance, après qu’ils aient été bien définis. Nous utilisons des fichiers .obj, un format qui nous avons créé et qui se trouve être la liste des différens polygônes (carrés et triangles) qui composent les différents objets : bâtiments, arbres, pierre, etc... Chaque ligne est du format suivant : Nbr x y z R G B A Text, où : – Nbr est le nombre de sommets du polygône – x, y, et z sont les coordonnées relatives du sommet (où 0,0,0 est l’origine du repère dans lequel est placé le bâtiment, ce ne sont bien sur pas les coordonnés sur la map qui varieront continuellement) – R, G, B sont les 3 paramètres de la couleur à utiliser pour tracer la map dans le cas où il n’y a pas de texture – A est la transparence du polygône – Text est le numéro de la texture, 0 correspondant à l’état "pas de texture". Les textures sont effets chargées dans un tableau, puis désignées en fonction de leur position à l’interieur de celui-ci. Le tableau est défini dans un .pas à part, lui permettant d’être chargé une fois pous toute à l’ouverture du jeu et d’être utilisé sans modération au cours des parties. On arrive ainsi à des bâtiments très divers, de la simple maison (58 surfaces), au temple (280 surfaces). La réalisation des bâtiments a au début été assez laborieuse... il fallait en effet écrire à la main les sommets 1 à 1 dans le .obj, puis lancer le jeu pour voir l’aperçu. Le placement des bâtiments n’étant pas implémenté à cette époque, on devait également modifier le code source à chaque nouveau bâtiment pour l’afficher dans le jeu... Un peu avant la 3e soutenance est né l’éditeur de bâtiments, qui nous a permis un placement des points et une construction des textures bien plus aisés, avec un aperçu très simple également. La construction des bâtiments s’est donc vue nettement simplfiée, ce qui nous a permi de réaliser des bâtiments bien plus complexes et jolis. Page 33 SimKingdom Rapport de projet F IG . 21 – Un bâtiment simple : une maison F IG . 22 – Un bâtiment plus complexe : le temple Page 34 SimKingdom Rapport de projet La totalité des batiments ont été réalisé par 80018 (quelques corrections effectués par 80048 et 80120). 5 Site Web Nous avons voulu réaliser un site convivial, rapide, facile d’accès, dynamique et qui respecte les recommandations du W3C - www.w3c.org (World Wilde Web Consortium). Notre site a été développé grâce à Macromedia Dreamweaver et est codé seulement en XHTML et CSS. Nous n’avons pas utilisé de PHP ni de base de donnée par choix car cela necessitait l’installation d’un serveur local (par exemple apache) en cas de problème internet (ce qui nous est arrivé fréquemment lors des soutenances ...) et pour le rendu sur le CD-Rom final. De plus vu le peu de choses à gérer sur notre site, les pages PHP auraient eu peu d’utilité ... Pour rendre notre site plus vivant nous avons utilisé la technologie Flash de Macromedia. Le header du site comporte quelques effets de bande lumineuse. De plus, pour plus de facilité, le menu a été mappé grâce au Flash. Nous avons également essayé de rendre notre charte graphique le plus agréable possible en créant un design fait avec soin et approprié au contenu de notre projet dans un genre médiéval. Suite à notre note sur le site Web à la 3e qui nous a deçu nous avons décidé de le rendre plus vivant en ajoutant des images en rapport avec les jeux médiévaux sur chaque page du site : troll, chevalier, ogre ... Pour ce qui est du contenu nous avons séparé notre site comme suit : – Index : regroupe toutes les news concernant le projet Simkingdom et l’équipe Atlanteam, ainsi qu’en bas une description rapide du projet. – Projet : description plus complète du projet. – Liens : la page qui regroupe tous les liens qui ont été utilisé pour réaliser le projet, classés dans différentes catégories : Delphi, OpenGL, animations d’un modèle 3D, les quaternions (génial ...), EPITA. – Team : descriptif des membres du projet avec les photos, les rôles dans le projet ... Suivi de photos bonus : nos Tshirt Simkingdom personnalisés, et d’autres photos du groupe. – Screenshots : pleins de captures d’écran du jeu à différents stades du développement : soutenance 1 et 2, soutenance 3, version 81, version 135 (liés aux numéro de version de SVN) et la version actuelle du jeu. Page 35 SimKingdom Rapport de projet – Download : pour télécharger tout ce qui concerne le projet : goodies (fond d’écran, modules, cahier des charges), sources et pdf des rapports de soutenance, executables des versions présentées aux soutenances, des executables de versions stables et après la soutenance finale : les sources du jeu. F IG . 23 – Notre site web Le site web a été réalisé par 80122 avec l’aide de 80018. L’ensemble du groupe a participé a sa vie. 6 Son Le son a été intégralement développé avant la première soutenance, mais une amélioration y a été rajoutée il y a peu de temps. Nous avons utilisé les librairies FMOD qui proposent de très nombreuses fonctions simples à utiliser, mais aussi très utiles pour la plupart dès que l’on veut faire des choses un peu plus complexes que jouer une musique de fond. Nous avons à la base créé plusieurs fonctions, séparant les musiques, les effets sonores et les voix. Une lecture de notre fichier .ini de configuration nous permet de fixer le volume au démarrage du jeu (modifiable par la suite). Pourquoi des fonctions différentes ? Car on ne joue pas une musique de la même façon d’un effet sonore. En effet, une musique doit se repeter, et il peut-être utile de la mettre en pause, alors qu’une voix ou un effet sonore simple n’a pas besoin de se répeter ou d’être mis en pause Page 36 SimKingdom Rapport de projet aussi souvent, et au même moment. De plus, l’utilisation de 3 canaux différents nous permet de jouer une musique, un son et une voix en même temps. Voici les prototypes de nos différentes fonctions : procedure procedure procedure procedure procedure procedure procedure procedure procedure init_son; // Initialiser FMOD et les canaux init_son_var(m,s,v:integer); // Initialise les différents volumes jouer_musique(music:string); pause_musique; stop_musique; jouer_son(son:string); jouer_voix(voix:string); pause_son; pause_voix; Nous avons récemment mis en place un système permettant de jouer des playlist, variant ainsi les musiques au cours du jeu... Cette petite fonctionnalité rendra les parties, nous l’esperons, un peu plus agréables. 7 Moteur graphique F IG . 24 – Le jeu 7.1 Introduction Au début, le moteur graphique devait être simplement la partie graphique (sans blague) de notre jeu, une sorte de boucle de jeu. Elle était nommé "moteur_graphique_map" et contenait tous les appels des fonctions de traçage OpenGL. Mais petit à petit, nous nous sommes apperçu Page 37 SimKingdom Rapport de projet que cette boucle de jeu nous serait également utile pour faire tous nos calcults, action sur les objets, la créature. . . En clair, elle deviendrait la boucle principale du jeu qui appelle une multitude de fonctions (et procédures) mais pas simplements des fonctions qui tracent quelque chose à l’écran. On peut néanmoins décomposer le véritable moteur graphique en plusieurs sous parties qui sont induites dans le code de notre jeu par une décomposition en fonctions et procédures. Initialisation C’est dans le moteur graphique qu’à lieu l’initialisation d’OpenGL et de GLFW, ainsi que le réglage de la matrice 3D. L’initialisation du moteur graphique ainsi que les réglages ont été codé par 80122 et 80048. Rotation, déplacement Ce sont les fonctions bord, touche, zoom et angle de la classe TJeu qui permettent au joueur de se déplacer sur la carte. Il est aussi possible au joueur de revenir au centre de la carte en appuyant sur ctrl-o. L’ensemble de ces fonctionnalités ont été implémentés par 80122 et 80048. Skybox Une skybox a été réalisé afin de rendre le jeu moins froid et plus. . . normal ? La skybox est tout simplement une boite géante (de la taille maximale selon les paramètres de notre espace) avec des textures ciel pour le ciel et eau pour l’eau (trop fort). Traçage général Tout d’abord, la fonction de traçage générale : Tracer(ModPick : boolean) qui est une sorte de procédure chapeau qui appelle d’autres fonctions de traçage et qui indique si on est mode empilement d’objets dans la pile de noms ou traçage dans la fenêtre de sortie : – TracObj(ModPick) qui trace TOUS les objets présents sur la carte et qui appelle la fonction draw_object (qui sera vu plus loin . . . ) – Terrain(i,ModPick) qui affiche le terrain case par case Page 38 SimKingdom Rapport de projet F IG . 25 – La skybox – Et enfin, CreatureClass.Principal qui appelle la procédure principale de gestion de la créature qui effectue les animations, puis trace la créature. (voir la partie sur la Créature dans le rapport) Ces deux fonctions ont été réalisé par 80122. Les montagnes C’est dans le moteur graphique que les montagnes, et leur formes sont calculés. Etant donné que les montagnes ne change pas dans notre jeu, elles sont calculé une seule fois lors du début de la partie. La forme des montagnes dépend du nombre de case montagne adjacente : plus il y en as, plus le sommet est élevé. Tout les calculs nécessaire se déroule dans la fonction charger_map de la classe TJeu. Cette partie a été réalisé par 80122. Les composants Il faut également tracer tout ce qui est composants OpenGL : – – – – – – – Bouton Flèche Case à cocher Compteur Texte d’édition ListBox Barre de défilement Page 39 SimKingdom Rapport de projet L’interface Puis tracer tout l’interface qui regroupe des composants, des images (icones ou fond de l’interface) : 1 2 3 4 5 Barre des ressources. Affichage du mois et de l’année. Mini-map : indique la position des objets. Zone de selection du bâtiment que vous désirez construire. Zone pour faire défiler la liste des bâtiments si elle contient plus de huit bâtiments. 6 Zone d’information sur les bâtiments une fois construits. 7 Boutons pour accéder aux conseillers : Dame Kristel (F1), Baron Rodot (F2), Sir Benalli (F3), Lady Nath (F4), Pere Petit (F5), Lord Krisboul (F6). 8 Bouton pour accéder aux objectifs (F7). 9 Vitesse du jeu. Et tracer un fond de l’interface pour faire plus joli (ou en tout cas essayer). . . Les fenêtres Et enfin, il faut tracer tout ce qui est fenêtre. Page 40 SimKingdom 8 Rapport de projet Moteur physique Au début, nous pensions que nous allions gérer les collisions dans le moteur physique, ce que nous n’avons pas fait puisque nous avons trouvé une autre solution plus adapté à notre type de jeu : un astar qui gère le contournement des obstacles, ce qui c’est révélé très astucieux. Puis, nous nous sommes intéressé à "sur quoi pointe la souris ?" avant de trouver de la documentation sur le picking par nom, ce qui est devenu notre méthode pour gérer la sélection. En effet, au début, nous pensions repérer les coordonnées de la souris puis grâce à des calculs mathématiques avec l’angle de rotation, les déplacements sur les axes x, y et connaitre l’objet pointé par notre souris. . . Hum, finalement, cette méthode ne nous réjouissant pas vraiment (les mathématiques à haute dose ça donndes boutons.) nous avons poussé nos recherches et découvert le picking ! Le picking par nom Pour sélectionner les objets sur la carte, nous avons donc utilisé la méthode du picking par nom. Le principe de cette méthode est le suivant : lorsque l’on trace notre scène, nous attribuons (enfin, OpenGL) des noms aux différents objets que nous désirons sélectionner et nous empilons nos noms dans une pile de nom gérée toute seule par OpenGL (c’est pas beau ça ?). Plus précisément, le picking par nom ce déroule en quatre étapes : 1 Passer en mode "sélection" grâce à la fonction glRenderMode(GL_SELECT), lui fournir un tableau à remplir (un buffer) et créer la pile de noms glPushName ; 2 Dessiner chaque élément voulu en lui attribuant un nom (GLint) pour chaque objet juste avant de le dessiner concrètement dans la fenêtre de rendu grâce à la fonction ou glLoadName ; 3 Sortir du mode "sélection" et récupérer les informations sur le picking (nombre d’éléments cliqué : hits et no de l’élément qui correspond à sa position sur la carte) ; 4 Gérer les informations fournies pour effectuer la sélection ou les actions sur l’objet. Page 41 SimKingdom Rapport de projet Le traçage des objets Le traçage s’effectue dans deux modes : le mode normal et le mode sélection. Le premier consiste à tracer nos objets avec leurs textures, couleurs. . . En appelant la procédure tracer avec le ModPick := False (variable booléenne qui indique si on affiche ou pas et si on empile le nom de l’objet). Après, il nous faut tracer les objets en mode "sélection" et stocker les noms dans la pile de noms d’OpenGL. Pour cela, on appelle la procédure Picking qui lance le mode "sélection", initialise le buffer de sélection puis trace les objets de la carte en mode picking, c’est-à-dire en appelant la procédure Tacer avec ModPick := True. Cela a pour effet de tracer les objets en empilant leurs noms dans la pile des noms. Les informations données par le picking Une fois que l’on a tracé les objets en mode picking, on récupère les informations stockées par OpenGL. Cette opération s’effectue en plusieurs étapes : 1 On récupère le nombre de hits ; 2 Si le nombre de hits est supérieur à 0 : – On donne à la variable Selection la valeur True pour effectuer les actions spécifiques dans la fonction Tracer pour l’objet sélectionné ; – On récupère ensuite le nom de l’objet sélectionné par la souris ; – On récupère le type de l’objet sélectionné dans tableau TabPick qui contient tous les objets de notre carte ; – On effectue l’action (plutôt plus tard ou immédiatement) correspondante à l’objet ; 3 On revient en mode de traçage normal Exemple Cette partie a été réalisé par 80122 avec l’aide de 80120. Page 42 SimKingdom Rapport de projet F IG . 26 – Curseur sur une case F IG . 27 – Curseur sur un objet (ici une maison) F IG . 28 – Quand on a cliqué sur un objet ! Page 43 SimKingdom 9 Rapport de projet Créatures 9.1 Introduction La créature a une fonction importante dans notre jeu : elle est l’incarnation du joueur dans le royaume. Grâce à elle, le royaume pourra jouir de nombreux privilèges : augmentation de la production, influence sur la criminalité, pouvoirs sépciaux ... Elle a aussi était implanté par une classe. Cette classe contient exclusivment la créature (modèle parsé et près à être affiché) et toute les actions que touchent à modifier le modèle 3D. Tout ce qui est gestion de la créature ou astar est codé par un autre partie (pour l’astar voir plus bas, pour la gestion voir CoreCreature) TCCreature = class private My_Creature : TCreature; TabVertex : Tab2TVertex_mdl; CurrAnim : TAnimCur; angle, scale : real; frame : integer; procedure reinit_tabvertex; // réinitialise le tableau de vertex à tracer procedure Animation_mdl; // Procédure d'animation procedure Traceur_mdl(Picking : boolean); // traceur procedure Deplacement(var DeplacB : boolean; ArrivC : TDeplac; var i :integer); function QuatRotat(Quat : TTransform) : TKey; public posx, posy : real; CSelectionnee : boolean; Move : TabTDeplac; constructor Create(x, y : real); procedure Parseur_mdl(NomMdl : string); // parseur procedure setAnim(index : integer; boucle : boolean); procedure Principal(Picking : boolean; var DeplacC : boolean; mil, x, y, fps : real; ArrivC : TDeplac; var i :integer); end; Page 44 SimKingdom 9.2 Rapport de projet Parseur mdl Notre créature est un modèle 3D qui fonctionne sur un mode indépendant au .obj cité plus haut. Elle est beaucoup plus complexe et surtout complète et fait appel aux animations. C’est le seul "objet" de notre jeu qui pourra se déplacer sur la carte grâce au clic du joueur ! Le déplacement est géré par l’a-astar (voir plus bas). Le format de notre créature n’est pas le .3ds ou le .ase comme de nomc breux projets mais le .mdl. Ce format a été "inventé" par Blizzardpour ses jeux Warcraft III et World Of Warcraft. Nous utiliserons nous des modèles exportés de Warcraft III. Le .mdl comporte de nombreux avantages par rapport au .3ds : – Format simplifiable très facilement selon nos besoin. – Ouverture et modification direct dans le Bloc Note ou tout autre éditeur de texte. Il ne nécessite pas l’ouverture de 3dsMax ou autre logiciel de 3D comme le .3ds. – Syntaxe assez simple à comprendre si l’on a déjà fait de la 3D. – De très nombreux modèles sont à notre disposition, de quoi afficher n’importe quelle créature fantaisiste sans se casser la tête à chercher partout sur Internet. – Nombreux tutoriaux sur le format .mdl dans la communauté francophone de Warcraft III. C’est ainsi que notre choix a été très vite fait au profit du format .mdl face au .3ds, de plus nous pouvons coder nous même notre loader mdl sans se compliquer ... 9.2.1 Structure simplifiée du mdl Le format mdl s’organise en 3 grandes parties : – Définition des animations et de la (des) texture(s) utilisée(s). – Définition du (des) Geoset(s). – Définition des bones et les PivotPoints. Un Geoset est une partie du modèle qui utilise une texture. En tout logique, 2 Geosets dans un modèle utiliserons 2 textures différentes, c’est pour celà qu’ils sont séparés. Un bone ou "os" est une articulation du modèle. Ils sont reliés entre eux et forment le squelette de notre modèle, ce qui permettra ensuite de l’animer ! Les PivotPoints ou "points de pivot" représente la position des bones. Page 45 SimKingdom Rapport de projet Pour le moment nous n’utilisons que la (les) texture(s) et une partie du (des) Geoset(s), les animations viendront plus tard. 9.2.2 Le loader de créature (.mdl) Notre loader de créature c’est à dire notre loader de .mdl parse le .mdl pour ensuite envoyer toutes les données à une procédure de traçage. C’est en gros le même principe que notre loader de .obj. La partie la plus importante à parser est le Geoset ! C’est lui qui contient toutes les informations nécessaires au traçage de la créature : – Les Vertices (ou sommets). – Les TVertices (ou sommets de texture). – Les Faces (triangle composé de 3 sommets sur lequel est appliqué une partie de la texture). C’est 3 parties sont une suite de nombres (en général des flottants). En général il y a autant de Vertices que de TVertices, car chaque sommet possède une coordonnée de texture. Les Faces sont toujours un multiples de 3 ! En effet, on récupère 3 numéros dans la définition Faces qui correspondent au n˚ des Vertices (et donc des TVertices) pour dessiner un triangle texturée. Ainsi, notre parseur stocke dans différents tableaux : les Vertices, les TVertices et les Faces. Notre créature est un enregistrement de ces 3 tableau plus d’un chaîne de caractères qui représente la texture. Nous utilisons des créatures à ou plusieurs Geoset avec animations ! Voici les types que nous avons utilisé pour notre créature : // Type vertex du mdl TVertex_mdl = Record x, y, z : real; end; // Type face du mdl TFace_mdl = Record V1, V2, V3 : integer; end; // Type texture du mdl TTVertex_mdl = Record x, y : real; Page 46 SimKingdom Rapport de projet end; // Tableau de vertex mdl TabTVertex_mdl = array of TVertex_mdl; // Tableau de tableau de vertex mdl Tab2TVertex_mdl = array of array of TVertex_mdl; // Tableau de texture mdl TabTTVertex_mdl = array of TTVertex_mdl; // Tableau de faces mdl TabTFaces_mdl = array of TFace_mdl; // Tableau de VertexGroup TabVertexGroup = array of integer; // Matrices TMatrices = array of integer; // Tableau de Matrices TabGroups = array of TMatrices; // Geoset TGeoset = Record Vertex : TabTVertex_mdl; // Vertex Textures : TabTTVertex_mdl; // TVertex Faces : TabTFaces_mdl; // Faces VertexGroup : TabVertexGroup; // VertexGroup Groups : TabGroups; // Matrices DirTex : string; // Textures end; // Tableau de Geosets TabTGeoset = array of TGeoset; On envoie ensuite dans le jeu toutes ces informations à la procédure de traçage qui récupère la texture et la charge (si on est pas en mode picking pour ajouter la créature dans la pile de nom), et lit toutes les Faces pour tracer un triangle. L’enchainement de triangles permet d’afficher notre créaPage 47 SimKingdom Rapport de projet ture. Elle effectue cette opération sur tous les geosets procedure TCCreature.Traceur_mdl(Picking : boolean); var i, j, nFace : integer; // Tableau de Faces Faces : TabTFaces_mdl; // Tableau de Textures TexVertex : TabTTVertex_mdl; begin // Sans pickng // if not(Picking) then begin {Autorise les textures} glEnable(GL_TEXTURE_2D); {-----------------------------} // Récupérations des Geoset // {-----------------------------} for i := 0 to length(My_Creature.Geoset)-1 do begin // Définition des variables TexVertex := My_Creature.Geoset[i].Textures; Faces := My_Creature.Geoset[i].Faces; // Utilisation de la texture du Geoset glBindTexture(GL_TEXTURE_2D, TexCreature[i]); // Récupération des faces // for j := 0 to length(Faces)-1 do begin glColor3f(1,1,1); glBegin( GL_TRIANGLES ); // Affichage de la face // // Vertex 1 Page 48 SimKingdom Rapport de projet nFace := Faces[j].V1; glTexCoord2f(TexVertex[nFace].x,1 - TexVertex[nFace].y); glVertex3f( TabVertex[i,nFace].x, TabVertex[i,nFace].y, TabVertex[i,nFace].z ); // Vertex 2 nFace := Faces[j].V2; glTexCoord2f(TexVertex[nFace].x,1 - TexVertex[nFace].y); glVertex3f( TabVertex[i,nFace].x, TabVertex[i,nFace].y, TabVertex[i,nFace].z ); // Vertex 3 nFace := Faces[j].V3; glTexCoord2f(TexVertex[nFace].x,1 - TexVertex[nFace].y); glVertex3f( TabVertex[i,nFace].x, TabVertex[i,nFace].y, TabVertex[i,nFace].z ); glEnd; end; end; {On désactive les textures} glDisable(GL_TEXTURE_2D); end // Avec picking // else begin {-----------------------------} // Récupérations des Geoset // {-----------------------------} for i := 0 to length(My_Creature.Geoset)-1 do begin // Définition des variables TexVertex := My_Creature.Geoset[i].Textures; Faces := My_Creature.Geoset[i].Faces; // Récupération des faces // for j := 0 to length(Faces)-1 do Page 49 SimKingdom Rapport de projet begin glColor3f(1,1,1); glBegin( GL_TRIANGLES ); // Affichage de la face // // Vertex 1 nFace := Faces[j].V1; glVertex3f( TabVertex[i,nFace].x, TabVertex[i,nFace].y, TabVertex[i,nFace].z ); // Vertex 2 nFace := Faces[j].V2; glVertex3f( TabVertex[i,nFace].x, TabVertex[i,nFace].y, TabVertex[i,nFace].z ); // Vertex 3 nFace := Faces[j].V3; glVertex3f( TabVertex[i,nFace].x, TabVertex[i,nFace].y, TabVertex[i,nFace].z ); glEnd; end; end; end; end; 9.3 Format des bones Pour réaliser les animation de la créature, nous avons dû utiliser la 2e grande partie des mdl : les bones, ils forment le squelette de la créature et permettent de l’articuler. Les sommets (ou vertex) sont attachés aux bones par un méchanisme qui sera étudié plus bas. Les bones sont composés ainsi : – Le geoset auquel il est attaché – L’index de son bone parent (bone auquel est "attaché" ce bone) Page 50 SimKingdom Rapport de projet – Les transformations : translation, rotation et homothétie Un bone peut contenir soit 1 type de transformation, soit 2 soit les 3. Chaque type de transformation possède une construction particulière et permet des modifications différentes sur les bones. – Les translation permettent de déplacer les bones sur les axes x, y ou z – Les rotations permettent de faire tourner les bones dans l’espace autour d’un axe – Les homothétie permettent d’agrandir les bones Au cours de l’animation, des transformations différentes sont appliquées à chaque frame sur les bones. Ainsi, les transformations des bones sont des tableaux qui contiennent plusieurs données de transformations. Les translations et les homothéties possède donc plusieurs données de transformation qui sont des enregistrements de 4 valeurs : – Un temps donné qui permet d’effectuer cette transformation à un moment précis dans la boucle d’animation – Une coordonnée x – Une coordonnée y – Et une coordonnée z Pour les rotations, ce sont des enregistrements d’un quaternion (extension des nombres complexes qui permet d’effectuer différents calculs sur les matrices et souvent utilisé dans les rendus et animations 3D, que du bonheur !) composés des coordonées (x, y, z, w) et d’un temps Voici un exemple d’un bone complet possédant les 3 transformations : Bone "Mesh15" { 0 11 Translation 12 { 6667 0 0 -1.23516 6900 0 0 7.11766 7167 0 0 -1.54375 7433 0 0 6.30507 7667 0 0 -1.23516 Page 51 SimKingdom Rapport de projet 8333 0 0 0 } Rotation 12 { 6667 0 0.0654033 0 -0.997859 6900 0.0388058 0.00169976 -0.0389302 0.998487 7167 0 -0.0610487 0 0.998135 7433 -0.0381404 0.0196451 0.0380599 0.998354 7667 0 0.0654033 0 -0.997859 8333 0 0 0 1 } Scaling 8 { 667 1 1 1 1167 0.875823 0.875823 0.875823 1667 1 1 1 8333 1 1 1 } } Et les types utilisés pour nos bones et pivotspoints // Transformations (unitaire) TTransform = Record Time : integer; x, y, z, w : real; end; // Tableau de TTransform (transformations multiples) TabTTransform = array of TTransform; // Utilisation geoset TGeosetUtil = array of integer; // Bones TBones = Record Parent : integer; MeshID : TGeosetUtil; Rotation, Translation, Scaling : TabTTransform; end; // Tableau de Bones TabTBones = array of TBones; Page 52 SimKingdom Rapport de projet // Clé TKey = Record x, y, z : real; end; // Tableau de clés TabTKey = array of TKey; (*-----------------*) (* Points de pivot *) (*-----------------*) // Point de pivot TPivotPoints = Record x, y, z : real; end; // Tableau de point de pivot TabTPivotPoints = array of TPivotPoints; 9.4 Attachement des bones aux vertex Chaque bone possède un index et il est stocké dans tableau de bone. Il sera donc ensuite identifiable par son index. Chaque geoset de la créature possède, en plus de tout ce qu’on a cité plus haut en rappel, 2 autres structures : – Les VertexGroup – Et les Groups On utilise ces 2 structures pour lier les vertex du geoset (qui sont stockés dans un tableau de vertex et identifiables par leur index) à un bone. Le schéma ci-dessous explique ce mode de liaison : 9.5 Principe d’animation Pendant le jeu, notre créature effectuera 2 animations : "Stand" (Repos) qui est l’animation par défaut et "Walk" (Marcher) lorsque qu’elle se déplace. Il faut donc tout d’abord, définir l’animation à jouer. Pour cela, nous Page 53 SimKingdom Rapport de projet F IG . 29 – la liaison bones-vertex Page 54 SimKingdom Rapport de projet avons créer 1 procédure : procedure setAnim(index : integer ; boucle : boolean). Le type Tab2Vertex_mdl est un tableau de vertex qui sera utilisé pour appliquer les transformations et tracer la créature. Voici les type des séquences d’animations : // Animations TAnims = Record Nom : string; Debut, Fin, Longueur : integer; end; // Tableau d'animations TabTAnims = array of TAnims; // Animation en cours TAnimCur = Record index : integer; boucle : boolean; time_start : real; end; setAnim permet de définir l’animation à jouer, si elle se joue en boucle, et récupère le temps de départ de l’animation et de réinitialiser le tableau de vertex à tracer. Toutes les animations sont stockées dans un tableaux et sont identifiables par leur index. procedure TCCreature.setAnim(index : integer; boucle : boolean); begin reinit_tabvertex; CurrAnim.index := index; CurrAnim.boucle := boucle; CurrAnim.time_start := glfwGetTime; end; Ensuite à chaque boucle de jeu on entre dans la procédure d’animation : Animation_mdl Page 55 SimKingdom Rapport de projet Tout d’abord, on commence par tracer les bones. Puis on vérifie si on est pas sortit de l’animation, c’est à dire si le temps n’a pas dépassé la durée de l’animation. // Si on est sortit de l'anim if time > (My_Creature.Animations[CurrAnim.index].Fin)/1000 then begin // Si anim jouée en boucle if CurrAnim.boucle then begin CurrAnim.time_start := glfwGetTime; time := 0.0; //reinit_tabvertex; end else // Sinon on lance l'animation de base de la créature (Stand) begin setAnim(0,true); Exit; end; end; Puis on parcours le tableau de bone est on effectue les transformations associées à chaque bone sur les vertex qui y sont attachés. On calcule la donnée de transformation qui correspond au temps en cours de l’animation puis on effectue les transformations par une interpolation entre la donnée trouvé et la donnée précédente pour éviter d’avoir une animation saccadé. Voici un exemple pour les translation : // Trouve la position de clé adapté au temps en cours index := 0; while (index < length(Transformation)-1) and (Transformation[index].time/1000 < (time))do inc(index); if( index > 0 ) then begin // Interpolation entre les 2 clés // Temps qui s'écoule entre les 2 clés Page 56 SimKingdom Rapport de projet deltaTime := (Transformation[index].Time - Transformation[index-1].Time)/150; // Position relative de l'interpolation de la position fraction := (time - Transformation[index-1].Time/150) / deltaTime; // Valeurs de la clé translation (x, y, z) Translation.x := Transformation[index-1].x + fraction * (Transformation[index].x - Transformation[index-1].x); Translation.y := Transformation[index-1].y + fraction * (Transformation[index].y - Transformation[index-1].y); Translation.z := Transformation[index-1].z + fraction * (Transformation[index].z - Transformation[index-1].z); end else begin // Valeurs de Translation.x Translation.y Translation.z end; la := := := clé translation (x, y, z) Transformation[index].x; Transformation[index].y; Transformation[index].z; Il faut ensuite affecter ces valeurs de transformations aux vertex associés aux bones. Ensuite, on effectue le même travail mais cette fois-ci avec les données de Rotation du bone. La seule chose qui chose c’est qu’on applique pas direction les données de transformation du qutaernion, on les transforme : // Récupère le quaternion d'avant et le transforme RotatCoord := QuatRotat(Transformation[index-1]); Pour cela, il faut appliquer une formule mathématique trouvée à partir de la matrice de transformation d’un quaternion, ce qui donne (un code horrible mais bon ...) : function TCCreature.QuatRotat(Quat : TTransform) : TKey; var Mat : TKey; Page 57 SimKingdom Rapport de projet begin Mat.x := 1 - 2*sqr(Quat.y)- 2*sqr(Quat.z) + 2*Quat.x*Quat.y + 2*Quat.w*Quat.z + 2*Quat.x*Quat.z - 2*Quat.w*Quat.z; Mat.y := 2*Quat.x*Quat.y - 2*Quat.w*Quat.z + 1 - 2*sqr(Quat.x) 2*sqr(Quat.z) + 2*Quat.y*Quat.z + 2*Quat.y*Quat.z + 2*Quat.w*Quat.x; Mat.z := 2*Quat.x*Quat.z + 2*Quat.w*Quat.y + 2*Quat.y*Quat.z 2*Quat.w*Quat.x + 1 - 2*sqr(Quat.x) - 2*sqr(Quat.y); result := Mat; end; Vive les maths et les quternions ! Une fois cette transformation effectuée, on applique les translation et les rotations sur nos vertex : // Rotation TabVertex[Geoset,Vertex[j]].x := My_Creature.Geoset[Geoset].Vertex[Vertex[j]].x * Rotation.x; TabVertex[Geoset,Vertex[j]].y := My_Creature.Geoset[Geoset].Vertex[Vertex[j]].y * Rotation.y; TabVertex[Geoset,Vertex[j]].z := My_Creature.Geoset[Geoset].Vertex[Vertex[j]].z * Rotation.z; // Translation TabVertex[Geoset,Vertex[j]].x := My_Creature.Geoset[Geoset].Vertex[Vertex[j]].x + Translation.x; TabVertex[Geoset,Vertex[j]].y := My_Creature.Geoset[Geoset].Vertex[Vertex[j]].y + Translation.y; TabVertex[Geoset,Vertex[j]].z := My_Creature.Geoset[Geoset].Vertex[Vertex[j]].z + Translation.z; 9.6 Procédure principal de la créature Tout ce que nous avons vu plus haut et lancer à partir d’un procédure, la procédure principale de la créature : procedure TCCreature.Principal(Picking : boolean; var DeplacC : boolean; mil, x, y, fps : real; ArrivC : TDeplac; var i :integer); var time : real; ok : boolean; Page 58 SimKingdom Rapport de projet begin // Déplace toutes les 1s time := frame/fps; if time >= 0.05 then begin ok := true; frame := 0; end else begin ok := false; inc(frame); end; // Gère les déplacements de la créature if DeplacC and ok then Deplacement(DeplacC,ArrivC,i); glPushMatrix; // Transformations 3D glTranslatef(TAILLE_QUADRILLAGE * (posx+0.5) - mil + x, TAILLE_QUADRILLAGE * (posy+0.5) - mil + y, 0); glScale(scale,scale,scale); glRotatef(angle,0,0,1); // Gestion des animations Animation_mdl; // Cercle de sélection if CSelectionnee then Tracer_Cercle(0,0,0.5,30,1,0,0); glColor3f( 1, 1, 1 ); // Traçage des faces avec texture Traceur_mdl(Picking); glPopMatrix; Page 59 SimKingdom Rapport de projet end; Si on est en mode déplacement, on lance la procédure Deplacement qui incrémente soit la position x, soit la position y, soit les 2 de la créature de 0,5 jusqu’à arriver à la case d’arrivée. On effectue cette action jusqu’à ce qu’on soit arrivé à la fin du tableau fournit par l’astar (voir plus bas) Ensuite, on la déplace physiquement avec OpenGL, on la tourne. . . On gère les animations comme vu plus haut, on trace un cercle autour si elle a été sélectionné puis on la trace ! Au final, on est passé de ça : F IG . 30 – la . . . créature hum ! A ça : Ou même : le mouton avec son cercle de sélection, le mouton et Krisboul ! La création, le parçage, enfin bref, toussa, a été réalisé par 80122. 9.7 L’a-star Dans une interface. . . Afin de permettre à notre créature de se déplacer sans encombre jusqu’à son point d’arrivée, il a été nécessaire de faire un a-star, c’est-à-dire Page 60 SimKingdom Rapport de projet F IG . 31 – ca poutre un max Page 61 SimKingdom Rapport de projet F IG . 32 – avec le cercle de sélection F IG . 33 – le cochon Page 62 SimKingdom Rapport de projet F IG . 34 – notre prof préféré :D Page 63 SimKingdom Rapport de projet un algorithme de calcul du plus court chemin. Nous avons d’abord implémenter cette a-star via une interface delphi, dans le but de pouvoir facilement visualisé son efficacité. Etant donné la manière dont est stocké le terrain, nous avons choisi d’utiliser la méthode dite du "case pas case", au détriment de la méthode dite "des surfaces". Avec cette méthode, nous avons choisi d’utiliser un tableau contenant par case un enregistrement contenant ceci : – un état ; – des coordonnées (abscisse, ordonnée) ; – des coordonnées "prédécesseur" ; – des coûts. L’état indique si la case est libre ou non, et si elle se trouve dans la liste ouverte ou fermée (ces deux listes seront expliquées par la suite), ou si elle forme une partie du chemin. Les coordonnées "prédécesseur" correspondent aux coordonnées de la case d’où l’on vient. Les coûts correspondent à une distance restante et une distance déjà effectuée, ils seront explicités par la suite. L’utilisation de deux listes permet de stocker le chemin définitif (liste ouverte) et de stocker les cases possibles pour le chemin définitif (liste fermée). Afin de calculer le plus court chemin, nous utilisons un astar qui calcul deux coûts : – le premier donne une estimation de la distance qu’il reste à parcourir, il est calculé en allant de la case en cours jusqu’à la case d’arrivée en ignorant les obstacles et en ne se déplaçant que verticalement ou horizontalement ; – le second donne la distance déjà effectuée, c’est-à-dire celle comprise entre la case départ et la case en cours. Maintenant que nous avons exposé ce que nous avons dû utiliser, expliquons comment fonctionne l’a-star sur une interface. Nous utiliserons pour cela un exemple simple comme suit : – Rouge : case de départ – Bleu : case d’arrivée – Noir : obstacles Pour calculer le meilleur chemin, nous procéderons en plusieurs étapes. La première consiste à mettre les cases adjacentes à la case en cours, et qui ne sont pas des obstacles, dans la liste ouverte de la manière suivante : Page 64 SimKingdom Rapport de projet F IG . 35 – fonctionnement de l’astar : au début procedure aelo(Point: p_elt); var i: Integer; PTemp: p_elt; place: boolean; begin i := 0; place := false; if listeouverte.Longueur > 0 then begin while ( i < listeouverte.Longueur ) and not place do begin PTemp := listeouverte.Enieme(i).Contenu; if Point.coutf < PTemp.coutf then begin listeouverte.InsererMaillon(Point, i); place := true; end; i := i + 1; end; if not place then listeouverte.AjouterMaillon(Point); end else listeouverte.AjouterMaillon(Point); end; Page 65 SimKingdom Rapport de projet Maintenant que nous avons les cases adjacentes, il nous faut trouver la case qui servira au chemin définitif. C’est ici qu’interviennent les coûts. Le premier (décris précédement) est implémenté comme suit : function ctcpc(Ptemp:p_elt) :integer; var test : boolean; i, j : integer; begin i := ptemp.abscisse; j := ptemp.ordonne; test := (j-1 >= 0) and (i-1 >= 0) and (j+1 <= dim) and (i+1 <= dim); if test and ((((matrice[i, (j+1)]^.etat) = 1) and ((matrice[(i+1), j]^.etat) = 1)) or (((matrice[i, (j-1)]^.etat) = 1) and ((matrice[(i-1), j]^.etat) = 1)) or (((matrice[i, (j+1)]^.etat) = 1) and ((matrice[(i-1), j]^.etat) = 1)) or (((matrice[i, (j-1)]^.etat) = 1) and ((matrice[(i+1), j]^.etat) = 1))) then result := 150 else begin if ((abs(ptemp.pere - ptemp.abscisse) + abs(ptemp.mere - ptemp.ordonne))) = 1 result := 10 else result := 14; end; end; La première partie sert à empecher de prendre une diagonale entre deux obstacles. Le second (décris précédement) est coder comme suit : couth := (10*(abs(abscisse - x_arr) + abs(ordonnee - y_arr))); Pour obtenir la case qui servira au chemin, nous comparons les différents coûts pour chaque case adjacente à celle en cours. Le plus petit coût est celui retenu, la case ayant ce dernier est placée dans la liste ouverte. – Gris : case se situant dans la liste fermée – Jaune : case présente dans la liste ouverte Afin d’obtenir le chemin final, nous réiterons le procédé jusqu’à arrivé à la case d’arrivé, une fois cette dernière atteinte, la liste ouverte contiendra toute les cases du chemin. Pour tracer le chemin sur l’interface, nous utilisons les coordonnées "prédécesseur", puisqu’elles connaissent toutes Page 66 SimKingdom Rapport de projet F IG . 36 – Le fonctionnement de l’astar en cours de route leurs prédécesseurs respectifs, il ne nous reste plus qu’à donner une couleur au chemin, et nous aurons tracer le chemin le plus court. F IG . 37 – Le chemin calculé par notre Astar Dans le jeu. . . Une fois l’a-star implémenté dans une interface, il a fallu l’implémenter dans le jeu. Pour cela, nous utilisons une classe TAstar dont le contenu est fortement identique à celui dans l’interface : TAstar = class private matrice: array of array of p_elt; x_arr, y_arr : integer; //arrivee pc_x,pc_y : integer; //parents en cours Page 67 SimKingdom Rapport de projet listeouverte, listefermee : TChaine; dim : integer; procedure init_matrice ; procedure close_matrice; function principale(a, b : p_elt):boolean; procedure init_liste; procedure close_liste; function edlo (P : p_elt):integer; function edlf(P:p_elt):integer; procedure aelo(Point: p_elt); function ctcpc(Ptemp:p_elt) :integer; procedure acavlo( i, j,dim :Integer ); procedure cpc(var Point: p_elt); procedure aff_chemin(x, y: Integer); function Recherche(x, y : integer; Move : TabTDeplac) : boolean; procedure reset_terrain; public procedure Generer(nb_cases : integer; TabMap : TTabOccup); // génère la mat function Calcul(dep_x, dep_y, arr_x, arr_y : integer; var Move : TabTDeplac end; Nous appelons la fonction calcul pour lancer l’a-star. Dans cette dernière, nous testons si il y a un chemin existant et si c’est le cas, nous parcours le tableau à la recherche du chemin de la manière suivante : if not(bool) then begin while (x <> arr_x) or (y <> arr_y) do begin a := -1; bool := true; while (a <= 1) and bool do begin b := -1; while (b <= 1) and bool do begin if (x + a >= 0) and (x + a < dim) and (y + b >= 0) and (y + b < dim) then begin if (matrice[x+a,y+b]^.etat = 2) and not(Recherche(x+a,y+b,Move)) then begin Page 68 SimKingdom Rapport de projet x := x + a; y := y + b; setlength(Move,i+1); Move[i].x := x; Move[i].y := y; inc(i); bool := false end; end; inc(b); end; inc(a); end; end; result := true; end else result := false; Finalement, on fait un parcours des cases adjacentes à la case en cours pour retrouver le bon chemin. Cette partie a été réalisé par 80120 avec l’aide de 80122 (implémentation et correction des bugs). 10 Editeur de cartes Principe Nous avons décidé de réaliser un éditeur de cartes dès le début (difficile quand on ne connait pas encore Delphi ...) pour générer facilement nos cartes qui seront ensuite affichées dans notre jeu par parsage d’un fichier *.map. L’éditeur permet de générer une carte sur laquelle sont placés les objets de base qui seront chargés par le moteur graphique au début du jeu, les différents objets sont : – Arbres – Montagnes – Eau – Ressources (or et pierre) Page 69 SimKingdom Rapport de projet F IG . 38 – L’éditeur de carte Fonctions de l’éditeur Notre éditeur est doté de plusieurs fonctions pratiques pour permettre de créer facilement et rapidement une carte jouable. – Génération aléatoire d’une carte avec de l’eau et/ou de la montagne et/ou des arbres – Choisir une texture et une taille – Placer des objets un par un dont les ressources (pierre et or) – Effacer un objet – Afficher une grille pour repérer les cases Génération de la carte Avant de pouvoir placer les objets sur la carte, il faut la générer. Pour cela, il faut choisir certains paramètres initiaux. Tout d’abord, il faut choisir la texture qui sera utilisée : – Gazon – Herbe – Herbe sèche – Roche Ensuite la taille de la carte (toutes les cartes sont carrées) : – Petite (302 cases) – Moyenne (502 cases) Page 70 SimKingdom Rapport de projet – Grande (1002 cases) Il est possible de placer aléatoirement des objets (arbres, eau, montagnes) grâce à la génération de la carte. Ainsi, la carte sera déjà remplie (utile si l’on veut beaucoup d’objets). Pour celà il suffit de faire varier les différentes TrackBars. Les positions initiales sont 0. Enfin, pour générer la carte il suffit d’appuyer sur le bouton "Générer". Une série d’information sur la carte s’afficheront sur la droite du programme : longueur et largeur en pixels (une case fait 20 pixels), texture utilisée, nombre de cases (largeur×longueur), occupation totale de la carte par les objets générés, occupation en fonction du type de l’objet (exprimées en %) : arbres, eau, montagnes, ressources. F IG . 39 – une carte généré par notre éditeur Placement des objets Pour placer des objets il suffit de cliquer sur son icône (un message dans la barre de statut vous indiquera l’objet choisi) puis de cliquer sur la carte à l’emplacement où vous souhaitez placer l’objet. Page 71 SimKingdom Rapport de projet Pour faciliter l’utilisation de l’éditeur, deux petites fonctionnalités pourront nous être utile : – La gomme : elle permet d’effacer un objet, elle fonctionne sur le même principe que le placement des objets. – La grille : affichage d’une grille jaune qui décompose la carte en cases. Pratique pour placer un objet précisément. Sauvegarder et charger un *.map Une des fonctionnalités les plus importantes de cet éditeur sont la possibiliter de charger une carte déjà faite auparavant grâce à l’éditeur. Le menu "Charger" de l’éditeur est prévu à cet effet. Il affichera la carte avec sa texture, sa taille, ses objets et affichera les informations relatives à la carte. Et enfin, il est bien sûr possible de sauvegarder notre carte gâce au menu "Sauvegarder" ou au bouton présent sur la fiche. L’éditeur enregistrera notre carte au format *.map ci-dessous : Texture [Texture de la carte] Cases [Nombre de cases en largeur (ou longueur)] [type de l'objet 1] [coordonnée x] [coordonnée y] [type de l'objet 2] [coordonnée x] [coordonnée y] .. . [type du dernier objet] [coordonnée x] [coordonnée y] Exemple : Texture Herbe Cases 30 eau 0 0 Page 72 SimKingdom Rapport de projet or 0 1 pierre 0 2 eau 0 8 arbre 0 12 montagne 0 23 Notre carte est ainsi prête à être affichée par le moteur graphique. Aide Un menu d’aide est disponible dans l’éditeur (F1) et vous permet d’obtenir des informations sur : – Comment générer une carte – Comment placer un objet – Comment sauvegarder/charger 11 11.0.1 Editeur de bâtiments Introduction Comme nous l’avons vu lors de nos précédentes soutenances, nos batiments et objets sont générés a partir de notre fonction draw_object qui se base sur notre format .obj que nous ne rapellerons plus. Bien que facilement lisible et éditable, il est assez compliqué de réaliser des formes géométriques complexes simplement à partir d’une feuille de papier milimetrée et d’un plan (~x, ~y, ~z). . . C’est donc dans cette optique que l’éditeur de batiments a été réalisé lors de la troisième soutenance. Celui-ci a pour particularité majeure d’être programmé en OpenGL (via notamment les composants du menu) et d’être entièrement programmé en objet. 11.0.2 De l’intérêt de la POO Comme on peut le voir sur l’image, l’interface de l’éditeur comprend de nombreuses occurences de mêmes objets : la notion de POO a donc été Page 73 SimKingdom Rapport de projet F IG . 40 – l’aide F IG . 41 – l’interface de l’éditeur de batiments Page 74 SimKingdom Rapport de projet pleinement exploité pour l’application. Ainsi, chacun des 4 écrans qui représentent la vue de face, de côté, de haut et la vue 3d sont tous issues de la même classe T_Ecran. Voici la déclaration de la classe T_Ecran, qui comprend un grand nombre de fonction, puisque l’essentiel des fonctionnalités de l’éditeur se joue sur ces écrans. T_Ecran = class protected start : boolean; r,g,b:real; num_polygone:integer; position_x,position_y,echelle,nbc,tc:integer; function Calcul_Milieu(Objet : T_Obj ; NbPoly : integer):T_Coord3; function New_Coord(x1,y1,x2,y2:integer):T_Coord; public pl:string; mode:string; dpx,dpy:real; procedure Init_Color(num_polygone,r,g,b:integer); function Afficher_Coordonnees(plan:string;h,xs,ys:integer):string; procedure Afficher_Ecran(pos_x,pos_y,tcase,nbcases,ech:integer); function Focus(h,xs,ys:integer):boolean; procedure Tracer_Point(px,py:integer;r,g,b:real); procedure Afficher_Obj(var obj:T_Obj;h,xs,ys,Nb,C:integer; Color:array of T_Color;cgauche:boolean); function GetSelection(var obj:T_Obj;h,xs,ys:integer):t_coord3; function Convertir_Coord(x,y,z:real):T_Coord; function Focus_Point(h,xs,ys,px,py:integer):boolean; procedure Surbrillance_Point(px,py:integer); function GetCoordonnees(h,xs,ys:integer):T_Coord2; procedure DeSel(var Obj:T_Obj); procedure Ajout_Afficher(NewObj:T_Obj;Nb_Poly,Nb_Pts:integer); function Ajout_NewPoint(var NewObj : T_Obj;h,xs,ys:integer;num_poly, num_point,type_gl:integer;Couleur:T_Color;Tex:integer):T_NP; procedure Ajout_LnTemp(NewObj:T_Obj;h,xs,ys,nb_poly,nb_pts:integer); procedure Modifier_Point(var Objet:T_Obj;num_poly,num_pts,h,xs,ys:integer); procedure Supprimer_Polygone(var Objet:T_Obj;num_poly,h,xs,ys:integer); end; L’aperçu 3D est un peu différent des autres écrans : c’est une classe enfant qui hérite des particularités de la classe T_Ecran et en ajoute d’autre. Voici sa déclaration : Page 75 SimKingdom Rapport de projet T_Ecran3d = class(T_Ecran) public procedure Ecran_Apercu(pos_x,pos_y,tcase,nbcases:integer); procedure Tracer_Objet_3D(Mode:string;Objet:T_Obj; Nb_Vertex:integer;xb,yb,zb:real;zoom:integer;C:integer;Color:array of T_Color); procedure Apercu_3D(mode:string;dx,dy:integer;axe_x,axe_y, zoom_vue:integer;w,h:integer;objet:T_obj; Nb_Vertex:integer;C:integer;Color:array of T_Color); end; 11.0.3 La fonctionnalité avant tout Afin de rendre la création des bâtiments facile, de nombreux raccourcis clavier ont été ajoutés (ctrl+s,entrer,tab,. . . ), ainsi qu’une barre de message : F IG . 42 – la barre de message De même, l’affichage 3D permet de voir le bâtiment sous toutes les coutures, de zoomer,. . . Il est aussi possible via la checkbox apercu texture/couleur de voir le bâtiment texturé et coloré tel qu’il apparaitra dans le jeu ou de la même couleur que les polygones des écrans. Enfin, un aperçu de la texture ou de la couleur spécifié par l’utilisateur est également possible : Nous avons réalisé depuis un nouveau mode d’affichage, en 3D fil de fer, dont voici un exemple : 12 Editeur de scénarios Une fois le mode scénario implémenté, nous avons décidé de réaliser un outil afin de nous faciliter la création des campagnes. Cet outil à été réaliser de manière à être très simple à utiliser, son interface est donc très simple tout comme l’aide qui l’accompagne. Cela permet ainsi au joueur qui le désire de créer un nouveau scénario ou même une campagne entière s’il le désire, en utilisant les cartes du jeu déjà existante ou des cartes qu’il aura préalablement crée via l’éditeur de carte. L’éditeur de campagne récupère les valeurs données par l’utilisateur et génére deux fichiers : un fichier *.snr et un fichier *.txt. Page 76 SimKingdom Rapport de projet F IG . 43 – aperçu en mode 3D fil de fer F IG . 44 – L’éditeur de scénario Page 77 SimKingdom Rapport de projet Le fichier *.snr contient les objectifs à atteindre ainsi que le nom de la carte à utiliser. C’est ce fichier qui est parser par le jeu pour être exploité par le système de scénario. Le fichier *.txt l’accompagnant comprend tout simplement l’histoire de notre scénario. Il est affiché lors du choix du scénario. F IG . 45 – Les fichiers générés L’éditeur de scénario a été réalisé par 80048 avec l’aide de 80120. 13 Conclusion Cette année de Sup est passé très rapidement (surtout vers la fin), et le projet y est pour quelque chose. Au premier abord sur-réaliste, la réalisation d’un jeu vidéo complet est vite devenue le point central de nos vie de gentils geeks, occupant nos rares moments libres, nos nuits, nos journées, nos cours d’élecs. . . bref. En effet quoi de plus passionnant pour nous mordus d’informatique ? Aujourd’hui, nous sommes extremement fiers du résultat que nous avons obtenus. Nous avons réunie en un seul plusieurs jeux sur lesquels nous avions passé maintes heures, c’est-à-dire SimCity (inspiration principal), Caesar III, Black and White,. . . De plus, le jeu est entièrement jouable, il est intéressant, on peut le faire évoluer (via nos éditeurs), sa durée de vie est illimité, et les graphismes sont époustouflants (tout est relatif hein). Qui plus est il est dénué de bugs (la classe). Enfin, nous avons réussi a faire le jeu dont nous rêvions : le jeu est fidèle à la vision que nous en avions au début de l’année, lors de la rédaction du cahier des charges, voire même mieux. Page 78 SimKingdom Rapport de projet Ce projet nous a aussi beaucoup appris et ce à tous les niveaux : les joies et les difficultés du travail en groupe, la POO (grande révélation), la 3D, la réalisation d’un projet complet et de tout ce qui va autour (rapports, soutenances). . . C’est donc heureux et exténués par ces longues nuits de codes que nous terminons ce rapport et cette année. 14 Tableau récapitulatif : qui a fait quoi Légende : – A : a fourni une aide – E : a effectué le travail Qui à coder quoi dans SimKingdom Page 79 SimKingdom Fichier AntUtils Astar Class_Creature Construct getip highscore mode_scenario moteur_graphique_draw_object moteur_graphique_map moteur_graphique_parseur op_chaine s_objets_u s_textures skybox son_fonctions type_graph type_mdl interface_composant_button interface_composant_button_arrow interface_composant_checkbox interface_composant_combobox interface_composant_compteur interface_composant_edit interface_composant_listbox interface_composant_trackbar charger_icones composant_fenetre composant_graphique composant_minimap composant_ressources composant_selactive composant_tab_icone composant_tableau composant_temps composant_vitesse conseillers Rapport de projet Nb Lignes 80018 132 477 902 445 55 E 86 E 65 304 2759 45 134 55 174 100 103 E 78 140 75 116 70 117 101 116 77 84 60 138 302 204 170 223 225 215 100 118 390 A 80048 E 80120 80122 E E A E A E A E A A A E A E E E E E E E E E E E E E E E E E E E A A E E E E E E E E A E A Page 80 A SimKingdom Fichier Nb Lignes open_fenetres 179 corebatiments 558 corebudgets 127 corecreature 50 corecs 142 coreministere 227 corepopulation 137 corescenario 168 coretech 189 SimKingdom.dpr 689 Rapport de projet 80018 80048 E E E A E 80120 A E E E E E 80122 A E A E A A E E Page 81