
Date non précise
Due à une avance prise qui est tout de même notable je vais résumer ce qui a été fait avant le Lundi 16 Décembre.
(Non important) Pour commencer, le projet ayant germé dans ma tête pendant les grandes vacances, j'avais déjà posée les règles du jeu et créer les cartes qui composeront les différents paquet de cartes des joueurs. Aucune ligne de code n'as été fait à ce stade car je ne savais pas encore codée.
(Non important) Environ moitié novembre j'avais réussi à faire un prototype du projet avec un personnage qui se déplacer sur quadrillage et qui pouvait attaquer d'autres unité. Ce prototype ne seras pas expliquer car le projet a été repris à zéro (pour une optimisation du code) une semaine avant son officialisation (Lundi 16 Décembre).
Quadrillage: Donc pour commencer, j'ai due créer un quadrillage sur lequel se déplaceraient les futurs unité. Pour cela j'ai créé une fonction quadrillage(). dans cette fonction je fais intervenir deux boucle qui me permettrons de créer une case.
void Quadrillage(){
noFill();
stroke(255);
strokeWeight(1);
for(int ycolonne=0; ycolonne<15; ycolonne++){
for (int xligne=0; xligne<20; xligne++){
rect(xligne*taille,ycolonne*taille,taille,taille);}}}
noFill() pour avoir des cases vide, stroke(255) pour s'assurer que les lignes soit blanche, strokeWeight(1) pour avoir des lignes fines (normalement inutile mais utile par la suite). La première boucle sert pour la position y de la case, la seconde boucle sert pour la position x de la case. Si je vulgarise ça donne pour tels ligne y < 15 lignes, faire 20 rectangle, la variable taille est à retenir car elle définie la taille des cases et donc des unités mais aussi de l'interface de jeu.
Mais en vue d'une mécanique de jeu et pour simplification du code j'ai décidé de créer une instance d'objet appelé case. Mais il faut donc définir les différentes case dans un tableau de 300 case (dans la situation actuelle 15*20). Pour cela j'ai crée une autre fonction quadrillage qui ne se situe pas dans draw() contrairement à la première mais dans setup(). La fonction est la même à la différence qu'au lieu de tracer un rectangle, j'initialise une case du tableau d'objet Case, le tableau s'appelle Emplacement.
void quadrillage_D(){
noFill();
stroke(255);
strokeWeight(1);
for(int ycolonne=0; ycolonne<15; ycolonne++){
for (int xligne=0; xligne<20; xligne++){
Emplacement[NmbEmplacement]= new Case(xligne*taille,ycolonne*taille);
NmbEmplacement++; } } }
La variable NmbEmplacement de sert qu'as savoir qu'elle case est initialisé dans le tableau.
Unité: Maintenant que le terrain de jeu est prêt occupons-nous des unités. Pour les unités étant donné qu'elles sont amenée à se multiplier, j'ai créer une instance d'objet pour les unités aussi.
class Unite{
int xperso;
int yperso;
int taille;
int Vie;
int VieMax;
int Cout1;
int Cout2;
int vit=5;
String portee;
int att;
int def;
int mouvement;
PImage icon;
Nous avons ici les caractéristique principale de nos unité (attention d'autre sont déjà présent mais nous en parlerons plus tard). Les premières variable, les x et yperso, correspondent à l'emplacement de l'unité. Après vient la variable taille (pas besoin d'explication j'imagine). Les deux suivante sont les variable Vie et VieMax là aussi pas besoin d'explication. Ensuite viennent les variables Cout1 et Cout2, ce sont les variables qui permettent de savoir combien le joueur a besoin de ressource pour invoquer l'unite (variable qui vont être transposée plus dans l'instance d'objet Carte). Ensuite nous avons quatre variable, att (attaque) deff (deffense) vit(vitesse) et mouvement, vitesse correspond au nombre maximum de déplacement pour l'unité et mouvement au nombre de déplacement restant. Ensuite viennent deux objet un de texte portee et une image icon, portee sert à, comme son nom l'indique, savoir la distance d'attaque mais ce n'est pas un nombre pour ainsi pouvoir certaine distinction entre 1 (attaque à 1 case) 2 (attaque à 2 case) et 1-2 (attaque à 1 et 2 case). L'objet icon sert à afficher l'unité.
Bon maintenant comme dit dans les règles du jeu j'ai en tout 6 tableau en relation avec les unité. Un tableau pour l'instance d'objet Unité et un tableau pour savoir lesquels existent et ceux pour le joueur 1 on duplique et hop un joueur 2. Et enfin on applique la même logique mais pour le total cette fois-ci. Cela permettra de simplifier le codage (en tout cas de le rendre plus lisible).
Maintenant affichons notre unité. Pour cela initialisons deux unités dans la fonction setup() (il serviront de test). Après nous ajoutons une fonctions affichage(). Fonction toute simple où l'on utilise la fonction image() au coordonnée x et y de notre unité +1 et la taille -1. Le +1 et le -1 servent à faire en sorte que l'image soit bien dans le cadre de la case, sinon il dépasse au niveau du coin supérieur gauche.
Enfin on va pouvoir passer au mouvement. Mais un problème se pose, c'est un jeu en tactical rpg, on doit donc pouvoir sélectionner n'importe qu'elle unité que l'on veut en premier lieu.
void Selection(){
if(Tour_action>0){
if(action==0){
if(mouvement<=0){
if(mouseX>=xperso && mouseX<=xperso+taille){
if(mouseY>=yperso && mouseY<= yperso+taille){
if(mousePressed==true){
mouvement=vit;
action=1;
Phase_Perso=1;
Tour_action=Tour_action-1;
xOriPerso=xperso;
yOriPerso=yperso;
La première correspond à une variable rajouté dans l'instance objet, elle sert à savoir si l'unité à déjà joué dans ce tour, je la laisse en variable en non en booléen au cas ou si une capacité que je pourrais rajouter augmenterais le nombre de coup par tour. La deuxième condition utilise la variable action qui elle est définie dans tout le programme, elle sert à faire en sorte que lorsque vous sélectionner une unité, aucune autre ne peut être sélectionné en même temps. La troisième condition est juste une assurance. La quatrième et cinquième condition consiste à savoir si le curseur se situe bien sur la case de l'unité. Enfin la dernière condition sert à savoir si le joueur sélectionne bien se pion. Donc la conséquence de tout ça: le nombre de mouvement de l'unité passe au nombre maximal de mouvement. Action augmente d'un cran afin de bien dire au programme "une unité est sélectionné donc on ne peut en sélctionner d'autre". Phase_Perso sert à définir les différentes phase: 1=mouvement et 2=attaque. Tour_action baisse d'un cran. Et enfin x et yOriPerso enregistre la position de départ de l'unite ainsi dans le futur je pourrais faire en sorte que le joueur puisse annulée son mouvement (mais pas son attaque).
Mouvement: Bien maintenant que notre unité est sélectionné deux choix s'offre à nous pour déplacer l'unité. Le 1er serait de choisir où va l'unité avec un clic de la souris, possible mais trop compliquer si l'on veut rajouter des obstacle sur le trajet. Par contre le deuxième lui est plus simple, on déplace l'unité case par case mais avec le clavier cette fois si. Nous utiliserons donc la deuxième solution.
Notre fonction commenceras par la condition: si la variable mouvement>0 alors ... afin de limiter le nombre de mouvement. Ensuite une seconde condition: si une touche du clavier est pressée alors... et ensuite nous passons au déplacement, ci-dessus un exemple avec le déplacement en direction du bas.
//Bas/////////////////////////////////Bas//
if(keyCode==DOWN){
yperso=yperso+taille;
mouvement=mouvement-1;
for(int C=0; C<Emplacement.length; C++){
if(xperso==Emplacement[C].xposition && yperso==Emplacement[C].yposition){
if(Emplacement[C].Presence!="Vide"){
yperso=Emplacement[C].yposition-taille;
mouvement++; } } }
if(yperso>=taille*15){
yperso=taille*14;
mouvement=mouvement+1;
}
}
Donc dans l'ordre, si la variable keyCode (touche spéciale du clavier) est égale à flèche du bas, alors augmenter la variable de position y du personnage d'une case puis baisser d'un cran la variable mouvement. Alors nous avons après deux possibilité afin de codé la collision avec un élément externe, je présente pour l'instant la première puis après avoir vue le déplacement je reviendrais dessus rapidement.
Donc, pour la collision, nous déclenchons une boucle qui se répéteras pour chaque case du terrain. On vérifie d'abord que la case et le personnage ont les même coordonnées, puis on vérifie que l'objet texte Presence de la case est contraire à "Vide", si c'est le cas cela veut dire que la case est déjà occupé, on annule donc le mouvement. Et enfin, la dernière condition sert à savoir si le personnage est hors du terrain. Si c'est le cas on annule là aussi le déplacement.
Après on répète ce processus pour les trois autres directions, et à la toute fin, dans la condition "Si une touche est pressée" on rajoute la ligne : keyCode=0; De la sorte, on évite que le personnage se déplace plus que nécessaire.
Nous avons donc fini de parler du déplacement et nous pouvons donc revenir sur notre histoire de collision. Afin d'optimiser et d'alléger notre programme (on va pas ce le cachait une boucle de 300 cases 36x par seconde sa fait mal au programme), on rajoute juste une variable s'appelant NumCase (le numéro de la case), une variable prenant en mémoire le numéro de la case dans le tableau Emplacement (celui des case du terrain). Ainsi, en reprenant notre exemple du déplacement vers le bas, on obtient :
//Bas/////////////////////////////////Bas//
if(keyCode==DOWN){
if(Emplacement[NumCase+20].Presence=="Vide"){
yperso=yperso+taille;
mouvement=mouvement-1;
if(yperso>=taille*15){
yperso=taille*14;
mouvement=mouvement+1; } } }
J'ai donc supprimé la boucle qui alourdissait le programme en le replaçant par une simple condition. La condition vérifie simplement si la case en dessous du personnage est vide si c'est le cas on exécute le déplacement sinon rien n'est fait. Pour savoir quelle case il faut chercher, on met simplement à la case égale à la variable NumCase dans le tableau Emplacement, variable auquel on soustrait ou ajoute le nombre correspondant à la direction ( le nombre dépend de comment on à créer les cases du terrain. Dans ce programme, +1 correspond à une case sur la droite, -1 une case sur la gauche, +20 une case en bas, -20 une case en haut. De ce fait on peut chercher n'importe quelle case du tableau à partir de la variable NumCase).
Attaque : Maintenant que notre unité se déplace, il est temps de passer à l'attaque. Pour optimiser le code nous rajoutons la fonction Attaque() dans un autre onglet que celui de l'instance d'objet Unite. Mais avant cela il nous faut trois fonction importante. La première est une fonction que j'appelle ChangePhase() qui permet de changer l'action de l'unité (mouvement à attaque puis fin d'action). Fonction placer dans l'instance d'objet Unite. On commence par deux conditions afin de savoir si l'unité est en action ou passive, si elle est active une troisième condition s'ensuit afin de savoir si le clavier est pressé et on cherche de quelle touche il s'agit et on actionne alors la phase voulue par la touche.
void ChangePhase(){
if(action>0){
if(Phase_Perso==1){
if(keyPressed==true && key=='1'){
Phase_Perso=2;
key=0;
keyPressed=false;
}
}
if(Phase_Perso>=1){
if(keyPressed==true && key==ENTER){
action=0;
Phase_Perso=0;
mouvement=0;
key=0;
keyPressed=false; } } } }
On
a donc notre première condition servant à savoir si une unité est en
action puis une deuxième avec Phase_Perso servant à savoir l'état de
l'unité et si c'est bien elle qui est en action. Si il y a deux
condition Phase_Perso c'est pour gérer dans un premier temps le
changement de phase en attaque et le deuxième pour valider les mouvement
de l'unité et mettre fin à son tour.
Dans la première condition (celle pour l'attaque), on vérifie donc que la touche qui est pressée est le chiffre 1 puis on change la phase de l'unité (phase 2= attaque) puis on réinitialise les variables du clavier afin que ça ne nous cause pas de bug dans le reste du programme.
Dans la seconde condition (celle pour la fin d'action), on vérifie que la touche pressée est Entrée puis on remet toute les variables relatif à l'activité de l'unité à 0. Ainsi l'unité ne peut plus agir.
Nous avons donc une fonction pour dire à l'unité de passer en mode attaque mais il nous manque encore une fonction essentiel à l'optimisation de notre code dans sa globalité dès qu'il s'agit des unités. Il s'agit d'une fonction qui actualise les unités des tableaux personnel des joueurs dans le tableau englobant toute les unités du jeu.
void Unit_Actu(){
for(int U=0; U<Tunite_JI.length; U++){
if(TuniteVivant_JI[U]==true){
Tunite_Total[U]= Tunite_JI[U];
TuniteVivant_Total[U]= true;
}
}
for(int U=0; U<Tunite_JII.length; U++){
if(TuniteVivant_JII[U]==true){
Tunite_Total[U+limite_d_unit]= Tunite_JII[U];
TuniteVivant_Total[U+limite_d_unit]= true; } } }
Nous avons ici deux boucles, une pour chaque tableaux d'unité de Joueur. La logique est la même pour chaque boucle, pour chaque unité actif/vivante du joueur on l'actualise à la même position dans le tableau totale l'unité, donc chaque unité est en fait présente en double exemplaire. Ceci permet l'optimisation des futures lignes de codes. A noter qu'il est nécessaire, lors de l'actualisation des unité du second joueur de rajouter à la valeur de la position dans le tableau, la valeur égale à la longueur d'un tableau d'objet Unite.
Et enfin, notre troisième fonction avant d'attaquer est la même que la fonction précédente à la différence qu'elle consiste à faire l'inverse (c'est à dire actualiser les tableaux d'unité personnel des Joueurs à partir du tableau Totale).
Et étant donné que j'ai fini le codage de l'attaque des unité après l'officialisation du projet je mets fin à cet onglet et parlerais du codage actuelle de l'attaque à la date du 20 Décembre 2019.