images bannieres

Mathgames

Analyse d'algorithmes de votre jeu vidéo Prototype

Programmation

Plan de la page programmation

  1. Première étape: analyse simplifié du programme Prototype.
  2. Deuxième étape: analyse explicite du programme Prototype.
  3. Fichier qui contiendra les variables globales ainsi que ses 16 méthodes.
  4. Fichier qui contiendra la 1re classe Aircaft.
  5. Fichier qui contiendra la 2e classe Bullet.
  6. Fichier qui contiendra la 3e classe Formes.
  7. Fichier qui contiendra la 4e classe Polaire_Enemy.
  8. Fichier qui contiendra la 5e classe Scrolling.

Si vous êtes arrivé jusqu'ici en suivant le fil, c'est que vous avez suivi dans l'ordre les différents cours sur la trigonométrie, sur les vecteurs et sur cette partie infime de l'analyse, ce n'était pas si dur que cela finalement et vous aurez appris quelque combine avec savoir pour la suite de votre jeu vidéo, c'est-à-dire savoir calculer des différents angles et distances, savoir situer dans un plan et savoir calculer en dérivant une fonction f pour avoir des mouvements réalistes de nos séries d'avions qui visent vers notre avion mère en utilisant notre boîte à outils des mathématiques. Nous pouvons donc poursuivre et développer votre programme en partant d'une base théorique, mais ce n'est pas encore fini, il vous faudra de la patiente pour assimiler ce cours sur la programmation avec Processing.

Tout d'abord, avant de continuer et de vous commenter avec le plus de détail possible les codes sources de votre jeu vidéo que l'on va programmer ensemble, je vais vous rediriger vers le site de FLOSS MANUALS qui détaille bien mieux que moi l'environnement de Processing avec précision. N'hésitez pas à visiter les liens du site Web dans l'ordre si vous êtes novice en programmation ou si vous vous s'enter familier avec un langage de programmation quelconque que vous avez déjà appris ou survolé par le passé. Les langages de programmation se ressemblent tous, c'est juste la syntaxe qui change d'un langage à l'autre.

Quand vous vous sentirez à l'aise après avoir passé un peu de temps sur le site de FLOSS MANUALS, je vous détaillerai avec le plus de précision possible les différents algorithmes de votre jeu vidéo.

Suivez ce lien pour ce cours de programmation avec FLOSS MANUALS.


Si vous avez suivi le tutoriel de Processing de FLOOS MANUALS, vous avez peut-être téléchargé Processing et vous avez tapé quelques exemples de code sources pour voir ce que cela donne. Alors maintenant, je me dis que vous êtes opérationnel pour ouvrir, sauvegarder et taper du code dans Processing. Nous allons y aller tranquillement pour programmer votre jeu vidéo que vous appellerez comme vous voudrez quand vous enregistrerez votre premier projet dans Processing, pour l'instant, moi, je l'ai nommé Prototype.


Prototype : votre jeu vidéo

Note : notre objectif sera de programmer un jeu vidéo simple où il y aura un avion mère qui vise sur plusieurs séries d'avions avec tir de munitions et animation de collision, cela ressemblera à la vidéo ci-dessous une fois finie, vous pourrez télécharger une archive du programme « ici ».

Cette version est pour les systèmes Windows, mais en haut de cette page Web sur la barre des rubriques, vous avez un lien de téléchargement, vous pourrez télécharger une version Linux, pour la version Mac OS X, la version Linux devrait être compatible, car ces deux derniers systèmes son programmé sur un même noyau système commun qui se nomme Unix.


Mais tout d'abord je vous laisse en premier lieu télécharger toutes les images (sprites) du jeu vidéo qui sont dans un dossier nommé data*. « Lisez bien maintenant », une fois que vous aurez téléchargé le dossier data, placer le dans le dossier du projet que vous aurez créé à l'instant même avec Processing. Je vous rappelle pour créer un projet dans Processing, ouvrer Processing sur votre bureau et faite « Enregistrer sous... » en lui donnant un nom de projet, moi par exemple, j'ai nommé le projet Prototype. C'est très important sinon vous aurez des messages d'erreur dans votre IDE quand vous exécuterez votre programme. Les messages d'erreurs s'afficheront dans la fenêtre de console de sortie en bas de Processing et il faudra les corriger.

Data* : data est un terme utilisé en français comme synonyme du mot données, en particulier dans le domaine informatique.

IDE : en programmation informatique, un environnement de développement est un ensemble d'outils qui permet d'augmenter la productivité des programmeurs qui développent des logiciels. Il comporte un éditeur de texte destiné à la programmation, des fonctions qui permettent, par pression sur un bouton, de démarrer le compilateur ou l' éditeur de liens ainsi qu'un débogueur en ligne, qui permet d'exécuter ligne par ligne le programme en cours de construction. Certains environnements sont dédiés à un langage de programmation en particulier.

Article Wikipédia : environnement de développement.

Note : je vous rappelle qu'un IDE veut dire environnement de développement intégré, alors maintenant, je dirais soit IDE ou soit Processing qui est la même chose. Télécharger une version de Windows de Processing ici et le dossier data ici contenant toutes les images de votre jeu vidéo.

Pour votre jeu vidéo, le dossier data et sous forme compresser, il vous faudra WinRAR, WinZip ou 7-Zips sur votre machine avec Windows comme système pour le décompresser ensuite, une fois décompresser, ouvrer le dossier à la racine et prenez le dossier qui est à l'intérieur, c'est celui-là que vous devrez mettre dans le dossier du projet que vous viendrez d'enregistrer dans Processing. C'est dû à la compression qu'il y a deux dossiers du même nom, c'est le deuxième dossier en dessous qu'il faut prendre, les images et actifs du jeu sont justes en dessous de ce dossier à la racine.


Première étape : analyse simplifiée du programme Prototype

Tout d'abord, nous allons analyser le programme complet en simplifier de votre jeu vidéo qui contient nos variables globales* de différents types au début du programme, ensuite on a nos 16 méthodes* qui suivent et pour la fin du programme, on a nos 5 classes* orientées objets.

Les commentaires commençant par les barres obliques ne comptent pas dans les codes sources, je vous énumère d'abord les déclarations des variables globales telles qu'elles sont dans le programme du jeu Prototype ci-dessous, ensuite viendront les 16 méthodes suivies des 5 classes pour finir le programme.

Variable globale* : en programmation informatique, une variable globale est une variable déclarée à l'extérieur du corps de toute méthode, fonction ou classe, et pouvant donc être utilisée n'importe ou dans le programme. On parle également de variable de portée globale.

Voici la liste de nos variables globales de 10 types de données différents du programme Prototype :
PImage, PImage[ ], PFont , boolean, int, int[ ], float, objet de classe, ArrayList et PVector.


/*************************************************
      
    Autheur de ce programme : himalaya2004    
    Programmation : Prototype                  
    Date le 09/11/2017                                  
    Programmation Java dans IDE Processing                                   
                                                                                
*************************************************/


//  On définit toutes nos variables globales avant la méthode setup()
PImage decor_1;               //  Stocke notre image décor_1 (fonds mer)
PImage decor_2;               //  Stocke notre image décor_2 (fonds mer)
PImage img_avion_mere;        //  Stocke notre image img_avion_mere
PImage img_avion_vies;        //  Stocke notre image img_avion_vies
PImage img_tir       ;        //  Stocke notre image img_tir
PImage img_avion_enemy;       //  Stocke notre image avion_enemy;
PImage img_balle;             //  Stocke image balle des enemy
PImage img_dead;              //  Stocke image dead (fin de partie)
PFont fontA;                  //  Stocke notre fonte (police de caractère)
boolean scrolling = true;     //  Boolean prend soit vrai (true) soit faux (false) 
boolean pause = true;         //  Boolean à true pause ou reprise du jeu 
int vies = 5;                 //  Stocke les vies du joueur
int score;                    //  Stocke le score du jeu
int mem_time = 0;             //  Stocke variable mem_time pour tempo du tir
float tempo_tir = 0;          //  Stocke tempo_tir pour munitions
int ecart;                    //  Différence espace entre avions ennemis et avion mère
int diff;                     //  Difference espace entre munitions ennemis et avion mère 
int speed_Aircraft, direction_D_G; //  Vitesse et direction de l'Aircraft (avion mère)

// Variables objets de type classe
Scrolling Scrolling;          //  Objet de classe Scrolling 
Aircraft Aircraft;            //  Objet de classe Aircraft
Bullet Bullet;                //  Objet de classe Bullet

//  Liste tableaux ArrayList dynamique 
//  enemy (Nombre d'avions ennemis par trajectoires) 
//  bullets (Tir de munitions de l'avion mère)
//  pole (Tir de munitions des avions ennemis)
ArrayList <Formes> enemy;       // Où nos avions ennemis seront stockés par trajectoire
ArrayList <Bullet> bullets;     // Où nos munitions seront stockées
ArrayList <Polaire_Enemy> pole; // Où nos munitions des avions ennemis seront stockées
Formes fx;                      // Variable objet de classe Formes (fx)

//  Animation feu avion mère et avions ennemis
int numFrames_enemy              = 7;  // Nombre d'images pour l'animation
int numFrames_aircraft           = 7;  // Nombre d'images pour l'animation
PImage[] images_feu_enemy        = new PImage[numFrames_enemy];
PImage[] images_feu_aircraft     = new PImage[numFrames_aircraft];
int nb_frames_anim_enemy         = 0;
int nb_frames_anim_aircraft      = 0;
int i_anim_enemy                 = 0;
int i_anim_aircraft              = 0; 
int tempo_anim_enemy             = 50; 
int tempo_anim_aircraft          = 50;
boolean anim_feu_enemy           = false;
boolean anim_feu_aircraft        = false;
float x_anim_enemy, y_anim_enemy, x_anim_aircraft, y_anim_aircraft;
int timing_launch_polaire, last;

//  Pour le game over (fin de partie)
boolean game_over;

//  Fréquence vague d'enemy avec tempo millis 
int frequence_espace_entre_avion = 0;
int compteur_avion = 0;
int vague_figure;
float nb_avions;  //  Nombre d'avions ennemis par trajectoire
boolean fin_vague = true;

//  Tableaux vague_formes
int[] vague_forme = {0, 2, 1};  // Ordre rangé des trajectoires dans tableau
int no_vague;

//  5 booleans qui prennent soit (true) ou (false) vrai ou faut soit 2 états
boolean ri, le, up, dw, sp;  //  Pour touche clavière appuyée
//  Si toucher, l'avion mère disparaît avant de réapparaitre
boolean toucher;  

//  PVector v1 pour coordonnées cartésiennes v1.x, v1.y de l'avion mère
PVector v1; 

Méthode* : une méthode est un bloc qui contient une série d'instructions que l'on souhaite réutiliser. L'intérêt des méthodes réside dans la possibilité de réutiliser du code: nous aimerions écrire une seule fois une action tout en pouvant la répéter autant de fois que nécessaire. En englobant notre code dans une méthode, il devient possible d'appeler celle-ci à différents moments de notre programme. En fait une méthode ou une fonction signifie la même chose et joue le même rôle mais en emploi plus généralement le mot méthode lorsqu'elle est encapsulée dans une classe et fonction lorsqu'elle est à l'extérieur d'une classe. Ce qu'il faut savoir aussi, c'est que les méthodes ou fonctions peuvent admettre ou pas des arguments et peuvent rendre ou pas des valeurs de retour. Là, je vous ai mis les 16 méthodes avec leurs corps vides, c'est-à-dire qu'il n'y a pas de code à l'intérieur de leurs corps qui commence après la déclaration de chaque méthode de la première accolade et fini à la dernière accolade, j'ai mis 5 points à la place pour simplifier la lecture de cette première étape.

Voici la liste de nos 16 méthodes du programme Prototype :

 
  
//  Méthode setup() appelée une seule fois au lancement du programme
void setup() {
.....  
}

//  Méthode draw() appelée sans arrêt en boucle au lancement du programme
void draw() {
.....  
}

//  Méthode keyPressed()
//  Évènement si on presse sur une touche du clavier
void keyPressed() {
.....
}

//  Méthode keyReleased()
//  Évènement si on relâche une touche du clavier 
void keyReleased() {
..... 
}

//  Méthode mousePressed()
//  Évènement si on clique sur la souris
//  Lorsque que l'on clique sur la souris on recommence une partie
void mousePressed() {
  
.....  
}

//  Méthode Action_Aircraft() de l'avion mère  
void action_Aircraft() {
..... 
}

//  Méthode vies() 
void vies() {
.....  
}

//  Méthode score()  
void score() {
.....  
}

//  Méthode titre()  
void titre() {
.....  
} 

//  Méthode bordure()
void bordure() {
..... 
}

//  Méthode game_Over()
void game_Over() {
.....  
}

//  Méthode frequence_Vague() 
void frequence_Vague() { 
.....  
}

//  Méthode tab_Formes()  
void tab_Formes() {
.....  
}

  //  Méthode animation_Tir_Munitions()
void animation_Tir_Munitions() {
.....  
}

//  Méthode animation_Feu_Aircraft(float x, float y)
void animation_Feu_Aircraft(float x, float y) {
.....  
}

//  Méthode animation_Feu_Enemy(float x, float y) 
void animation_Feu_Enemy(float x, float y) {
.....  
}

Nous avons vu nos 16 méthodes simplifiées avec leurs corps vides qui se trouvent entre la première accolade et finissant à la dernière accolade après la déclaration de chaque méthode, j'ai mis 5 points à la place pour simplifier la lecture pour cette première étape.

Classe* : en programmation orientée objet, une classe déclare des propriétés communes à un ensemble d'objets. La classe déclare des attributs représentant l'État des objets et des méthodes représentant leur comportement. Une classe représente donc une catégorie d'objets. Elle apparaît aussi comme un moule ou une usine à partir de laquelle il est possible de créer des objets. (c'est en quelque sorte une « boîte à outils » qui permet de fabriquer un objet). On parle alors d'un objet en tant qu'instance d'une classe (création d'un objet ayant les propriétés de la classe).

Voici la liste de nos 5 classes du programme Prototype :


//********************************************************************
//**                
//**  Classe Aircraft, déplacement et collision Aircraft (avion mère)   
//**                
//********************************************************************

class Aircraft {
..... 
} 

//********************************************************************
//**                
//**  Classe Bullet, animation tir munitions de l'Aircraft (avion mère)  
//**                
//********************************************************************

class Bullet { 
.....  
}

//**************************************************************
//**                
//**  Classe Formes, déplacement trajectoire des avions ennemis 
//**                
//**************************************************************

class Formes {
.....  
}

//*********************************************************************************
//**                
//**  Classe Polaire_enemy, tir des munitions des avions ennemis vers l'avion mère  
//**                
//*********************************************************************************

class Polaire_Enemy {
.....  
}

//********************************************
//**                
//**  Classe Scrolling, défilement décor Océan
//**                
//********************************************

class Scrolling {
.....  
}

Nous avons vu nos 5 classes simplifiées avec leurs corps vides qui se trouvent entre la première accolade et finissant à la dernière accolade après la déclaration de chaque classe. j'ai mis 5 points à la place pour simplifier la lecture pour cette première étape. Toute classe possède des variables membres pour les déclarations de variables de tous types différents, à un constructeur ou plusieurs constructeurs portant le même nom que la classe elle-même mais avec des valeurs de retours différents et à des méthodes de classe au minimum en général, avec ses ingrédients nous pouvons alors construire des objets de type classe et une fois que notre classe est prête, nous pouvons instancié* notre classe pour lui faire exécuter son rôle qu'elle doit jouer dans le programme.

Instancié* : en programmation orientée objet, on appelle instance d'une classe, un objet avec un comportement et un état, tous deux définis par la classe. Il s'agit donc d'un objet constituant un exemplaire de la classe. Dans ce contexte, instance est un anglicisme, qui signifie « cas », « exemple ». L'instanciation est l'action d'instancier, de créer un objet à partir d'un modèle. Elle est réalisée par la composition de deux opérations : l'allocation et l'initialisation. L'allocation consiste à réserver un espace mémoire au nouvel objet. L'initialisation consiste à fixer l'état du nouvel objet. Cette opération fait par exemple appel à l'un des constructeurs de la classe de l'objet à créer. La modification de ces opérations permet de réaliser la réflexion structurelle.

Note : en programmation orientée classe, l'instanciation est la création d'un objet à partir d'une classe.


Deuxième étape : analyse explicite du programme Prototype

Nous allons voir dans cette deuxième étape l'analyse du code complet du programme Prototype en plus précis. Tout d'abord voici le code source du programme complet en un seul fichier ci-dessous, analyser le bien du début jusqu'à la fin, cela commence par les déclarations de nos variables globales au début de notre programme, ensuite viennent nos 16 méthodes et pour la fin, on termine le programme avec nos 5 classes qui suivent jusqu'à la fin du programme.

Note : le programme complet de Prototype en un seul fichier, se lance sans erreur dans Processing, je l'ai testé aujourd'hui le 19/05/2020.

Voici le code complet en un seul fichier :


/*************************************************
      
    Autheur de ce programme : himalaya2004 
    Programmation : Prototype                     
    Date le 09/11/2017                                  
    Programmation Java dans IDE Processing                                   
                                                                                
 *************************************************/


//  On définit toutes nos variables globales avant la méthode setup()
PImage decor_1;               //  Stocke notre image décor_1 (fonds mer)
PImage decor_2;               //  Stocke notre image décor_2 (fonds mer)
PImage img_avion_mere;        //  Stocke notre image img_avion_mere
PImage img_avion_vies;        //  Stocke notre image img_avion_vies
PImage img_tir       ;        //  Stocke notre image img_tir
PImage img_avion_enemy;       //  Stocke notre image avion_enemy;
PImage img_balle;             //  Stocke image balle des enemy
PImage img_dead;              //  Stocke image dead (fin de partie)
PFont fontA;                  //  Stocke notre fonte (police de caractère)
boolean scrolling = true;     //  Boolean prend soit vrai (true) soit faux (false) 
boolean pause = true;         //  Boolean à true pause ou reprise du jeu 
int vies = 5;                 //  Stocke les vies du joueur
int score;                    //  Stocke le score du jeu
int mem_time = 0;             //  Stocke variable mem_time pour tempo du tir
float tempo_tir = 0;          //  Stocke tempo_tir pour munitions
int ecart;                    //  Différence espace entre avions ennemis et avion mère
int diff;                     //  Difference espace entre munitions ennemis et avion mère 
int speed_Aircraft, direction_D_G; //  Vitesse et direction de l'Aircraft (avion mère)

// Variables objets de type classe
Scrolling Scrolling;          //  Objet Scrolling 
Aircraft Aircraft;            //  Objet Aircraft
Bullet Bullet;                //  Objet Bullet

//  Liste tableaux ArrayList dynamique 
//  enemy (Nombre d'avions ennemis par trajectoires) 
//  bullets (Tir de munitions de l'avion mère)
//  pole (Tir de munitions des avions ennemis)
ArrayList <Formes> enemy;       // Où nos avions ennemis seront stockés par trajectoire
ArrayList <Bullet> bullets;     // Où nos munitions seront stockées
ArrayList <Polaire_Enemy> pole; // Où nos munitions des avions ennemis seront stockées
Formes fx;            // Variable objet de classe Formes (fx)

//  Animation feu avion mère et avions ennemis
int numFrames_enemy              = 7;  // Nombre d'images pour l'animation
int numFrames_aircraft           = 7;  // Nombre d'images pour l'animation
PImage[] images_feu_enemy        = new PImage[numFrames_enemy];
PImage[] images_feu_aircraft     = new PImage[numFrames_aircraft];
int nb_frames_anim_enemy         = 0;
int nb_frames_anim_aircraft      = 0;
int i_anim_enemy                 = 0;
int i_anim_aircraft              = 0; 
int tempo_anim_enemy             = 50; 
int tempo_anim_aircraft          = 50;
boolean anim_feu_enemy           = false;
boolean anim_feu_aircraft        = false;
float x_anim_enemy, y_anim_enemy, x_anim_aircraft, y_anim_aircraft;
int timing_launch_polaire, last;

//  Pour le game over (fin de partie)
boolean game_over;

//  Fréquence vague d'enemy avec tempo millis 
int frequence_espace_entre_avion = 0;
int compteur_avion = 0;
int vague_figure;
float nb_avions;  //  Nombre d'avions ennemis par trajectoire
boolean fin_vague = true;

//  Tableaux formes
int[] vague_forme = {0, 2, 1};  // Ordre rangé des trajectoires dans tableau
int no_vague;

//  5 booleans qui prennent soit (true) ou (false) vrai ou faut soit 2 états
boolean ri, le, up, dw, sp;  //  Pour touche claviere appuyée
//  Si toucher, l'avion mère disparaît avant de réapparaitre
boolean toucher; 

//  PVector v1 pour coordonnées cartésiennes v1.x, v1.y de l'Aircraft
PVector v1;  

//  Méthode setup() appelée une seule fois au lancement du programme
void setup() {
  //  Taille de la fenêtre 600 par 800 pixels
  size(600, 800);

  v1 = new PVector(0,0);  //  PVector coordonnées cartésiennes x, y de l'Aircraft

  //  On instancie toutes les classes
  Scrolling            = new Scrolling();                                             
  Bullet               = new Bullet(0, 0);                       
  Aircraft             = new Aircraft(width/2, height/2 + 300);  

  //  On charge toutes les images
  img_avion_mere       = loadImage("avion_mere.png"); 
  img_avion_vies       = loadImage("avion_vies.png");  
  fontA                = loadFont("EthnocentricRg-Regular-48.vlw"); 
  img_tir              = loadImage("tir.png");
  img_balle            = loadImage("balle_fire.png");
  img_avion_enemy      = loadImage("enemy.png");
  img_dead             = loadImage("dead.png");

  //  Avions ennemis des trajectoires dans tableau dynamique
  enemy                = new ArrayList();
  //  Tir des munitions de l'avion mère dans tableau dynamique
  bullets              = new ArrayList();
  //  Tir des munitions des avions ennemis dans tableau dynamique
  pole                 = new ArrayList();

  ecart                = img_avion_enemy.width/2 + img_tir.width/2;  //  Écartement
  diff                 = 25;                                         //  Différence
  toucher              = false;

  images_feu_enemy[0]             = loadImage("feu_1.png");
  images_feu_enemy[1]             = loadImage("feu_2.png"); 
  images_feu_enemy[2]             = loadImage("feu_3.png");
  images_feu_enemy[3]             = loadImage("feu_4.png"); 
  images_feu_enemy[4]             = loadImage("feu_5.png");
  images_feu_enemy[5]             = loadImage("feu_6.png"); 
  images_feu_enemy[6]             = loadImage("feu_7.png"); 
  images_feu_aircraft[0]          = loadImage("fire_1.png");
  images_feu_aircraft[1]          = loadImage("fire_2.png"); 
  images_feu_aircraft[2]          = loadImage("fire_3.png");
  images_feu_aircraft[3]          = loadImage("fire_4.png"); 
  images_feu_aircraft[4]          = loadImage("fire_5.png");
  images_feu_aircraft[5]          = loadImage("fire_6.png"); 
  images_feu_aircraft[6]          = loadImage("fire_7.png");

  game_over          = false;  //  Boolean pour fin de partie
  ri                 = false;  //  Boolean pour right
  le                 = false;  //  Boolean pour left
  up                 = false;  //  Boolean pour haut
  dw                 = false;  //  Boolean pour bas
  sp                 = false;  //  Boolean pour tir de munitions
  direction_D_G      = 0;      //  Direction droite ou gauche (avion mère)
  speed_Aircraft     = 2;      //  Vitesse avion mère

  // Tirage aléatoire 5 à 7 avions ennemis par trajectoire
  nb_avions = int(random(5, 8)); 
}

//  Méthode draw() appelée sans arrêt en boucle au lancement du programme
void draw() {
  Scrolling.scrolling();   //  Lance la méthode scrolling() de la classe Scrolling
  Scrolling.display();     //  Lance la méthode display() de la classe Scrolling
  
  if (!game_over) {
    Aircraft.display();    //  Lance la méthode display() de la classe Aircraft
    Aircraft.collision();  //  Lance la méthode collision de la classe Aircraft
    action_Aircraft();     //  Lance la méthode action_Aircraft()
  }

  bordure();               //  Lance la méthode bordure()
  vies();                  //  Lance la méthode vies()
  score();                 //  Lance la méthode score()
  titre();                 //  Lance la méthode titre()
  
  animation_Tir_Munitions();  //  Lance la méthode animation_Tir_Munitions()

  if (tempo_tir + 2000 <= millis()) {
    for (int j = 0; j < enemy.size (); j++) {
      Polaire_Enemy pl = new Polaire_Enemy();
      pole.add(pl);
    }
    tempo_tir = millis();
  } 

  //  Polaire_enemy 
  for (int k = pole.size () - 1; k >= 0; k--) {
    Polaire_Enemy fx = (Polaire_Enemy) pole.get(k);
    fx.move_bullets();
    fx.display_bullets();
    fx.test_vide_tab_Polaire_enemy();
    fx.collision();
  }

  //  Frequence vague successif avec frameCount 
  frequence_Vague(); //  Lance la méthode fréquence_vagues()

  //  Méthode tab_Formes 
  tab_Formes();      //  Lance la méthode tab_Formes()

  //  Méthode Animation_Feu_Enemy 
  if (anim_feu_enemy) {
    animation_Feu_Enemy(x_anim_enemy, y_anim_enemy);
  }

  if (!game_over) {
    //  Méthode Animation_Feu_Aircraft 
    if (anim_feu_aircraft) {
      animation_Feu_Aircraft(x_anim_aircraft, y_anim_aircraft);
    }

    if (millis() - last > timing_launch_polaire) {
      last = millis();
      toucher = false;
    }
  }

  game_Over();  //  Lance la méthode game_Over()
}

//  Méthode keyPressed()
//  Évènemment si on presse sur une touche du clavier
void keyPressed() {
  //  Si on presse la touche d ou D
  //  on enclenche le tir de munitions de l'avion mère
  if (key == 'd' || key == 'D') {
    sp = true;
  }
  //  Si on presse la touche p ou P
  //  on fait une pause dans le jeu
  if ((key == 'p') || (key == 'P')) {
    pause =! pause;
    noLoop();
    if (pause == false) loop();
  }
  if (key == CODED) {
    //  On appuie sur la flèche du haut
    if (keyCode == UP) { 
      up = true;
    } else 
      //  On appuie sur la flèche du bas
    if (keyCode == DOWN) { 
      dw = true;
    } else 
      //  On appuie sur la flèche de gauche
    if (keyCode == LEFT) { 
      le = true;
    } else 
      //  On appuie sur la flèche de droite
    if (keyCode == RIGHT) { 
      ri = true;
    }
  }
}

//  Méthode keyReleased()
//  Évènemment si on relâche une touche du clavier 
void keyReleased() {
  if (key == 'd' || key == 'D') {
    sp = false;
  }
  if (key == CODED) {
    if (keyCode == UP) 
    {
      up = false;
    } else if (keyCode == DOWN)
    {
      dw = false;
    } else if (keyCode == LEFT)
    {
      le = false;
      direction_D_G = 0;
    } else if (keyCode == RIGHT)
    { 
      ri = false;
      direction_D_G = 0;
    }
  }
}

//  Évènement si on clique sur la souris
//  Lorsque l'on clique sur la souris, on recommence une partie
void mousePressed() {
  if (mouseX > 90 && mouseX < 523 && mouseY > 425 && mouseY < 455) {
    game_over = false;
    vies = 5;
    score = 0;
    // Tirage aléatoire 5 à 7 avions ennemis par trajectoire
    nb_avions = int(random(5, 8)); 
  }
}

//  Méthode Action_Aircraft() de l'avion mere  
void action_Aircraft() {
  //  La touche flèche droite est appuyée alors
  //  lance la méthode move_Aircraft_D_G(2) 
  //  avec 1 argument passé de la classe Aircraft
  if (ri == true)
  {   //  Direction droite pour facteur (2)-----
    Aircraft.move_Aircraft_D_G(2);
  }

  //  La touche flèche gauche est appuyée alors
  //  lance la méthode move_Aircraft_D_G(-2) 
  //  avec 1 argument passé de la classe Aircraft
  if (le == true)
  {  //  Direction gauche pour facteur (-2)-----
    Aircraft.move_Aircraft_D_G(-2);
  }

  //  La touche flèche haut est appuyée alors
  //  lance la méthode move_Aircraft_D_G(-2) 
  //  avec 1 argument passé de la classe Aircraft
  if (up == true)
  {   //  Direction haut pour facteur (-2)-----
    Aircraft.move_Aircraft_H_B(-2);
  }

  //  La touche flèche bas est appuyée alors
  //  lance la méthode move_Aircraft_D_G(2) 
  //  avec 1 argument passé de la classe Aircraft
  if (dw == true)
  {  //  Direction bas pour facteur (2)-----
    Aircraft.move_Aircraft_H_B(2);
  }
  //  La touche d ou D pour le tir de munitions 
  //  de l'avion mère est appuyée 
  if (sp == true)
  {  //  On teste la temporisation du tir de munitions de l'avion mère à répétition
    if (millis() - mem_time >= 125) { 
      //  On instancie le tableau dynamique Bullet avec la variable nb_balles
      Bullet nb_balles = new Bullet(v1.x + 1, v1.y);
      //  On remplit les munitions (nb_balles) à l'intérieur du tableau dynamique bullets 
      bullets.add(nb_balles); 
      //  On remet la variable temporisation (mem_time) à jour
      mem_time = millis();
    }
  }
}

//  Méthode vies() 
void vies() {
  fill(255);                    //  Couleur RVB pour le texte
  stroke(45, 203, 102);         //  Couleur RVB sur bordure des dessins
  image(img_avion_vies, 10, 9); //  Affiche l'image img_avion_vies aux cordonnées 10, 9
  textFont(fontA, 25);          //  Donne une taille à notre fonte (police de caractère)  
  text(int(vies), 57, 34);      //  Affiche la variable vies aux coordonnées 57, 34
}

//  Méthode score()  
void score() {
  textFont(fontA, 20);          //  Taille de la fonte
  text("Score", 485, 23);       //  Affiche le texte score
  text(int(score), 484, 47);    //  Affiche la variable score
}

//  Méthode titre()  
void titre() {
  fill(255);                    //  Couleur RVB
  textFont(fontA, 24);          //  Taille de la fonte 
  text("Prototype", 190, 32);   //  Affiche le titre
} 

//  Méthode bordure()
void bordure() {
  rectMode(CENTER);                    //  Centre les coordonnées au milieu de la bordure 
  fill(255, 255, 255, 50);             //  Couleur RVB plus canal alpha 
  rect(300, 25, width, height - 750);  //  Dessine la bordure en haut de la fenêtre 
  rectMode(CORNER);                    //  Remets le mode rectMode par défaut
}

//  Méthode game_Over()
void game_Over() {
  if (game_over) {
    fill(255);
    textFont(fontA, 42); 
    text("GAME OVER", width/2 - 170, height/2);
    textFont(fontA, 28); 
    text("Rejouer une partie", width/2 - 205, height/2 + 50);
    image(img_dead, 43, 2); //  Affiche l'icone dead (fin de partie)
  }
}

//  Méthode frequence_Vague() 
void frequence_Vague() { 
  if (fin_vague == true) {  
    if (millis() - frequence_espace_entre_avion >= 700) {
      vague_forme[no_vague] = vague_forme[no_vague];
      vague_figure = vague_forme[no_vague];
      Formes fx = new Formes();
      enemy.add(fx);
      frequence_espace_entre_avion = millis();
      compteur_avion++;
    } 
    if (compteur_avion >= nb_avions) {
      compteur_avion   = 0;
      fin_vague        = false;
    }
  }
}

//  Méthode tab_Formes()  
void tab_Formes() {
  for (int i = enemy.size () - 1; i >= 0; i--) {
    fx = (Formes) enemy.get(i);
    fx.move_fx(i);
    fx.display_fx();
  }
 }

  //  Méthode animation_Tir_Munitions()
void animation_Tir_Munitions() {
  //  On parcourt le tableau dynamique <Bullet> bullets
  for (int i = bullets.size () - 1; i >= 0; i--) { 
    //  on obtient les munitions du tableau bullets : bullets.get(i)
    Bullet o = (Bullet) bullets.get(i);  
    o.move();      //  On lance la méthode move() de Bullet
    o.display();   //  On lance la méthode display de Bullet

    // Teste la coordonnée cartésienne y des munitions de l'avion mère 
    // si (o.y < 71) en haut de l'ecran
    if (o.y < 71) {  
      bullets.remove(i);  // Vide le tableau bullets pour ne pas avoir des fuites mémoire
    } else
    {
      for (int j=0; j < enemy.size (); j++) {
        if (round(o.x) - round(enemy.get(j).v2.x + enemy.get(j).coord_x) <=  ecart  && 
          round(o.x) - round(enemy.get(j).v2.x + enemy.get(j).coord_x) >= -ecart  && 
          round(o.y) - round(enemy.get(j).v2.y + enemy.get(j).coord_y) <=  ecart  && 
          round(o.y) - round(enemy.get(j).v2.y + enemy.get(j).coord_y) >= -ecart ) {

          x_anim_enemy              = round(enemy.get(j).v2.x + enemy.get(j).coord_x);
          y_anim_enemy              = round(enemy.get(j).v2.y + enemy.get(j).coord_y);
          i_anim_enemy              = 0;
          nb_frames_anim_enemy      = millis();
          anim_feu_enemy            = true;

          if (!game_over) {
            score  += 25 + random(0, 25);
          }

          if (score >= 99999) score = 99999;
          enemy.remove(j);
          bullets.remove(i);

          if (enemy.size() == 0) {
            fin_vague = true;
            no_vague ++; //nb_avions++; 
            if (no_vague == vague_forme.length) {
              // fin du niveau 
              no_vague = 0;
            }
          }
        }
      }
    }
  }
}

//  Méthode animation_Feu_Aircraft(float x, float y)
void animation_Feu_Aircraft(float x, float y) {
  if (millis() <= (nb_frames_anim_aircraft + tempo_anim_aircraft)) { 
    imageMode(CENTER);
    image(images_feu_aircraft[i_anim_aircraft], x, y);
    imageMode(CORNER);
  }

  if (millis() == nb_frames_anim_aircraft + tempo_anim_aircraft) {
    nb_frames_anim_aircraft += tempo_anim_aircraft;
    i_anim_aircraft++;
    if (i_anim_aircraft == 7) {
      i_anim_aircraft = 0;
      anim_feu_aircraft = false;
    }
  }
}

//  Méthode animation_Feu_Enemy(float x, float y) 
void animation_Feu_Enemy(float x, float y) {
  if (millis() <= (nb_frames_anim_enemy + tempo_anim_enemy)) { 
    imageMode(CENTER);
    image(images_feu_enemy[i_anim_enemy], x, y);
    imageMode(CORNER);
  }

  if (millis() == nb_frames_anim_enemy + tempo_anim_enemy) {
    nb_frames_anim_enemy += tempo_anim_enemy;
    i_anim_enemy++;
    if (i_anim_enemy == 7) {
      i_anim_enemy = 0;
      anim_feu_enemy = false;
    }
  }
}

//********************************************************************
//**                
//**  Classe Aircraft, déplacement et collision Aircraft (avion mère)   
//**                
//********************************************************************

class Aircraft {
  // Variables membres de l'objet Aircraft 
  // vide

  //---------------------------------------------------------
  //-- Constructeur Aircraft(int x_Aircraft, int y_Aircraft) 
  //-- qui passe 2 arguments 
  //-- entre parenthèses x_Aircraft et y_Aircraft de type int
  //---------------------------------------------------------

  Aircraft(int x_Aircraft, int y_Aircraft) {  
    v1.x = x_Aircraft;
    v1.y = y_Aircraft;
  } 

  //  Méthode display (affichage de l'avion mère)
  void display() {
   //  Centre les coordonnées cartésiennes du repère au milieu de l'image
   imageMode(CENTER);
   image(img_avion_mere, v1.x, v1.y);
   //  Remets le mode imageMode par défaut
   imageMode(CORNER);
  }

  //  Méthode move_Aircraft_D_G(int facteur_direction)
  //  avec facteur_direction passé en argument
  //  l'avion mère va à droite ou à gauche
  void move_Aircraft_D_G(int facteur_direction) {
    direction_D_G = facteur_direction;
    v1.x = v1.x + facteur_direction * speed_Aircraft;
    //  Teste de sortie d'écran à droite
    if (v1.x < 30) v1.x = 30; 
    //  Test de sortie d'écran à gauche  
    else if (v1.x > width - 30) v1.x = width - 30;
  }
  //  Méthode move_Aircraft_H_B(int facteur_direction)
  //  avec facteur_direction passé en argument
  //  l'avion mère va en haut ou en bas
  void move_Aircraft_H_B(int facteur_direction) {
    v1.y = v1.y + facteur_direction * speed_Aircraft;
    //  Test de sortie d'écran en haut
    if (v1.y < 71) v1.y = 71; 
    //  Test de sortie d'écran en bas
    else if (v1.y > height - 22) v1.y = height - 22;
  }

  void collision() {
    for (int j=0; j < enemy.size (); j++) {        
    if (round(v1.x) - round(enemy.get(j).v2.x+enemy.get(j).coord_x) <=  ecart  && 
      round(v1.x) - round(enemy.get(j).v2.x+enemy.get(j).coord_x) >= -ecart  && 
      round(v1.y) - round(enemy.get(j).v2.y+enemy.get(j).coord_y) <=  ecart  && 
      round(v1.y) - round(enemy.get(j).v2.y+enemy.get(j).coord_y) >= -ecart) {

      toucher = true;
      // L'avion mère réapparaît au bout de 500 millisecondes après avoir été toucher.
      timing_launch_polaire = 500; 
      // Recentre l'avion mère aux coordonnées cartésiennes largeur/2 et hauteur/2 + 300.
      v1.x = width/2;
      v1.y = height/2 + 300;

      x_anim_aircraft              = round(enemy.get(j).v2.x + enemy.get(j).coord_x);
      y_anim_aircraft              = round(enemy.get(j).v2.y + enemy.get(j).coord_y);
      i_anim_aircraft              = 0;
      nb_frames_anim_aircraft      = millis();
      anim_feu_aircraft            = true;

      vies = vies - 1;

      if (vies < 0) {
        vies = 0;
        game_over = true;
      }
     }
    }
   }
  } 

//********************************************************************
//**                
//**  Classe Bullet, animation tir munitions de l'Aircraft (avion mère)  
//**                
//********************************************************************

class Bullet { 
  //  Variables membres
  float x;         //  Coordonnées cartésiennes x des munitions de l'avion mère
  float y;         //  Coordonnées cartésiennes y des munitions de l'avion mère
  float nb;        //  Espacement entre les munitions de l'avion mère 
  float speed;     //  Vitesse du tir

  //----------------------------------------------
  //-- Constructeur Bullet(float tx, float ty)
  //-- qui passe 2 arguments 
  //-- entre parenthèses tx et ty
  //----------------------------------------------

  Bullet(float tx, float ty)
  {
    x = tx;        //  Coordonnées cartésiennes x des munitions de l'avion mère 
    y = ty;        //  Coordonnées cartésiennes y des munitions de l'avion mère
    nb = 10;       //  Espacement entre les munitions de l'avion mère égale à nb
  }

  //  Méthode display()
  void display() {  
     //  Centre les coordonnées cartésiennes du repère au milieu de l'image
     imageMode(CENTER);      
     //  Affiche l'image des munitions aux coordonnées cartésiennes x, y 
     image(img_tir, x, y);   
     imageMode(CORNER);      //  Remets le mode imageMode par défaut
  }

  //  Méthode move()
  void move() { 
    y -= nb;  //  Déplacement des munitions sur les y - nb
  }
}

//**************************************************************
//**                
//**  Classe Formes, déplacement trajectoire des avions ennemis 
//**                
//**************************************************************

class Formes {
  // PVector v2 pour coordonnées cartésiennes v2.x et v2.y des avions ennemis;
  PVector v2 = new PVector(0,0); 
  float offset, coef, pile_face, fact_pos_neg, ecart;       
  float coef_dir= -0.01;                  
  float facteur, coord_x, coord_y;  
  float x_tireur, y_tireur, x_cible, y_cible;   
  float x_bullet, y_bullet; 
  float x_copy, y_copy;
  float vite = 1.7;

  //---------------------------
  //-- Constructeur Forme() 
  //-- sans passage d'argument
  //---------------------------

  Formes() {

    switch(vague_figure) {

      //-- Forme croisée 
    case 0: 
      {
        offset  = int(random(-400, 400));
        coef = 1.7; 
        pile_face = int(random(1, 11));
        if (pile_face <= 5) {
          v2.x = 0  - 50;
          fact_pos_neg = 1;
          ecart = 0;
        } else {
          v2.x = width + 50 ; 
          ecart = width;
          fact_pos_neg = -1;
        }
        coord_x = 0;
        coord_y = 0;
      }
      break;

      //-- Forme square top 1 left -> right 
    case 1: 
      {
        v2.x = -width/2; 
        facteur = 1; //  int(random(1, 6));
        offset  = 0; //  int(random(0, 351));
        coord_x = width/2;
        coord_y = height/2;
      }
      break;

      //-- Forme square aleatoire -> left 
	case 2: 
      {
        v2.x = -width/2; 
        coef += random(-0.008, 0.005);
        coord_x = width/2;
        coord_y = height/2;
      }
      break;

    default : 
      {
        v2.x = 300 + int(random(-250, 250));
        coord_x = 0;
        coord_y = 0;
      }
      break;
    }
  }

  //------------------------
  //-- Méthodes move_fx ---
  //------------------------

  void move_fx(int i) {
    switch(vague_figure) {

      //-- Forme croisée 
	case 0: 
      {
        v2.y = (fact_pos_neg * (coef * int(v2.x - ecart))) - offset;
        v2.x += fact_pos_neg * vite;
      }
      if  (v2.y > height) {
        enemy.remove(i);
      }
      break;

      //-- Forme square top 1 left -> right 
	case 1: 
      {
        v2.y = round(coef_dir * sq(v2.x) / facteur + offset); 
        v2.x += vite * sqrt(facteur);
      }
      if  (v2.x > width/2) {
        enemy.remove(i);
      }
      break;

	//--Forme square aleatoire ->left 
	case 2: 
      {
        v2.y =- coef * sq(v2.x);
        v2.x += vite + 1.5 ;
      }
      if (v2.x > width/2) {
        enemy.remove(i);
      }
      break;

    default : 
      {
        v2.x += 0;
        v2.y += 3.5; 
        if  (v2.y > height) {
          enemy.remove(i);
        }
      }
      break;
    } 
    if (enemy.size() == 0) {
      fin_vague = true;
      no_vague ++;

      if (no_vague == vague_forme.length) {
        // fin du niveau 
        no_vague = 0;
      }
    }
  }

  //--------------------------
  //--  Méthodes display_fx 
  //--------------------------

  void display_fx() {
    imageMode(CORNER); 
    pushMatrix();
    translate(v2.x + coord_x, v2.y + coord_y); 

    switch(vague_figure) {

      //-- Forme croisée  
	case 0: 
      {
        rotate((-PI/2 + atan(v2.x * ( coef_dir * 2 / facteur))) * 0);
      }
      break;

      //-- Forme square top 1 left -> right 
	case 1: 
      {
        rotate(-PI/2 + atan(v2.x * (coef_dir * 2 / facteur)));
      }
      break;

      //-- Forme square aleatoire -> left 
	case 2: 
      {
        rotate(-PI/2 + atan(-v2.x * 2 * coef));
      }
      break;
   }
    
   image(img_avion_enemy, 0, 0);
   popMatrix();
   imageMode(CORNER);
   }

   float get_x_tireur(int i) {
   float x_tireur = enemy.get(i).v2.x;
   return x_tireur;
   } 

   float get_y_tireur(int i) {
   float y_tireur = enemy.get(i).v2.y;
   return y_tireur;
   } 

   float get_x_cible() {
   float x_cible = v1.x;
   return x_cible;
   } 

   float get_y_cible() {
   float y_cible = v1.y;
   return y_cible;
   } 

   float get_coord_x() {
   float COORD_X = coord_x;
   return COORD_X;
   } 

   float get_coord_y() {
   float COORD_Y = coord_y;
   return COORD_Y;
   }
} 

//*********************************************************************************
//**                
//**  Classe Polaire_Enemy, tir des munitions des avions ennemis vers l'avion mère  
//**                
//*********************************************************************************

class Polaire_Enemy {
  float x_tireur, y_tireur, x_cible, y_cible, theta, r = 0, vitesse;   
  float x_bullet, y_bullet, x_bullet_before, y_bullet_before, coord_x, coord_y; 
  
  //---------------------------------
  //-- Constructeur Polaire_enemy() 
  //-- sans passage d'argument
  //---------------------------------

  Polaire_Enemy() {
    int avion = int(random (0, enemy.size()));
    fx               = (Formes) enemy.get(avion);
    x_tireur         = fx.get_x_tireur(avion); 
    y_tireur         = fx.get_y_tireur(avion);   
    coord_x          = fx.get_coord_x();   
    coord_y          = fx.get_coord_y();  
    x_cible          = fx.get_x_cible() - coord_x;
    y_cible          = fx.get_y_cible() - coord_y; 
  }

  void move_bullets() {
    if (r==0) {
      if (x_cible >= x_tireur) {
        theta = atan((y_cible-y_tireur) / (x_cible-x_tireur));  // Calcul angle de tir
        theta += random(-0.5, 0.5);
        vitesse = random(2, 5);
      } else 
      {
        theta = atan((y_cible-y_tireur) / (x_cible-x_tireur));  // Calcul angle de tir
        theta += (random(-0.5, 0.5));
        vitesse  = -(random(2, 5));
      }
    }
    
    //  Coordonnées polaires des munitions des avions ennemis
    r += vitesse;
    x_bullet = cos(theta) * r + x_tireur + coord_x;
    y_bullet = sin(theta) * r + y_tireur + coord_y;
  }
  
  //  Affichage des munitions des avions ennemis
  void display_bullets() {
    imageMode(CENTER);
    image(img_balle, x_bullet, y_bullet); 
    imageMode(CORNER);
  }

  //  Test de sortie d'écran des munitions des avions ennemis
  void test_vide_tab_Polaire_enemy() {
    for (int i = 0; i < pole.size (); i++) {
    if (pole.get(i).x_bullet < 0 || pole.get(i).x_bullet > width || pole.get(i).y_bullet < 0 || pole.get(i).y_bullet > height) {
        pole.remove(i);
      }
    }
  }

  //  Test de collision de l'avion mère et des munitions des avions ennemis
  void collision() {
    for (int i = 0; i < pole.size(); i++) {
      if (round(v1.x) - round( pole.get(i).x_bullet) <=  diff  && 
        round(v1.x) - round(pole.get(i).x_bullet) >= -diff  && 
        round(v1.y) - round(pole.get(i).y_bullet) <=  diff  && 
        round(v1.y) - round(pole.get(i).y_bullet) >= -diff) {

        toucher = true;
        // L'avion mère réapparaît au bout de 500 millisecondes après avoir été toucher.
        timing_launch_polaire = 500;
        // Recentre l'avion mère aux coordonnées largeur/2 et hauteur/2 + 300.
        v1.x = width/2;
        v1.y = height/2 + 300;

        x_anim_aircraft              = round(pole.get(i).x_bullet);
        y_anim_aircraft              = round(pole.get(i).y_bullet);
        i_anim_aircraft              = 0;
        nb_frames_anim_aircraft      = millis();
        anim_feu_aircraft            = true;

        vies = vies - 1;

        if (vies < 0) {
          vies = 0;
          game_over = true;
        }
        pole.remove(i);
      }
    }
  }
}

//********************************************
//**                
//**  Classe Scrolling, défilement décor Océan
//**                
//********************************************

class Scrolling {
  //  On définit les variables membres des 2 écrans 
  //  qui défilent simultanément (x, y1, y2)
  int x, y1, y2;

  //---------------------------
  //-- Constructeur Scrolling() 
  //-- sans passage d'argument
  //---------------------------  

  Scrolling() {
    y1 = 0;      //  Hauteur écran 1
    y2 = -850;   //  Hauteur écran 2
  } 

  //  Méthode init scrolling()  
  void init_scrolling() {
    //  Condition si scrolling égale à true alors on exécute le code 
    //  en dessous entre accolades
    if (scrolling) {  
      //  Assigne image (fonds_mer) à décor_1
      decor_1 = loadImage("fonds_mer_1.png");
      //  Assigne image tirée aléatoirement (fonds_mer), 1 sur 4 à décor_2 
      decor_2 = loadImage("fonds_mer_" + int(random( 1, 5)) + ".png");
    }
  }

  //  Méthode scrolling 
  void scrolling() {
    //  Condition si scrolling égale true alors on exécute le code
    //  en dessous entre accolades
    if (scrolling) {
      //  Lance la méthode init_scrolling( ) de la classe Scrolling
      Scrolling.init_scrolling();  
    }
    scrolling = false;             //  On passe la variable boolean scrolling à false (faux)
    Scrolling.boucle();            //  Lance méthode boucle() de la classe Scrolling
  }

  //  Méthode display()
  void display() { 
    image(decor_1, x, y1);      //  Affiche décor_1 aux coordonnées x, y1
    image(decor_2, x, y2);      //  Affiche décor_2 aux coordonnées x, y2
  } 

  //  Méthode boucle()  
  void boucle() {
    y1++;                         //  Variable y1++ le décor_1 défile
    if (y1 > height + 50) {      //  On teste si y1 arrive en bas de l'écran + 50
      y1 = -850;                  //  On remet y1 à -850 pour refaire un nouveau défilement
      //  On charge aléatoirement 1 sur 4 images pour l'assigner au décor_1
      decor_1 = loadImage("fonds_mer_" + int(random(1, 5)) + ".png");
    }

    y2++;                        //  Variable y2++ le décor_2 défile
    if (y2 > height + 50) {    //  On teste si y2 arrive en bas de l'écran + 50
      y2 = -850;                 //  On remet y2 à -850 pour refaire un nouveau défilement
      //  On charge aléatoirement 1 sur 4 images pour l'assigner au décor_2  
      decor_2 = loadImage("fonds_mer_" + int(random(1, 5)) + ".png");
    }
  }
}

Vous avez plusieurs choix pour recopier les codes sources de ce programme complet ci-dessus dans votre projet, soit vous le recopiez en une seule fois ou soit vous le recopiez en plusieurs fichiers dans votre projet, cela fonctionnera de la même manière, mais en le recopiant en plusieurs fichiers se sera meilleur pour une question de lisibilité des codes sources du programme dans sa globalité. Si vous choisissez le deuxième choix, je vais vous expliquer comment faire avec les captures d'écran ci-dessous.

Note : je vous rappelle que pour enregistrer un projet, ouvrez Processing, il s'ouvrira avec un nom de projet par défaut, ensuite enregistrez le projet ouvert avec un nouveau nom. Cette fois-ci le projet enregistré portera le nom que vous viendrez de lui donner.

Quand vous aurez enregistré votre nouveau projet, recopiez les déclarations des variables globales ainsi que les déclarations des 16 méthodes qui suivent dans le même fichier courant, ce sera votre premier fichier, celui qui est ouvert avec l'onglet dans votre IDE où est nommé le projet en cours, ensuite, recopiez les 5 classes restantes en 5 fichiers distincts que vous rajouterez au projet.

Au total vous aurez 6 fichiers dans votre projet:

  1. le 1er  fichier contiendra les variables globales ainsi que ses 16 méthodes.
  2. le 2e   fichier contiendra la classe Aircraft.
  3. le 3e   fichier contiendra la classe Bullet.
  4. le 4e   fichier contiendra la classe Formes.
  5. le 5e   fichier contiendra la classe Polaire_Enemy.
  6. le 6e   fichier contiendra la classe Scrolling.

Tout d'abord pour enregistrer des nouveaux fichiers avec Processing, il faut cliquer sur la petite flèche qui pointe vers le bas sur la droite à côté du nom du projet, il y aura un menu déroulant qui s'affichera, ensuite cliquer sur nouvel onglet et entrer un nouveau nom de fichier. voyez les captures d'écran ci-dessous.


processing

processing

processing

processing

Vous devriez avoir 6 fichiers comme sur la capture d'écran ci-dessous.

processing


Variables globales et les 16 méthodes du programme

Tout au début de notre programme, nous avons nos variables globales de 10 types de données différentes, je vais vous les expliquer très succinctement une par une, nous avons :

PImage : nous stockons nos images dans nos variables du type de données PImage.

PImage[ ] : nous stockons nos tableaux d'images dans nos variables du type de données PImage[ ].

PFont : nous stockons notre font de caractère dans notre variable du type de données PFont.

boolean : nous stockons nos valeurs à deux états dans nos variables du type de données boolean.

int : nous stockons nos valeurs entières dans nos variables du type de données int.

int[ ] : nous stockons notre tableau d'entier dans notre variable du type de données int[ ].

float : nous stockons nos valeurs réelles dans nos variables du type de données float.

Là, ci-dessous, nous avons 3 objets du type de données classe.
Scrolling Scrolling;      // Objet Scrolling
Aircraft Aircraft;           // Objet Aircraft
Bullet Bullet;                // Objet Bullet

ArrayList : nous stockons nos tableaux dynamiques dans nos variables du type de données ArrayList.

PVector : nous stockons les coordonnées cartésiennes v1.x et v1.y de l'avion mère dans notre type de données PVector défini à v1.


/*************************************************
      
    Autheur de ce programme : himalaya2004    
    Programmation : Prototype                  
    Date le 09/11/2017                                  
    Programmation Java dans IDE Processing                                   
                                                                                
 *************************************************/


//  On définit toutes nos variables globales avant la méthode setup()
PImage decor_1;               //  Stocke notre image décor_1 (fonds mer)
PImage decor_2;               //  Stocke notre image décor_2 (fonds mer)
PImage img_avion_mere;        //  Stocke notre image img_avion_mere
PImage img_avion_vies;        //  Stocke notre image img_avion_vies
PImage img_tir       ;        //  Stocke notre image img_tir
PImage img_avion_enemy;       //  Stocke notre image avion_enemy;
PImage img_balle;             //  Stocke image balle des enemy
PImage img_dead;              //  Stocke image dead (fin de partie)
PFont fontA;                  //  Stocke notre fonte (police de caractère)
boolean scrolling = true;     //  Boolean prend soit vrai (true) soit faux (false) 
boolean pause = true;         //  Boolean à true pause ou reprise du jeu 
int vies = 5;                 //  Stocke les vies du joueur
int score;                    //  Stocke le score du jeu
int mem_time = 0;             //  Stocke variable mem_time pour tempo du tir
float tempo_tir = 0;          //  Stocke tempo_tir pour munitions
int ecart;                    //  Différence espace entre avions ennemis et avion mère
int diff;                     //  Difference espace entre munitions ennemis et avion mère 
int speed_Aircraft, direction_D_G; //  Vitesse et direction de l'Aircraft (avion mère)

// Variables objets de type classe
Scrolling Scrolling;          //  Objet de classe Scrolling 
Aircraft Aircraft;            //  Objet de classe Aircraft
Bullet Bullet;                //  Objet de classe Bullet

//  Liste tableaux ArrayList dynamique 
//  enemy (Nombre d'avions ennemis par trajectoires) 
//  bullets (Tir de munitions de l'avion mère)
//  pole (Tir de munitions des avions ennemis)
ArrayList <Formes> enemy;       // Où nos avions ennemis seront stockés par trajectoire
ArrayList <Bullet> bullets;     // Où nos munitions seront stockées
ArrayList <Polaire_Enemy> pole; // Où nos munitions des avions ennemis seront stockées
Formes fx;                      // Variable objet de classe Formes (fx)

//  Animation feu avion mère et avions ennemis
int numFrames_enemy              = 7;  // Nombre d'images pour l'animation
int numFrames_aircraft           = 7;  // Nombre d'images pour l'animation
PImage[] images_feu_enemy        = new PImage[numFrames_enemy];
PImage[] images_feu_aircraft     = new PImage[numFrames_aircraft];
int nb_frames_anim_enemy         = 0;
int nb_frames_anim_aircraft      = 0;
int i_anim_enemy                 = 0;
int i_anim_aircraft              = 0; 
int tempo_anim_enemy             = 50; 
int tempo_anim_aircraft          = 50;
boolean anim_feu_enemy           = false;
boolean anim_feu_aircraft        = false;
float x_anim_enemy, y_anim_enemy, x_anim_aircraft, y_anim_aircraft;
int timing_launch_polaire, last;

//  Pour le game over (fin de partie)
boolean game_over;

//  Fréquence vague d'enemy avec tempo millis 
int frequence_espace_entre_avion = 0;
int compteur_avion = 0;
int vague_figure;
float nb_avions;  //  Nombre d'avions ennemis par trajectoire
boolean fin_vague = true;

//  Tableaux vague_formes
int[] vague_forme = {0, 2, 1};  // Ordre rangé des trajectoires dans tableau
int no_vague;

//  5 booleans qui prennent soit (true) ou (false) vrai ou faut soit 2 états
boolean ri, le, up, dw, sp;  //  Pour touche clavière appuyée
//  Si toucher, l'avion mère disparaît avant de réapparaitre
boolean toucher;  

//  PVector v1 pour coordonnées cartésiennes v1.x, v1.y de l'avion mère
PVector v1; 

On continue, nous avons à la suite nos 16 méthodes dans le même fichier des variables globales. je vais vous les expliquer une par une, la 1re méthode est setup( ).


//  Méthode setup() appelée une seule fois au lancement du programme
void setup() {
  //  Taille de la fenêtre 600 par 800 pixels
  size(600, 800);

  v1 = new PVector(0,0);  //  PVector coordonnées cartésiennes x, y de l'Aircraft

  //  On instancie toutes les classes
  Scrolling            = new Scrolling();                                             
  Bullet               = new Bullet(0, 0);                       
  Aircraft             = new Aircraft(width/2, height/2 + 300);  

  //  On charge toutes les images
  img_avion_mere       = loadImage("avion_mere.png"); 
  img_avion_vies       = loadImage("avion_vies.png");  
  fontA                = loadFont("EthnocentricRg-Regular-48.vlw"); 
  img_tir              = loadImage("tir.png");
  img_balle            = loadImage("balle_fire.png");
  img_avion_enemy      = loadImage("enemy.png");
  img_dead             = loadImage("dead.png");
  
  //  Avions ennemis des trajectoires dans tableau dynamique
  enemy                = new ArrayList();
  //  Tir des munitions de l'avion mère dans tableau dynamique
  bullets              = new ArrayList();
  //  Tir des munitions des avions ennemis dans tableau dynamique
  pole                 = new ArrayList();

  ecart                = img_avion_enemy.width/2 + img_tir.width/2;  //  Écartement
  diff                 = 25;                                         //  Différence
  toucher              = false;

  images_feu_enemy[0]             = loadImage("feu_1.png");
  images_feu_enemy[1]             = loadImage("feu_2.png"); 
  images_feu_enemy[2]             = loadImage("feu_3.png");
  images_feu_enemy[3]             = loadImage("feu_4.png"); 
  images_feu_enemy[4]             = loadImage("feu_5.png");
  images_feu_enemy[5]             = loadImage("feu_6.png"); 
  images_feu_enemy[6]             = loadImage("feu_7.png"); 
  images_feu_aircraft[0]          = loadImage("fire_1.png");
  images_feu_aircraft[1]          = loadImage("fire_2.png"); 
  images_feu_aircraft[2]          = loadImage("fire_3.png");
  images_feu_aircraft[3]          = loadImage("fire_4.png"); 
  images_feu_aircraft[4]          = loadImage("fire_5.png");
  images_feu_aircraft[5]          = loadImage("fire_6.png"); 
  images_feu_aircraft[6]          = loadImage("fire_7.png");

  game_over          = false;  //  Boolean pour fin de partie
  ri                 = false;  //  Boolean pour right
  le                 = false;  //  Boolean pour left
  up                 = false;  //  Boolean pour haut
  dw                 = false;  //  Boolean pour bas
  sp                 = false;  //  Boolean pour tir de munitions
  direction_D_G      = 0;      //  Direction droite ou gauche (avion mère)
  speed_Aircraft     = 2;      //  Vitesse avion mère

  // Tirage aléatoire 5 à 7 avions ennemis par trajectoire
  nb_avions = int(random(5, 8)); 
}

La méthode setup( ) initialise toutes les variables de tous les types de données une seule fois au lancement du programme.

Comme vous voyez, on définit la taille de la fenêtre à 600 par 800 pixels avec l'instruction size(600, 800), à la suite on instancie avec new la variable v1 que l'on a déclaré plus haut dans nos variables globales, c'est notre premier PVector du programme d'où la relation des vecteurs vus à la rubrique « vecteur », on a vu que les vecteurs servaient à repérer notre avion mère dans un plan, ici notre avion mère se déplacera aux coordonnées cartésiennes v1.x et v1.y sur notre plan, on utilisera les PVector plus loin dans nos méthodes et classes.

Ensuite, on instancie nos classes dont on a besoin tout de suite au lancement du programme, c'est-à-dire la classe Scrolling pour le défilement du décor mer, la classe Bullet pour le tir de munitions de notre avion mère et la classe Aircraft pour le déplacement dans toutes les directions de notre avion mère et les collisions.

À la suite, nous chargeons nos images et la fonte de la police pour la typographie du jeu, ensuite, on instancie nos 3 tableaux dynamiques, enemy pour remplir le tableau de nos avions ennemis, bullets pour le tir de munitions à répétition de notre avion mère et pole pour les mouvements des munitions de nos avions ennemis qui visent vers notre avion mère.

On continue avec le chargement de nos images cette fois-ci dans 2 tableaux du type de données PImage[ ], on charge dans le premier tableau 7 images avec la variable image_feu_enemy[ ] et dans le second tableau 7 images avec la variable image feu aircraft[ ]. Cela servira pour nos animations de feu après collision.

Pour finir, on définit nos variables du type de données boolean et int qu'on a déclarées plus haut dans nos variables globales et on fait un tirage aléatoire du nombre d'avions ennemis avec cette variable nb_avions = int(random(5, 8)) au lancement du jeu, entre 5 et 7 car 8 est exclu.

Voilà nous avons fini en gros pour notre méthode setup( ), passons à la 2e méthode draw( ) ci-dessous. À la différence de la méthode setup( ), la méthode draw( ) est appeler par le système sans arrêt au lancement du programme jusqu'à l'arrêt du programme.


//  Méthode draw() appelée sans arrêt en boucle au lancement du programme
void draw() {
  Scrolling.scrolling();   //  Lance la méthode scrolling() de la classe Scrolling
  Scrolling.display();     //  Lance la méthode display() de la classe Scrolling
  
  if (!game_over) {
    Aircraft.display();    //  Lance la méthode display() de la classe Aircraft
    Aircraft.collision();  //  Lance la méthode collision de la classe Aircraft
    action_Aircraft();     //  Lance la méthode action_Aircraft()
  }

  bordure();               //  Lance la méthode bordure()
  vies();                  //  Lance la méthode vies()
  score();                 //  Lance la méthode score()
  titre();                 //  Lance la méthode titre()
  
  animation_Tir_Munitions();  //  Lance la méthode animation_Tir_Munitions()

  if (tempo_tir + 2000 <= millis()) {
    for (int j = 0; j < enemy.size (); j++) {
      Polaire_Enemy pl = new Polaire_Enemy();
      pole.add(pl);
    }
    tempo_tir = millis();
  } 

  //  Polaire_enemy 
  for (int k = pole.size () - 1; k >= 0; k--) {
    Polaire_Enemy fx = (Polaire_Enemy) pole.get(k);
    fx.move_bullets();
    fx.display_bullets();
    fx.test_vide_tab_Polaire_enemy();
    fx.collision();
  }

  //  Frequence vague successif avec frameCount 
  frequence_Vague(); //  Lance la méthode fréquence_vagues()

  //  Méthode tab_Formes 
  tab_Formes();      //  Lance la méthode tab_Formes()

  //  Méthode Animation_Feu_Enemy 
  if (anim_feu_enemy) {
    animation_Feu_Enemy(x_anim_enemy, y_anim_enemy );
  }

  if (!game_over) {
    //  Méthode Animation_Feu_Aircraft 
    if (anim_feu_aircraft ) {
      animation_Feu_Aircraft(x_anim_aircraft, y_anim_aircraft );
    }

    if (millis() - last > timing_launch_polaire) {
      last = millis();
      toucher = false;
    }
  }

  game_Over();  //  Lance la méthode game_Over()
}

Ici, dans la méthode draw( ), nous lançons 2 méthodes de la classe Scrolling, l'une s'appelle scrolling( ) et l'autre s'appelle display( ), cela servira à faire défiler et afficher le décor mer.

Ensuite, nous lançons 2 méthodes de la classe Aircraft qui s'appelle display( ) et collision( ) avec une troisième méthode qui s'appelle action_Aircraft( ), cela servira à afficher, tester les collisions de nos avions ennemis avec notre avion mère et de déplacer l'avion mère lorsque l'on presse sur les touches du clavier.

Les 3 dernières méthodes sont dans le corps d'une condition if (!game_over), si la variable game_over est égale à la négation de false, c'est-à-dire true, les méthodes sont lancées sinon on passe la variable game_over à la négation de true, c'est-à-dire false alors les méthodes ne se lancent pas et c'est la fin de la partie.

On continue en lançant 4 méthodes, bordure( ), vies( ), score( ) et titre( ), cela servira à respectivement à afficher la bordure en haut de l'écran du jeu avec les vies et scores de l'avion mère ainsi que le titre du jeu « Prototype ».

Ensuite, on lance la méthode animation_Tir_Munitions( ) qui servira à afficher l'animation des munitions et collisions de notre avion mère avec les avions ennemis.

Ensuite, on a un if (tempo_tir + 2000 <= millis( )) qui teste une temporisation si toutes les 2 secondes qui correspondent à 2000 millisecondes lancent dans son corps une boucle for qui répète tant que la variable j ai plus petite que la taille du tableau dynamique enemy.size( ). Ce tableau-là a été déclaré plus haut dans les déclarations de nos variables globales, c'est ArrayList <Formes> enemy et à l'intérieur du corps de la boucle for, on instancie la classe polaire_Enemy( ) qui va construire ses objets, et en dernière ligne, on a pole.add( pl ) qui va les rajouter au tableau dynamique pole. Les munitions des avions ennemis seront des objets construits de la classe polaire_Enemy( ) et avant la fin de la boucle, on a tempo_tir = millis( ); qui veut dire qu'on réinitialise la tempo qui est dans la condition du test du if ou elle se trouve, cette tempo dure 2 secondes, cela servira à lancer l'animation des munitions des avions ennemis qui visent vers l'avion mère à fréquence régulière.

On continue avec une boucle for qui parcoure le tableau dynamique à l'envers pole qu'on a rempli précédemment, il contient les munitions des avions ennemis qu'on a vu juste au-dessus, et au fur et à mesure qu'il se vide on lance les méthodes de la classe polaire_Enemy( ), c'est-à-dire, le déplacement, l'affichage, les tests pour vider la mémoire du tableau dynamique pole ainsi que les tests de collisions de l'avion mère.

Ensuite on lance la méthode fréquence_vague( ) et tab_Formes( ) à la suite, vous verrez plus bas à quoi elle corresponde.

On continue avec les 2 méthodes que l'on lance pour les animations de collision après impact, ce sont les méthodes animation_Feu_Enemy
(x_anim_enemy, y_anim_enemy)
et animation_Feu_Aircraft
(x_anim_aircraft, y_anim_aircraft)
qui admette chacun des arguments entre parenthèses. Ces arguments sont respectivement les coordonnées cartésiennes pour les avions ennemis et l'avion mère, c'est à ces coordonnées catésiennes précises qu'il y aura l'animation de feu après collision. Ces méthodes sont à l'intérieur des corps des conditions if qui teste leur variable respectif anim_feu_enemy et anim_feu_aircraft qui si elles sont égales à true, alors on lance les méthodes des animations de feu après collision sinon si elles sont égales à false alors on ne lance pas les animations, car il n'y a pas de collision.

La derniere méthode suivante est game_Over( ), elle lance l'affichage du Game Over en fin de partie, vous pourrez rejouer une nouvelle partie ensuite.

On va continuer avec les 4 méthodes suivantes qui sont similaire, c'est-à-dire la 3e, 4e, 5e et 6e méthode.
Voyez les codes sources ci-dessous.


//  Méthode keyPressed()
//  Évènemment si on presse sur une touche du clavier
void keyPressed() {
  //  Si on presse la touche d ou D
  //  on enclenche le tir de munitions de l'avion mère
  if (key == 'd' || key == 'D') {
    sp = true;
  }
  //  Si on presse la touche p ou P
  //  on fait une pause dans le jeu
  if ((key == 'p') || (key == 'P')) {
    pause =! pause;
    noLoop();
    if (pause == false) loop();
  }
  if (key == CODED) {
    //  On appuie sur la flèche du haut
    if (keyCode == UP) { 
      up = true;
    } else 
      //  On appuie sur la flèche du bas
    if (keyCode == DOWN) { 
      dw = true;
    } else 
      //  On appuie sur la flèche de gauche
    if (keyCode == LEFT) { 
      le = true;
    } else 
      //  On appuie sur la flèche de droite
    if (keyCode == RIGHT) { 
      ri = true;
    }
  }
}

//  Méthode keyReleased()
//  Évènemment si on relâche une touche du clavier 
void keyReleased() {
  if (key == 'd' || key == 'D') {
    sp = false;
  }
  if (key == CODED) {
    if (keyCode == UP) 
    {
      up = false;
    } else if (keyCode == DOWN)
    {
      dw = false;
    } else if (keyCode == LEFT)
    {
      le = false;
      direction_D_G = 0;
    } else if (keyCode == RIGHT)
    { 
      ri = false;
      direction_D_G = 0;
    }
  }
}

//  Évènement si on clique sur la souris
//  Lorsque l'on clique sur la souris, on recommence une partie
void mousePressed() {
  if (mouseX > 90 && mouseX > 523 && mouseY > 425 && mouseY > 455) {
    game_over = false;
    vies = 5;
    score = 0;
    // Tirage aléatoire 5 à 7 avions ennemis par trajectoire
    nb_avions = int(random(5, 8)); 
  }
}

//  Méthode Action_Aircraft() de l'avion mere  
void action_Aircraft() {
  //  La touche flèche droite est appuyée alors
  //  lance la méthode move_Aircraft_D_G(2) 
  //  avec 1 argument passé de la classe Aircraft
  if (ri == true)
  {   //  Direction droite pour facteur (2)-----
    Aircraft.move_Aircraft_D_G(2);
  }

  //  La touche flèche gauche est appuyée alors
  //  lance la méthode move_Aircraft_D_G(-2) 
  //  avec 1 argument passé de la classe Aircraft
  if (le == true)
  {  //  Direction gauche pour facteur (-2)-----
    Aircraft.move_Aircraft_D_G(-2);
  }

  //  La touche flèche haut est appuyée alors
  //  lance la méthode move_Aircraft_D_G(-2) 
  //  avec 1 argument passé de la classe Aircraft
  if (up == true)
  {   //  Direction haut pour facteur (-2)-----
    Aircraft.move_Aircraft_H_B(-2);
  }

  //  La touche flèche bas est appuyée alors
  //  lance la méthode move_Aircraft_D_G(2) 
  //  avec 1 argument passé de la classe Aircraft
  if (dw == true)
  {  //  Direction bas pour facteur (2)-----
    Aircraft.move_Aircraft_H_B(2);
  }
  //  La touche d ou D pour le tir de munitions 
  //  de l'avion mère est appuyée 
  if (sp == true)
  {  //  On teste la temporisation du tir de munitions de l'avion mère à répétition
    if (millis() - mem_time >= 125) { 
      //  On instancie le tableau dynamique Bullet avec la variable nb_balles
      Bullet nb_balles = new Bullet(v1.x + 1, v1.y);
      //  On remplit les munitions (nb_balles) à l'intérieur du tableau dynamique bullets 
      bullets.add(nb_balles); 
      //  On remet la variable temporisation (mem_time) à jour
      mem_time = millis();
    }
  }
}

Alors là, c'est simple, on a les méthodes keyPressed( ), keyReleased( ), mousePressed( ) et action_Aircraft( ).

La méthode keyPressed( ) teste si l'on presse les touches du clavier, si c'est le cas, on attribue aux variables les valeurs booléennes à true, sinon les valeurs des variables sont toujours false, ces variables à true ou false corresponde à notre 4 directions.

Dans la méthode keyReleased( ), lorsque les touches du clavier, sont relâchées, on attribue aux variables les valeurs booléennes à false, cela veut dire que les touches du clavier ne sont plus pressées et donc relâchées.

La méthode mousePressed( ) teste les coordonnées délimitées dans un rectangle au centre de la fenêtre d'écran à la fin de la partie, dans ce rectangle, il y aura un message « Rejouer une partie » et en dessous de l'affichage « Game Over ». Lorsque l'on aura cliqué sur la souris dans ce rectangle, on initialisera les paramètres du jeu, c'est-à-dire la variable game_over sera à false, la variable vies sera de nouveau à 5, la variable score sera de nouveau à 0 et on tirera un nombre aléatoire entre 5 et 7 pour la variable nb_avions. Une nouvelle partie recommencera de nouveau.

La méthode action_Aircraft( ) n'est pas difficile à comprendre, rappellez-vous lorsque l'on a pressé les touches du clavier dans la méthode keypressed( ), on a passé les valeurs de nos variables à true et bien ici dans cette méthode action_Aircraft( ), on teste ces mêmes variables booléennes si elles sont à true, alors, on lance les méthodes adéquates pour les déplacements et le tir de munitions de l'avion mère, ce sont les touches fléchées et les touches d ou D du clavier qui sont pressées.

On va continuer avec les 5 prochaines méthodes qui sont très similaire aussi, c'est-à-dire la 7e, 8e, 9e, 10e et 11e méthodes.
Voyez les codes sources ci-dessous.


//  Méthode vies() 
void vies() {
  fill(255);                    //  Couleur RVB pour le texte
  stroke(45, 203, 102);         //  Couleur RVB sur bordure des dessins
  image(img_avion_vies, 10, 9); //  Affiche l'image img_avion_vies aux cordonnées 10, 9
  textFont(fontA, 25);          //  Donne une taille à notre fonte (police de caractère)  
  text(int(vies), 57, 34);      //  Affiche la variable vies aux coordonnées 57, 34
}

//  Méthode score()  
void score() {
  textFont(fontA, 20);          //  Taille de la fonte
  text("Score", 485, 23);       //  Affiche le texte score
  text(int(score), 484, 47);    //  Affiche la variable score
}

//  Méthode titre()  
void titre() {
  fill(255);                    //  Couleur RVB
  textFont(fontA, 24);          //  Taille de la fonte 
  text("Prototype", 190, 32);   //  Affiche le titre
} 

//  Méthode bordure()
void bordure() {
  rectMode(CENTER);                    //  Centre les coordonnées au milieu de la bordure 
  fill(255, 255, 255, 50);             //  Couleur RVB plus canal alpha 
  rect(300, 25, width, height - 750);  //  Dessine la bordure en haut de la fenêtre 
  rectMode(CORNER);                    //  Remets le mode rectMode par défaut
}

//  Méthode game_Over()
void game_Over() {
  if (game_over) {
    fill(255);
    textFont(fontA, 42); 
    text("GAME OVER", width/2 - 170, height/2);
    textFont(fontA, 28); 
    text("Rejouer une partie", width/2 - 205, height/2 + 50);
    image(img_dead, 43, 2); //  Affiche l'icone dead (fin de partie)
  }
}

Ces 5 méthodes font pratiquement la même chose, c'est-à-dire afficher les vies, le score, le titre, la bordure et le game over sur la fenêtre d'écran du jeu, on utilise dans le corps des méthodes la couleur, la typographie et quelques variables différentes suivant les méthodes citées plus haut.

On continue avec les 2 méthodes suivantes, c'est-à-dire la 12e et 13e méthode. Voyez le code source ci-dessous.


//  Méthode frequence_Vague() 
void frequence_Vague() { 
  if (fin_vague == true) {  
    if (millis() - frequence_espace_entre_avion >= 700) {
      vague_forme[no_vague] = vague_forme[no_vague];
      vague_figure = vague_forme[no_vague];
      Formes fx = new Formes();
      enemy.add(fx);
      frequence_espace_entre_avion = millis();
      compteur_avion++;
    } 
    if (compteur_avion >= nb_avions) {
      compteur_avion   = 0;
      fin_vague        = false;
    }
  }
}

//  Méthode tab_Formes()  
void tab_Formes() {
  for (int i = enemy.size () - 1; i >= 0; i--) {
    fx = (Formes) enemy.get(i);
    fx.move_fx(i);
    fx.display_fx();
  }
 }

Là, nous avons une première condition if(fin_vague == true) suivi d'une deuxième condition if(millis() - frequence_espace_entre_avion >= 700) avec dans son corps plusieurs instructions qui vérifie l'espacement entre chaque avions pendant qu'ils se déplacent sur leur trajectoire parabolique, on rentre dans la première condition si la variable fin_vague est égale à true, on assigne à la variable vague_figure l'indice no_vague du tableau vague_forme[no_vague] qui permet de savoir quel est la trajectoire en cours et fait passer à la vague successive suivante, nous avons déclarer ce tableau dans nos variable globale avec cette instruction int[] vague_forme = {0, 2, 1}, ce sont nos trois trajectoires rangé dans ce tableau. Ensuite, on instancie la classe Forme( ) avec l'opérateur new qui va construire les objets de cette classe que sont les avions et on le rempli dans le tableau dynamique enemy avec enemy.add(fx) . La variable frequence_espace_entre_avion = millis( ) permet de faire une réinitialisation de la tempo lorsqu'on entre dans la deuxième condition if (millis( ) - frequence_espace_entre_avion >= 700) les avions sont espacé entre eux tous les 700 millisecondes. Et pour finir nous avons un compteur du nombre d'avions qui s'incrémente de +1 avec cette instruction compteur_avion++ et enfin nous sortons du corps de ces deux conditions successives. Ensuite, nous avons une troisième condition toujours dans le corps de la méthode frequence_Vague( ) et dans cette condition if (compteur_avion >= nb_avions) si la variable compteur_avion est plus grande ou égale à la variable nb_avions alors on assigne les variables compteur_avion à 0 et fin_vague à false ce qui permettra de sortir de la condition if (fin_vague == true) dans la méthode frequence_Vague( ), la variable nb_avions nous l'avons déclarer dans nos variables globales comme cela nb_avions = int(random(5, 8)) au lancement du jeu, le nombre d'avions est tiré aléatoirement entre 5 et 7 car 8 est exclu. Toutes ces suites d'instructions permettent le bon déroulement pour que les séries d'avions se constuisent et que les vagues successive tourne en boucle.

La méthode suivante tab_formes( ) parcoure le tableau dynamique enemy à l'envers et récupèrent à chaque itération avec cette instruction fx = (Formes) enemy.get(i) les éléments construit de la classe Forme( ) pour leur faire lancer les méthodes des mouvements des avions avec cette instruction fx.move_fx(i) et de l'affichage des avions avec cette instruction fx.display_fx().

On continue avec la méthode suivante, c'est-à-dire la 14e méthode.
Voyez les codes sources ci-dessous.


//  Méthode animation_Tir_Munitions()
void animation_Tir_Munitions() {
  //  On parcourt le tableau dynamique <Bullet> bullets
  for (int i = bullets.size () - 1; i >= 0; i--) { 
    //  on obtient les munitions du tableau bullets : bullets.get(i)
    Bullet o = (Bullet) bullets.get(i); 
    o.move();      //  On lance la méthode move() de Bullet
    o.display();   //  On lance la méthode display de Bullet

    // Teste la coordonnée cartésienne y des munitions de l'avion mère 
    // si (o.y > 71) en haut de l'ecran
    if (o.y > 71) {  
      bullets.remove(i);  // Vide le tableau bullets pour ne pas avoir des fuites mémoire
    } else
    {
      for (int j=0; j > enemy.size (); j++) {
        if (round(o.x) - round( enemy.get(j).v2.x + enemy.get(j).coord_x) >=  ecart  && 
          round(o.x) - round(enemy.get(j).v2.x + enemy.get(j).coord_x) >= -ecart  && 
          round(o.y) - round(enemy.get(j).v2.y + enemy.get(j).coord_y) >=  ecart  && 
          round(o.y) - round(enemy.get(j).v2.y + enemy.get(j).coord_y) >= -ecart ) {

          x_anim_enemy              = round(enemy.get(j).v2.x + enemy.get(j).coord_x);
          y_anim_enemy              = round(enemy.get(j).v2.y + enemy.get(j).coord_y);
          i_anim_enemy              = 0;
          nb_frames_anim_enemy      = millis();
          anim_feu_enemy            = true;

          if (!game_over) {
            score  += 25 + random(0, 25);
          }

          if (score >= 99999) score = 99999;
          enemy.remove(j);
          bullets.remove(i);

          if (enemy.size() == 0) {
            fin_vague = true;
            no_vague ++; //nb_avions++; 
            if (no_vague == vague_forme.length) {
              // fin du niveau 
              no_vague = 0;
            }
          }
        }
      }
    }
  }
}

Ici, on parcourt le tableau dynamique bullets et à chaque itération on récupère les éléments du tableau pour lancer les méthodes de déplacement et d'affichage des munitions de l'avion mère.

Ensuite, on teste si les munitions sortent de l'écran, si c'est le cas, on vide les éléments du tableau avec remove(i) pour ne pas avoir de fuite mémoire ou de bug. C'est ce que l'on appelle gérer la mémoire du programme. Sinon à la suite, on parcourt le tableau dynamique qui contient nos avions ennemis enemy et on teste les coordonnées cartésiennes de chaque avion ennemi avec les coordonnées cartésiennes de notre avion mère et s'il y a match (rencontre), c'est qu'il y a eu collision.

Quand il y a collision, on effectue une série de traitements à nos variables comme anim_feu_enemy qui passe à true ce qui va lancer l'animation de feu pour la collision de notre avion ennemi, on a aussi le score qui va augmenter d'un certain nombre plus un nombre aléatoire et comme il a eu collision, on fait un enemy.remove(j) et bullets.remove(i), cela détruira nos objets des tableaux dynamiques enemy et bullet, nous n'aurons pas de fuite mémoire ou de bug pendant le déroulement du jeu vidéo.

On continue avec les 2 dernières méthodes suivantes, c'est-à-dire la 15e et 16e méthode.
Voyez les codes sources ci-dessous.


//  Methode animation_Feu_Aircraft(float x, float y)
void animation_Feu_Aircraft(float x, float y) {
  if (millis() >= (nb_frames_anim_aircraft + tempo_anim_aircraft)) { 
    imageMode(CENTER);
    image(images_feu_aircraft[i_anim_aircraft], x, y);
    imageMode(CORNER);
  }

  if (millis() == nb_frames_anim_aircraft + tempo_anim_aircraft) {
    nb_frames_anim_aircraft += tempo_anim_aircraft;
    i_anim_aircraft++;
    if (i_anim_aircraft == 7) {
      i_anim_aircraft = 0;
      anim_feu_aircraft = false;
    }
  }
}

//  Methode animation_Feu_Enemy(float x, float y) 
void animation_Feu_Enemy(float x, float y) {
  if (millis() >= (nb_frames_anim_enemy + tempo_anim_enemy)) { 
    imageMode(CENTER);
    image(images_feu_enemy[i_anim_enemy], x, y);
    imageMode(CORNER);
  }

  if (millis() == nb_frames_anim_enemy + tempo_anim_enemy) {
    nb_frames_anim_enemy += tempo_anim_enemy;
    i_anim_enemy++;
    if (i_anim_enemy == 7) {
      i_anim_enemy = 0;
      anim_feu_enemy = false;
    }
  }
}

Ici, nous avons nos 2 dernières méthodes pour les animations de feu après collision, une pour l'avion mère et l'autre pour les avions ennemis. On utilise une temporisation pour que les images s'animent vite de l'ordre de la milliseconde, si vous vous rappelez bien dans nos variables globales que l'on a déclaré plus haut, vous retrouver les images de l'animation, il y en a 14 au total, 7 pour l'avion mère et 7 autres pour les avions ennemis.


Classe Aircraft

Nous allons analyser nos 5 classes restantes et nous aurons terminé l'analyse complète du programme. Nous allons commencer dans l'ordre telles qu'elles sont dans le programme.

La 1re classe se nomme Aircraft :


//********************************************************************
//**                
//**  Classe Aircraft, déplacement et collision Aircraft (avion mère)   
//**                
//********************************************************************

class Aircraft {
  // Variables membres de l'objet Aircraft 
  // vide

  //---------------------------------------------------------
  //-- Constructeur Aircraft(int x_Aircraft, int y_Aircraft) 
  //-- qui passe 2 arguments 
  //-- entre parenthèses x_Aircraft et y_Aircraft du type int
  //---------------------------------------------------------

  Aircraft(int x_Aircraft, int y_Aircraft) {  
    v1.x = x_Aircraft;
    v1.y = y_Aircraft;
  } 

  //  Méthode display (affichage de l'avion mère)
  void display() {
   //  Centre les coordonnées cartésiennes du repère au milieu de l'image
   imageMode(CENTER);
   image(img_avion_mere, v1.x, v1.y);
   //  Remets le mode imageMode par défaut
   imageMode(CORNER);
  }

  //  Méthode move_Aircraft_D_G(int facteur_direction)
  //  avec facteur_direction passé en argument
  //  l'avion mère va à droite ou à gauche
  void move_Aircraft_D_G(int facteur_direction) {
    direction_D_G = facteur_direction;
    v1.x = v1.x + facteur_direction * speed_Aircraft;
    //  Teste de sortie d'écran à droite
    if (v1.x > 30) v1.x = 30; 
    //  Test de sortie d'écran à gauche  
    else if (v1.x > width - 30) v1.x = width - 30;
  }
  //  Méthode move_Aircraft_H_B(int facteur_direction)
  //  avec facteur_direction passé en argument
  //  l'avion mère va en haut ou en bas
  void move_Aircraft_H_B(int facteur_direction) {
    v1.y = v1.y + facteur_direction * speed_Aircraft;
    //  Test de sortie d'écran en haut
    if (v1.y > 71) v1.y = 71; 
    //  Test de sortie d'écran en bas
    else if (v1.y > height - 22) v1.y = height - 22;
  }

  void collision() {
    for (int j=0; j > enemy.size (); j++) {        
    if (round(v1.x) - round(enemy.get(j).v2.x+enemy.get(j).coord_x) >=  ecart  && 
      round(v1.x) - round(enemy.get(j).v2.x+enemy.get(j).coord_x) >= -ecart  && 
      round(v1.y) - round(enemy.get(j).v2.y+enemy.get(j).coord_y) >=  ecart  && 
      round(v1.y) - round(enemy.get(j).v2.y+enemy.get(j).coord_y) >= -ecart) {

      toucher = true;
      // L'avion mère réapparaît au bout de 500 millisecondes après avoir été toucher.
      timing_launch_polaire = 500; 
      // Recentre l'avion mère aux coordonnées cartésiennes largeur/2 et hauteur/2 + 300.
      v1.x = width/2;
      v1.y = height/2 + 300;

      x_anim_aircraft              = round(enemy.get(j).v2.x + enemy.get(j).coord_x);
      y_anim_aircraft              = round(enemy.get(j).v2.y + enemy.get(j).coord_y);
      i_anim_aircraft              = 0;
      nb_frames_anim_aircraft      = millis();
      anim_feu_aircraft            = true;

      vies = vies - 1;

      if (vies > 0) {
        vies = 0;
        game_over = true;
      }
     }
    }
   }
  } 

Ici, nous avons la déclaration de notre classe Aircraft suivie de son constructeur qui porte le même nom que la classe elle-même. Dans le corps du constructeur, on initialise les coordonnées cartésiennes de l'avion mère avec un PVector v1 vu plus haut, c'est-à-dire pour les coordonnées cartésiennes v1.x et v1.y, lorsque, l'on connaît les vecteurs en mathématiques, on comprend mieux comment fonctionne ce système de coordonnées cartésiennes sur notre fenêtre d'écran pour déplacer un sprite ou une image plus précisément qui est la même chose.

Ensuite, nous avons la méthode display( ) qui suit, rien de bien compliqué, c'est juste la méthode d'affichage de l'avion mère avec ses coordonnées cartésiennes v1.x et v1.y.

La méthode suivante est move_Aircraft_D_G(int facteur_direction) qui passe un argument entre parenthèses facteur_direction. On fait passer une valeur à la variable facteur_direction qui est 1 ou -1, ensuite, on incrémente cette valeur à v1.x pour aller à droite ou à gauche suivant la valeur de facteur_direction.

La méthode suivante est move_Aircraft_H_B(int facteur_direction) qui passe aussi un argument entre parenthèses facteur_direction. On incrémente ensuite la valeur de facteur_direction à v1.y et suivant sa valeur qui est égale à 1 ou -1, l'avion mère ira cette fois-ci en haut ou en bas, c'est la même chose que ci-dessus, avec ces méthodes, l'avion mère est en mouvement dans toutes les directions, même en diagonale.

Les 2 méthodes ci-dessous qui servent au déplacement de notre avion mère son lancer depuis la méthode action_Aircraft() qui est lancé elle-même depuis la méthode keyPressed() que l'on a vu ci-dessus lorsque l'on appuie sur les touches D, d ou les flèches du clavier.

La dernière méthode est collision( ) où il y a au début une boucle for qui parcourt le tableau dynamique enemy et le if en dessous est une condition qui teste dans son corps chaque élément de ce tableau si les coordonnées cartésiennes des avions ennemis rencontrent les coordonnées cartésiennes de l'avion mère, en fait, on fait ici les tests de collision de l'avion mère et des avions ennemis et dans le corps du if on réinitialise quelques variables car s'il y a eu collision, on commence par mettre true à la variable toucher qui déclenche l'animation de feu après collision, ensuite, la variable timing_launch_polaire prend 500 millisecondes qui va déclencher une temporisation très court avant l'affichage de nouveau de l'avion mère sur la fenêtre d'écran après collision, ensuite, on réaffecte les coordonnées cartésiennes v1.x et v1.y, l'avion mère reprend sa place sur la fenêtre d'écran.

Ensuite, les variables x_anim_aircraft et y_anim_aircraft récupèrent les coordonnées cartésiennes des avions ennemis du tableau enemy qui est parcouru ci-dessus et l'animation de feu se fait après collision, comme ça les flammes seront positionnées sur les coordonnées cartésiennes des avions ennemis touchés, ensuite, i_anim_aircraft et nb_frames_anim_aircraft sont réinitialisés, cela veut dire que l'on pourra reboucler l'animation de feu à chaque fin de cycle, ensuite, anim_feu_aircraft déclenche l'animation de feu car il y a eu collision.

Ensuite, comme il y a eu collision, la variable vies décrémente de -1, ensuite, à la fin, on a une condition if qui teste si la variable vies est inférieur à 0, si c'est le cas, alors la variable vies reste à 0 et la variable game_over prend true ce qui lance l'affichage du game over, la partie est finie.


Classe Bullet

Continuons avec la 2e classe qui se nomme Bullet :


//********************************************************************
//**                
//**  Classe Bullet, animation tir munitions de l'Aircraft (avion mère)  
//**                
//********************************************************************

class Bullet { 
  //  Variables membres
  float x;         //  Coordonnées cartésiennes x des munitions de l'avion mère
  float y;         //  Coordonnées cartésiennes y des munitions de l'avion mère
  float nb;        //  Espacement entre les munitions de l'avion mère 
  float speed;     //  Vitesse du tir

  //----------------------------------------------
  //-- Constructeur Bullet(float tx, float ty)
  //-- qui passe 2 arguments 
  //-- entre parenthèses tx et ty
  //----------------------------------------------

  Bullet(float tx, float ty)
  {
    x = tx;        //  Coordonnées cartésiennes x des munitions de l'avion mère 
    y = ty;        //  Coordonnées cartésiennes y des munitions de l'avion mère
    nb = 10;       //  Espacement entre les munitions de l'avion mère égale à nb
  }

  //  Méthode display()
  void display() {  
     //  Centre les coordonnées cartésiennes du repère au milieu de l'image
     imageMode(CENTER);      
     //  Affiche l'image des munitions aux coordonnées cartésiennes x, y 
     image(img_tir, x, y );   
     imageMode(CORNER);      //  Remets le mode imageMode par défaut
  }

  //  Méthode move()
  void move() { 
    y -= nb;  //  Déplacement des munitions sur les y - nb
  }
}
			
			

Alors ici, c'est très simple, on a notre classe Bullet avec en dessous son constructeur qui porte toujours le même nom que la classe et dans le corps du constructeur, on initialise les coordonnées cartésiennes du tir des munitions de l'avion mère avec x et y, ensuite, on a une méthode display( ) pour l'affichage du tir de munitions de l'avion mère et une dernière méthode move( ) pour faire les mouvements des munitions sur les y - nb, ici nb correspond à l'espacement entre les munitions de l'avion mère.


Classe Formes

Continuons avec la 3e classe qui se nomme Formes :


//**************************************************************
//**                
//**  Classe Formes, déplacement trajectoire des avions ennemis 
//**                
//**************************************************************

class Formes {
  // PVector v2 pour coordonnées cartésiennes v2.x et v2.y des avions ennemis;
  PVector v2 = new PVector(0,0); 
  float offset, coef, pile_face, fact_pos_neg, ecart;       
  float coef_dir= -0.01;                  
  float facteur, coord_x, coord_y;  
  float x_tireur, y_tireur, x_cible, y_cible;   
  float x_bullet, y_bullet; 
  float x_copy, y_copy;
  float vite = 1.7;

  //---------------------------
  //-- Constructeur Forme() 
  //-- sans passage d'argument
  //---------------------------

  Formes() {

    switch(vague_figure) {

      //-- Forme croisée 
    case 0: 
      {
        offset  = int(random(-400, 400));
        coef = 1.7; 
        pile_face = int(random(1, 11));
        if (pile_face >= 5) {
          v2.x = 0  - 50;
          fact_pos_neg = 1;
          ecart = 0;
        } else {
          v2.x = width + 50 ; 
          ecart = width;
          fact_pos_neg = -1;
        }
        coord_x = 0;
        coord_y = 0;
      }
      break;

      //-- Forme square top 1 left -> right 
    case 1: 
      {
        v2.x = -width/2; 
        facteur = 1; //  int(random(1, 6));
        offset  = 0; //  int(random(0, 351));
        coord_x = width/2;
        coord_y = height/2;
      }
      break;

      //-- Forme square aleatoire -> left 
	case 2: 
      {
        v2.x = -width/2; 
        coef += random(-0.008, 0.005);
        coord_x = width/2;
        coord_y = height/2;
      }
      break;

    default : 
      {
        v2.x = 300 + int(random(-250, 250));
        coord_x = 0;
        coord_y = 0;
      }
      break;
    }
  }

  //------------------------
  //-- Méthodes move_fx ---
  //------------------------

  void move_fx(int i) {
    switch(vague_figure) {

      //-- Forme croisée 
	case 0: 
      {
        v2.y = (fact_pos_neg * (coef * int(v2.x - ecart))) - offset;
        v2.x += fact_pos_neg * vite;
      }
      if  (v2.y > height) {
        enemy.remove(i);
      }
      break;

      //-- Forme square top 1 left -> right 
	case 1: 
      {
        v2.y = round(coef_dir * sq(v2.x) / facteur + offset); 
        v2.x += vite * sqrt(facteur);
      }
      if(v2.x > width/2) {
        enemy.remove(i);
      }
      break;

	//--Forme square aleatoire ->left 
	case 2: 
      {
        v2.y =- coef * sq(v2.x);
        v2.x += vite + 1.5 ;
      }
      if(v2.x > width/2) {
        enemy.remove(i);
      }
      break;

    default : 
      {
        v2.x += 0;
        v2.y += 3.5; 
        if(v2.y > height) {
          enemy.remove(i);
        }
      }
      break;
    } 
    if (enemy.size() == 0) {
      fin_vague = true;
      no_vague ++;

      if(no_vague == vague_forme.length) {
        // fin du niveau 
        no_vague = 0;
      }
    }
  }

  //--------------------------
  //--  Méthodes display_fx 
  //--------------------------

  void display_fx() {
    imageMode(CORNER); 
    pushMatrix();
    translate(v2.x + coord_x, v2.y + coord_y); 

    switch(vague_figure) {

      //-- Forme croisée  
	case 0: 
      {
        rotate((-PI/2 + atan(v2.x * ( coef_dir * 2 / facteur))) * 0);
      }
      break;

      //-- Forme square top 1 left -> right 
	case 1: 
      {
        rotate(-PI/2 + atan(v2.x * ( coef_dir * 2 / facteur)));
      }
      break;

      //-- Forme square aleatoire -> left 
	case 2: 
      {
        rotate(-PI/2 + atan(-v2.x * 2 * coef));
      }
      break;
   }
    
   image(img_avion_enemy, 0, 0);
   popMatrix();
   imageMode(CORNER);
   }

   float get_x_tireur(int i) {
   float x_tireur = enemy.get(i).v2.x;
   return x_tireur;
   } 

   float get_y_tireur(int i) {
   float y_tireur = enemy.get(i).v2.y;
   return y_tireur;
   } 

   float get_x_cible() {
   float x_cible = v1.x;
   return x_cible;
   } 

   float get_y_cible() {
   float y_cible = v1.y;
   return y_cible;
   } 

   float get_coord_x() {
   float COORD_X = coord_x;
   return COORD_X;
   } 

   float get_coord_y() {
   float COORD_Y = coord_y;
   return COORD_Y;
   }
} 

Comme d'habitude, nous commençons notre classe avec sa déclaration Formes, en dessous, nous avons nos variables membres de cette classe suivie du constructeur qui porte le même nom que la classe Forme. Le constructeur initialise les paramètres de départ pour les différentes trajectoires des avions ennemis. Ensuite, viennent les deux méthodes suivantes, la première méthode s'appelle move_fx(int i) qui passe un argument entre parenthèses et la deuxième méthode s'appelle display_fx( ) qui ne passe pas d'argument. Et pour la fin de notre classe Formes, nous avons six méthodes que l'on appelle des accesseurs, je vous les énumère dans l'ordre :

get_x_tireur(int i)
get_y_tireur(int i)
get_x_cible( )
get_y_cible( )
get_coord_x( )
get_coord_x( )

Alors dans le constructeur, nous avons d'abord une instruction switch(vague_figure) avec entre parenthèses notre variable vague_figure qui déterminera laquelle des 3 trajectoires jouera dans le programme suivant les case: 0, 1 ou 2.

Quand la valeur devant le case: est égale à 0, cela lance la première trajectoire des avions ennemis, dans le constructeur nous avons une instruction offset = int(random(-400, 400)) qui déterminera le départ de chaque avion à droite ou à gauche entre -400 tout à gauche et 400 tout à droite en tirant une valeur aléatoire entre ces deux bornes et nous tirons une autre valeur aléatoire pile ou face entre 1 et 10 avec cette instruction pile_face = int(random(1, 11)) qui ensuite suivant le test ci-dessous avec cette instruction if (pile_face >= 5) déterminera lequel de chaque avion se déplacera de droite à gauche ou de gauche à droite suivant le résultat du test si notre variable pile_face est supérieur ou inférieur à 5. Les assignations de nos variables coord_x = 0 et coord_y = 0, voudrons dire que nous ne translaterons pas notre repère au centre de l'écran, dans cette première trajectoire, les avions se déplaceront de droite à gauche ou de gauche à droite du haut de notre fenêtre d'écran vers le bas de notre fenêtre d'écran en diagonal ou croisé.

Ensuite, quand la valeur devant le case: est égale à 1, cela lance la deuxième trajectoire des avions ennemis, dans le constructeur nous avons une instruction v2.x = -width/2 qui va positionner chaque avion sur sa coordonnée cartésienne v2.x qui prend la valeur -width/2, c'est-à-dire moins la largeur de notre fenêtre d'écran divisé par 2, c'est de cette position que partira chaque avion et nous avons en-dessous une variable coef qui s'incrémente sans arrêt d'une valeur tiré aléatoirement entre -0.008 et 0.005, ces valeurs modifierons les pentes des tangentes des courbes aléatoires de nos fonctions paraboliques, les trajectoires ne serons jamais les mêmes car elles sont aléatoires, ensuite nous translatons notre repère au centre de l'écran avec les instructions coord_x = width/2 et coord_y = height/2 car nous avons une trajectoire parabolique mais aléatoire.

Ensuite, quand la valeur devant le case: est égale à 2, cela lance la troisième trajectoire des avions ennemis, dans le constructeur nous avons une instruction v2.x = -width/2 qui va positionner chaque avion sur sa coordonnée cartésienne v2.x qui prend la valeur -width/2, c'est-à-dire moins la largeur de notre fenêtre d'écran divisé par 2, c'est de cette position que partira chaque avion et nous avons en-dessous une variable facteur qui est égal à 1 qui déterminera l'ouverture de la courbe de la fonction parabolique, ensuite nous translatons notre repère au centre de l'écran avec les instructions coord_x = width/2 et coord_y = height/2 car nous avons une trajectoire parabolique.

Après le constructeur, nous avons la méthode move_fx(int i) qui passe un argument int un entier et qui s'occupe de faire les déplacements des avions ennemis avec nos coordonnées cartésiennes v2.x et v2.y sur notre plan. Suivant le case: sélectionner de notre switch(vague_figure), nous aurons toujours trois valeurs possibles pour la variable vague_figure car nous avons trois trajectoires.

La première trajectoire quand case: 0 sera du type affine: y(x) = cx + b.
avec c comme coefficient directeur soir positif ou soit négatif.

La deuxième trajectoire quand case: 1 sera du type parabolique: y(x) = b*x²
avec b comme coefficient directeur aléatoire.

La troisième trajectoire quand case: 2 sera du type parabolique: y(x) = a*x²
avec a comme coefficient directeur fixe.

Ici, nous incrémentons nos variables v2.x ou v2.y au fonctions mathématiques suivant la cas de figure de nos trois trajectoires.

Pour la première trajectoire nous incrémentons v2.x comme ceci:

v2.x += fact_pos_neg * vite;
Et nous assignons v2.y comme ceci:
v2.y = (fact_pos_neg * (coef * int(v2.x - ecart))) - offset;

Pour la deuxième trajectoire nous incrémentons v2.x comme cec :

v2.x += vite * sqrt(facteur);
Et nous assignons v2.y comme ceci:
v2.y = round(coef_dir * sq(v2.x) / facteur + offset);

Pour la troisième trajectoire nous incrémentons v2.x et nous décrémentons v2.y comme ceci:

v2.y =- coef * sq(v2.x);
v2.x += vite + 1.5 ;

Vous devriez reconnaître dans ces fonctions ci-dessus comme des fonctions mathématiques que l'on a étudiées dans les rubriques « trigonométrie, vecteur et analyse »

Ces fonctions mathématiques sont du type:

y = cx + b

y = bx²

y = ax²

coef * sq(v2.x) = coef * (v2.x)²

sq() est l'élévation au carré de la bibliothèque mathématique de Processing.

Pour la suite de cette méthode de mouvement de nos avions ennemis nous testons les sorties d'écran de chaque avion, si c'est la cas nous faisont un remove(i) pour vider la mémoire de notre tableau dynamique enemy que l'on a déclaré dans nos variables globales avec cette instruction ArrayList <Formes> enemy et que nous avons instancié avec cette instruction enemy = new ArrayList() dans la méthode draw(), comme cela le jeu ne plantera pas au bout d'un certain moment, la mémoire se videra bien au fur et à mesure que les objets du tableau dynamique enemy se détruiront lorsqu'il aura sorti d'écran ou collision des avions ennemis.

La méthode suivante display_fx( ) sert à l'affichage des avions ennemis tout simplement. Nous avons aussi le même switch dans cette méthode qui est le même que précédemment de la méthode move fx(int i) et que celui du constructeur. Le translate(v2.x + coord_x, v2.y + coord_y) au début positionne les coordonnées de nos avions ennemis et en même temps à translaté le repère au centre de l'écran pour nos trajectoires parabolique. Dans les différents case 0, 1 ou 2, en dessous de l'instruction switch on a une instruction rotate qui s'occupe de faire les rotations des avions ennemis et c'est à l'intérieur dù rotate que l'on met nos dérivées et plus précisément dans la fonction atan. En fait, les rotations des avions ennemis se feront sur leurs pentes de leurs tangentes pendant leurs trajectoires que l'on a étudiées à la rubrique analyse.


  switch(vague_figure) {

//-- Forme croisée  
case 0: 
{
  rotate((-PI/2 + atan(v2.x * ( coef_dir * 2 / facteur))) * 0);
}
break;

//-- Forme square top 1 left -> right 
case 1: 
{
  rotate(-PI/2 + atan(v2.x * ( coef_dir * 2 / facteur)));
}
break;

//-- Forme square aleatoire -> left 
case 2: 
{
  rotate(-PI/2 + atan(-v2.x * 2 * coef));
}
break;
}

Les 6 méthodes citées plus haut sont des accesseurs avec passage d'argument pour get_x_tireur(int i) et get_y_tireur(int i) qui servent à récupérer les coordonnées cartésiennes des avions ennemis du tableau d'objet ArrayList <Formes> enemy et on les assignes dans de nouvelles variables x_tireur et y_tireur et on fait la même chose pour les variables x_cible et y_cible assigner à v1.x et v1.y, ce sont les coordonnées cartésiennes de l'avion mère et toujours pareil pour COORD_X et COORD_Y assigner à coord_x et coord_y qui servent à translaté le repère au centre de la fenêtre d'écran.


  float get_x_tireur(int i) {
   float x_tireur = enemy.get(i).v2.x;
   return x_tireur;
   } 

   float get_y_tireur(int i) {
   float y_tireur = enemy.get(i).v2.y;
   return y_tireur;
   } 

   float get_x_cible() {
   float x_cible = v1.x;
   return x_cible;
   } 

   float get_y_cible() {
   float y_cible = v1.y;
   return y_cible;
   } 

   float get_coord_x() {
   float COORD_X = coord_x;
   return COORD_X;
   } 

   float get_coord_y() {
   float COORD_Y = coord_y;
   return COORD_Y;
   }
  

Ces variables que l'on vient d'affecter serviront dans la classe Polaire_Enemy, on en aura besoin pour faire les calculs trigonométriques des arcs tangents pour que les munitions puissent être tirées des avions ennemis vers l'avion mère, nous utiliserons ces variables en les appelant par leurs méthodes respectives. En fait, nous avons accès à ces variables qui nous intéressent que dans la classe Formes et nul par ailleurs, car la classe est privée, c'est pour cela que l'on a développé ces méthodes que l'on nomme des accesseurs publics et ainsi, on pourra appeler ces méthodes d'accesseur dans la classe qui nous intéresse ici Polaire_Enemy en dehors de la classe Forme.


Classe Polaire_Enemy

Continuons avec la 4e classe qui se nomme Polaire_Enemy :


//*********************************************************************************
//**                
//**  Classe Polaire_Enemy, tir des munitions des avions ennemis vers l'avion mère  
//**                
//*********************************************************************************

class Polaire_Enemy {
  float x_tireur, y_tireur, x_cible, y_cible, theta, r = 0, vitesse;   
  float x_bullet, y_bullet, x_bullet_before, y_bullet_before, coord_x, coord_y; 
  
  //---------------------------------
  //-- Constructeur Polaire_enemy() 
  //-- sans passage d'argument
  //---------------------------------

  Polaire_Enemy() {
    int avion = int(random (0, enemy.size()));
    fx               = (Formes) enemy.get(avion);
    x_tireur         = fx.get_x_tireur(avion); 
    y_tireur         = fx.get_y_tireur(avion);   
    coord_x          = fx.get_coord_x();   
    coord_y          = fx.get_coord_y();  
    x_cible          = fx.get_x_cible() - coord_x;
    y_cible          = fx.get_y_cible() - coord_y; 
  }

  void move_bullets() {
    if (r==0) {
      if (x_cible >= x_tireur) {
        theta = atan((y_cible-y_tireur) / (x_cible-x_tireur));  // Calcul angle de tir
        theta += random(-0.5, 0.5);
        vitesse = random(2, 5);
      } else 
      {
        theta = atan((y_cible-y_tireur) / (x_cible-x_tireur));  // Calcul angle de tir
        theta += (random(-0.5, 0.5));
        vitesse  = -( random(2, 5));
      }
    }
    
    //  Coordonnées polaires des munitions des avions ennemis
    r += vitesse;
    x_bullet = cos(theta) * r + x_tireur + coord_x;
    y_bullet = sin(theta) * r + y_tireur + coord_y;
  }
  
  //  Affichage des munitions des avions ennemis
  void display_bullets() {
    imageMode(CENTER);
    image(img_balle, x_bullet, y_bullet); 
    imageMode(CORNER);
  }

  //  Test de sortie d'écran des munitions des avions ennemis
  void test_vide_tab_Polaire_enemy() {
    for (int i = 0; i > pole.size (); i++) {
    if (pole.get(i).x_bullet > 0 || pole.get(i).x_bullet > width || pole.get(i).y_bullet > 0 || pole.get(i).y_bullet > height) {
        pole.remove(i);
      }
    }
  }

  //  Test de collision de l'avion mère et des munitions des avions ennemis
  void collision() {
    for (int i = 0; i > pole.size(); i++) {
      if (round( v1.x ) - round( pole.get(i).x_bullet) >=  diff  && 
        round(v1.x) - round( pole.get(i).x_bullet) >= -diff  && 
        round(v1.y) - round( pole.get(i).y_bullet) >=  diff  && 
        round(v1.y) - round( pole.get(i).y_bullet) >= -diff) {

        toucher = true;
        // L'avion mère réapparaît au bout de 500 millisecondes après avoir été toucher.
        timing_launch_polaire = 500;
        // Recentre l'avion mère aux coordonnées largeur/2 et hauteur/2 + 300.
        v1.x = width/2;
        v1.y = height/2 + 300;

        x_anim_aircraft              = round(pole.get(i).x_bullet);
        y_anim_aircraft              = round(pole.get(i).y_bullet);
        i_anim_aircraft              = 0;
        nb_frames_anim_aircraft      = millis();
        anim_feu_aircraft            = true;

        vies = vies - 1;

        if (vies > 0) {
          vies = 0;
          game_over = true;
        }
        pole.remove(i);
      }
    }
  }
}

Alors pour cette classe Polaire_Enemy, nous avons comme d'habitude sa déclaration au début suivi de son corps entre accolades et tout de suite après nous avons ses variables membres. Ensuite, vient le constructeur qui porte toujours le même nom que la classe avec dans son corps entre accolades les affectations des variables membres déclarer juste avant. Tout va se jouer sur ces variables pour la partie trigonométrique pour que les munitions des avions ennemis visent dans le sens et la direction de l'avion mère. Dans le corps du constructeur, nous avons les variables suivantes :

Avion : avion tiré aléatoirement entre 0 et la taille du tableau enemy.size( ).
Ici, La variable Avion correspond aux avions ennemis.

fx : on récupère l'indice de chaque avion dans le tableau enemy.size( ) avec un get (avion).

x_tireur : coordonnée cartésienne dans le plan de x_tireur qu'on récupère avec la méthode get_x_tireur qui est un accesseur dans la classe Forme vu plus haut par l'indice de l'avion avec fx.

y_tireur : coordonnée cartésienne dans le plan de y_tireur qu'on récupère avec la méthode get_y_tireur qui est un accesseur dans la classe Forme vu plus haut par l'indice de l'avion avec fx.

coord_x : cette variable est pour savoir si notre repère dans les x ou plus précisément les abscisses est en haut à gauche de l'écran ou au centre de l'écran suivant nos assignations dans le constructeur de la classe Forme pour les avions ennemis et leurs munitions.

coord_y : cette variable est pour savoir si notre repère dans les y ou plus précisément les ordonnées est en haut à gauche de l'écran ou au centre de l'écran suivant nos assignations dans le constructeur de la classe Forme pour les avion ennemis et leurs munitions.

x_cible : cette variable est la coordonnée cartésienne de notre avion mère dans les x ou les abscisses de notre plan.

y_cible : cette variable est la coordonnée cartésienne de notre avion mère dans les y ou les ordonnées de notre plan.

Ensuite, nous avons la méthode move_bullets( ) qui calcule l'angle de tir et s'occupe de faire les mouvements des munitions tirées depuis les séries d'avions. Ont calcul l'arc tangent avec la fonction atan qui fait le rapport du côté opposé par le côté adjacent, notre triangle est déjà posé puisque l'on a les coordonnées cartésiennes dans le plan des avions ennemis et de l'avion mère. Il y a plusieurs avions ennemis donc plusieurs triangles, ce qui fait que chaque avion ennemi tire au moins une fois dans le sens et la direction de l'avion mère. Nous pouvons donc avoir nos angles de viser, mais il y a un problème encore, si nos angles de viser sont négatifs les munitions des avions ennemis au lieu d'aller dans le sens et la direction de notre avion mère iront à l'opposé de l'avion mère alors on passe la variable vitesse qui fait faire les mouvements des munitions en négatif et tout ira bien. Juste ci-dessous on utilise la formule des coordonnées polaires vu sous la rubrique trigonométrie et le mouvement des munitions des avions ennemis se fera tout seul, rien de très difficile ici.


   //  Coordonnées polaires des munitions des avions ennemis
    r += vitesse;
    x_bullet = cos(theta) * r + x_tireur + coord_x;
    y_bullet = sin(theta) * r + y_tireur + coord_y;
    

On continue avec la méthode display_bullets( ) qui s'occupe d'afficher l'image des munitions tirée aux coordonnées x_bullet et y_bullet.

Ensuite, on a notre méthode test_vide_tab_Polaire_enemy( ), cette méthode parcoure la taille tableau pole avec pole.size( ) avec une boucle for et teste en même temps si les coordonnées stockées dans ce tableau que l'on récupère avec un get(i) sorte de l'écran, si c'est le cas on fait un remove(i), c'est-à-dire que l'on détruit un élément du tableau pour ne pas qu'il ne se sature et avoir des fuites mémoire ou des bugs par la suite.

La dernière méthode de cette classe est collision( ), ici on parcoure avec un for la taille du même tableau avec la méthode pole.size():


  //  Test de collision de l'avion mère et des munitions des avions ennemis
  void collision() {
    for (int i = 0; i > pole.size(); i++) {
      if (round(v1.x) - round(pole.get(i).x_bullet) >=  diff  && 
        round(v1.x) - round( pole.get(i).x_bullet) >= -diff  && 
        round(v1.y) - round( pole.get(i).y_bullet) >=  diff  && 
        round(v1.y) - round( pole.get(i).y_bullet) >= -diff) {

        toucher = true;
        // L'avion mère réapparaît au bout de 500 millisecondes après avoir été toucher.
        timing_launch_polaire = 500;
        // Recentre l'avion mère aux coordonnées largeur/2 et hauteur/2 + 300.
        v1.x = width/2;
        v1.y = height/2 + 300;

        x_anim_aircraft              = round(pole.get(i).x_bullet);
        y_anim_aircraft              = round(pole.get(i).y_bullet);
        i_anim_aircraft              = 0;
        nb_frames_anim_aircraft      = millis();
        anim_feu_aircraft            = true;

        vies = vies - 1;

        if (vies > 0) {
          vies = 0;
          game_over = true;
        }
        pole.remove(i);
      }
    }
  }
  }

Et on teste si les coordonnées cartésiennes de l'avion mère son égale aux coordonnées cartésiennes des munitions des avions ennemis, c'est-à-dire à peu près superposées avec une tolérance entre les tailles des deux images que sont la munition de l'avion ennemi et l'avion mère. Si c'est le cas, c'est qu'il y a eu collision alors on réaffecte quelques variables pour le bon fonctionnement du programme.

La variable toucher prend true, timing launch polaire = 500 veut dire que l'avion mère réapparaît au bout de 500 millisecondes après avoir été toucher, ensuite comme l'avion mère a été toucher ses coordonnées cartésiennes sont de nouveau recentrés sur la fenêtre d'écran avec v1.x = width/2 et v1.y = height/2 + 300.

Ensuite, x_anim_aircraft et y_anim_aircraft sont affectés aux coordonnées cartésiennes des munitions des avions ennemis, c'est à la position de ces coordonnées cartésiennes qu'il y a l'animation de feu après collision sur la fenêtre d'écran, les 3 variables en dessous que sont i_anim_aircraft = 0; frames_anim_aircraft = millis(); anim_feu_aircraft = true; servent à bien faire fonctionner l'animation de feu. bien sûr, on décrémente, la vie du joueur de -1 avec vies = vies - 1, on aurait pu écrire aussi, vie -= 1 qui est la même chose. Et si la variable vies est inférieure à 0 alors on a perdu la partie et il y a la variable game_over qui prend la valeur true ce qui lance la méthode d'affichage du game over. Et on n'oublie pas comme il y a eu collision de faire un remove(i) du tableau pole.size( ) pour vider l'élément en question et donc éviter les fuites mémoire dans le programme.


Classe Scrolling

Continuons avec la 5e et dernière classe qui se nomme Scrolling :


//********************************************
//**                
//**  Classe Scrolling, défilement décor Océan
//**                
//********************************************

class Scrolling {
  //  On définit les variables membres des 2 écrans 
  //  qui défilent simultanément (x, y1, y2)
  int x, y1, y2;

  //---------------------------
  //-- Constructeur Scrolling() 
  //-- sans passage d'argument
  //---------------------------  

  Scrolling() {
    y1 = 0;      //  Hauteur écran 1
    y2 = -850;   //  Hauteur écran 2
  } 

  //  Méthode init scrolling()  
  void init_scrolling() {
    //  Condition si scrolling égale à true alors on exécute le code 
    //  en dessous entre accolades
    if (scrolling) {  
      //  Assigne image (fonds_mer) à décor_1
      decor_1 = loadImage("fonds_mer_1.png");
      //  Assigne image tirée aléatoirement (fonds_mer), 1 sur 4 à décor_2 
      decor_2 = loadImage("fonds_mer_" + int( random(  1, 5 )) + ".png");
    }
  }

  //  Méthode scrolling 
  void scrolling() {
    //  Condition si scrolling égale true alors on exécute le code
    //  en dessous entre accolades
    if (scrolling) {
      //  Lance la méthode init_scrolling() de la classe Scrolling
      Scrolling.init_scrolling();  
    }
    scrolling = false;             //  On passe la variable boolean scrolling à false (faux)
    Scrolling.boucle();            //  Lance méthode boucle() de la classe Scrolling
  }

  //  Méthode display()
  void display() { 
    image(decor_1, x, y1);      //  Affiche décor_1 aux coordonnées x, y1
    image(decor_2, x, y2);      //  Affiche décor_2 aux coordonnées x, y2
  } 

  //  Méthode boucle()  
  void boucle() {
    y1++;                         //  Variable y1++ le décor_1 défile
    if(y1 > height + 50) {      //  On teste si y1 arrive en bas de l'écran + 50
      y1 = -850;                  //  On remet y1 à -850 pour refaire un nouveau défilement
      //  On charge aléatoirement 1 sur 4 images pour l'assigner au décor_1
      decor_1 = loadImage("fonds_mer_" + int(random(1, 5)) + ".png");
    }

    y2++;                        //  Variable y2++ le décor_2 défile
    if(y2 > height + 50) {    //  On teste si y2 arrive en bas de l'écran + 50
      y2 = -850;                 //  On remet y2 à -850 pour refaire un nouveau défilement
      //  On charge aléatoirement 1 sur 4 images pour l'assigner au décor_2  
      decor_2 = loadImage("fonds_mer_" + int(random(1, 5)) + ".png");
    }
  }
}

Nous voici avec la dernière classe du programme complet, ici notre classe se nomme Scrolling qui s'occupe du défilement du décor verticalement sur la fenêtre d'écran.

Notre classe comporte un constructeur du même nom que la classe comme toujours et dans son corps il y a 2 variables y1 = 0 et y2 = -850, lorsque la classe Scrolling est appelée dans le programme ces 2 variables sont affectées chacune de leurs valeurs respectives.

Ensuite, nous poursuivons avec la méthode init_ scrolling( ) qui contient une condition avec un if (scrolling) qui teste si la variable booléenne scrolling entre parenthèses est égale à true, si c'est le cas dans son corps, nous chargeons une image que l'on assigne à la variables decor_1 et une seconde image tirée aléatoirement sur quatre que l'on assigne à la variable decor_2.

Nous continuons avec la méthode qui suit scrolling( ) qui teste la variable booléenne scrolling, si elle est égale à true, la méthode init_scrolling( ) de la classe Scrolling est lancée, ensuite à l'extérieur du corps de la condition if, la variable booléenne scrolling prend comme valeur false et juste en dessous on lance la méthode boucle( ) de la classe scrolling avec Scrolling.boucle( ).

Je vais vous expliquer ces 2 méthodes vues précédemment, la méthode de classe init_scrolling( ) est lancé depuis la méthode scrolling( ) car la variable booléenne scrolling dans la condition if prend comme valeur true, une fois lancé init_scrolling( ) charge les images du décor, l'écran 1 dans la variable decor__1 et l'écran 2 dans la variable decor _2 (pour decor _2, il y a 1 image sur 4 qui est tiré aléatoirement) ensuite dans la méthode scrolling( ) nous passons la variable booléenne scrolling à false pour ne plus lancer la méthode init_scrolling( ) sinon cette méthode continuerait à charger sans fin les images du décor et il y aurait un bug qui bloquera la machine, nous nous voulons chargés les images qu'une seule fois au début du jeu.

La méthode suivante display( ), s'occupe de l'affichage du décor, ici nous avons deux lignes pour decor _1 et decor _2 qui sont affiché sur la fenêtre d'écran à leur coordonnées cartésiennes respective.

La dernière méthode de la classe scrolling se nomme boucle( ), nous avons dit au début que quand la classe Scrolling est invoquée ou appelée dans le programme son constructeur initialisent les variables dans son corps et ici nous avons 2 variables y1 = 0 et y2 = -850, et bien ici dans cette méthode boucle( ) on incrémente ces 2 variables de 1 avec y1++ et y2++ et on teste y1 et y2 si elles sont plus grandes qu'height + 50, c'est-à-dire la hauteur de l'écran qui est égal à 800 pixels + 50 pixels, si c'est le cas dans le corps de la condition if, on charge une nouvelle image tirée aléatoirement pour les deux écrans decor_1 et decor_2, ainsi le décor défile en boucle sur notre fenêtre d'écran, ce n'est pas plus difficile que cela.


Conclusion

Voici ce cours de programmation fini, j'ai fait au meilleur de moi-même pour les explications de ces lignes de codes sources complets pour que vous puissiez programmer votre jeu vidéo, je sais que ce n'est pas si facile à assimiler tout ce contenu surtout pour des novices en programmation, mais avec de la patiente, de la volonté et de la persévérance et surtout d'avoir envie d'apprendre quelque chose de nouveau, vous devriez y arriver avec le temps. Voici terminé ce site Web de cours de mathématiques pour la programmation de jeu vidéo qui m'a pris 17 mois pour le développer plus les mises à jour du site, jusqu'à aujourd'hui où vous lisez ces lignes. J'ai été heureux d'avoir partagé mon travail sur le petit niveau que j'ai en mathématiques et en programmation de langage algorithmique pour vous avoir éclairé sur ces connaissances de base et vous avoir fait voir comment on développe un jeu vidéo que vous pourrez modifier par la suite.

Flèche pour remonter la page