Créons nos propres types de variables en VBA [#11 FORMATION EXCEL VBA COMPLETE]
Aujourd’hui, nous nous retrouvons pour un nouveau chapitre de la formation sur l'apprentissage de VBA pour les débutants, nous allons découvrir un nouveau type de variable très pratique à utiliser dans le cadre du développement de macros commandes. En effet, il s'agit d'un type de variable que nous allons pouvoir totalement personnaliser afin de pouvoir structurer notre code comme bon nous semble. Et pour rendre cette découverte ludique, ce tutoriel sera l'occasion de mettre en place un mini-jeu de course automobile.
Téléchargement
Vous pouvez télécharger le fichier d'exemple de cet article en cliquant sur le lien suivant :
Tutoriel Vidéo
Partie 1 :
Partie 2 :
Partie 3 :
Vous trouverez très régulièrement de nouvelles vidéos sur la chaîne, alors pensez à vous abonner pour ne manquer aucune astuce et devenir rapidement un pro d’Excel (cliquez-ici) !
1. Présentation
Durant l’un des chapitres précédents de la formation sur l’apprentissage du développement en VBA, et que vous avez pu découvrir sur le blog il y a quelques semaines de cela, nous avions eu l’occasion d’approfondir une des notions de la base de la programmation sur VBA, il s’agit en effet de la notion d’objets.
Nous avions alors vu que l’objet principal mis à notre disposition par les développeur de chez Microsoft est l’objet Application, lequel permet de manipuler une grande partie des informations présentées au sein de nos feuilles de calculs (gestion des classeurs, feuilles, cellules,…).
À présent, je vous propose de découvrir comment créer nos propres variables constituées de sous-variables.
Cette notion très puissante du langage VBA, nous permettra une fois ce chapitre terminé d’avoir à disposition un outil très pratique pour manipuler des variables, comme s’il s’agissait d’objets VBA ! Sans toutefois nous retrouver confronter à toutes les difficultés amenées par celles-ci.
Comme vous pourrez le constater tout au long de ce tutoriel, nous allons ici aborder un sujet un petit peu plus technique que ce que nous avions l’habitude de voir jusqu’à maintenant dans cette formation.
Néanmoins, pas de panique, nous allons prendre notre temps et détailler chaque point dans le détail.
Pour simplifier la compréhension, ce chapitre sera découpé en deux parties, dont nous découvrirons la suite la semaine prochaine !
Pour cela, revenons sur l’exemple que nous avons déjà vu dans le chapitre précédent, à savoir que nous souhaitons mettre en place une simulation de course automobile (carrément !).
En effet, pour rendre ce chapitre ludique nous allons mettre en place un jeu qui sera pleinement jouable (bon ok, ce n’est pas du Forza, mais vous pourrez toujours vous venter de l’avoir développé vous-même ;))
Pour cela, nous allons avoir trois voitures qui vont se disputer la victoire, chacune d’entre elles ayant ces propres caractéristiques (modèle, couleur,…).
2. Les variables simples
Pour initialiser les caractéristiques de la première voiture, nous pourrions imaginer utiliser des variables simples telles que :
Sub mesVoitures()
Dim marque As String
marque = "Ferrari"
Dim modele As String
modele = "F355"
Dim couleur As String
couleur = "Rouge"
Dim annee As Integer
annee = 1995
Dim vmax As Integer
vmax = 290
End Sub
Ensuite, nous arrivons à l’étape suivante qui est celle de l’initialisation de la seconde voiture, et là les choses commencent à se compliquer !
Une des solutions consisterait en effet à recréer des variables pour chaque véhicule, comme par exemple :
Sub mesVoitures()
Dim marque1 As String, marque2 As String, marque3 As String
marque1 = "Ferrari"
marque2 = "Ford"
marque3 = "Porsche"
End Sub
Mais créer autant de variables va très vite devenir laborieux, et complexe à utiliser.
3. Les tableau de VBA
Nous pourrions alors utiliser des tableaux (array) :
Sub mesVoitures()
Dim marque(1 To 3) As String
marque(1) = "Ferrari"
marque(2) = "Ford"
marque(3) = "Porsche"
End Sub
Ici les choses sont bien mieux architecturées, et cela permet notamment de passer en revue tous les éléments du tableau en utilisant une simple boucle For :
Maintenant, si nous souhaitons définir une seconde caractéristique, telle que la couleur de la voiture, nous allons devoir créer une seconde variable (toujours utilisée en tant que tableau) :
Sub mesVoitures()
Dim marque(1 To 3) As String
marque(1) = "Ferrari"
marque(2) = "Ford"
marque(3) = "Porsche"
Dim couleur(1 To 3) As String
couleur (1) = "Rouge"
couleur (2) = "Blanche et bleue"
couleur (3) = "Jaune"
End Sub
Ou mieux encore utiliser un tableau à deux dimensions :
Sub mesVoitures()
Dim voiture(1 To 3, 1 To 2) As String
voiture(1, 1) = "Ferrari"
voiture (1, 2) = "Rouge"
voiture (2, 1) = "Ford"
voiture (2, 2) = "Blanche et bleue"
voiture (3, 1) = "Porsche"
voiture (3, 2) = "Jaune"
End Sub
Mais comme vous pouvez le constater les choses vont continuer de se complexifier sérieusement, et il devient difficile de s’y retrouver dans les différents paramètres des variables…
4. Les variables structurées
Enfin, la dernière option que nous allons voir ici consiste à utiliser des « types de variables définies par l’utilisateur », que nous appellerons plus volontiers des variables structurées.
Ce type de variable offre la possibilité de définir des sous-variables à une variable principale.
Ce n’est toujours pas très clair ?
En d’autres termes, nous créons notre propre type de variable, qui va porter le nom de tVoiture.
Pour déclarer un type de variable personnalisé, nous procédons de la manière suivante (directement dans le module) :
Type tVoiture
marque As String
modele As String
couleur As String
annee As Integer
vmax As Integer
End Type
Pour résumer, un « types de variables définies par l’utilisateur » se déclare en utilisant le mot-clé Type, suivi du nom que nous souhaitons attribuer au type de variable.
Ainsi, le type tVoiture est une sorte de container composé d’un ensemble de sous-variables dans lesquelles nous retrouverons les caractéristiques de chaque voiture.
Ensuite, il suffit de déclarer de nouvelles variables reprenant ce type :
Dim voitureJoueur1 As tVoiture
Puis, nous pourrons ensuite affecter des valeurs à l’ensemble des paramètres de ces nouvelles variables, en appelant les sous-variables en utilisant le point de séparation, exactement comme nous le ferions avec des objets VBA :
Sub courseAutomobile()
Dim voitureJoueur1 As tVoiture
voitureJoueur1.marque = "Ferrari"
voitureJoueur1.modele = "F355"
voitureJoueur1.couleur = "Rouge"
voitureJoueur1.annee = 1995
voitureJoueur1.vmax = 290
Dim voitureJoueur2 As tVoiture
voitureJoueur2.marque = "Ford"
voitureJoueur2.modele = "Mustang"
voitureJoueur2.couleur = "Blanche et bleue"
voitureJoueur2.annee = 1964
voitureJoueur2.vmax = 190
Dim voitureJoueur3 As tVoiture
voitureJoueur3.marque = "Porsche"
voitureJoueur3.modele = "911 Type 997 Carrera S"
voitureJoueur3.couleur = "Jaune"
voitureJoueur3.annee = 2008
voitureJoueur3.vmax = 293
End Sub
Attention toutefois, bien que les variables structurées que nous venons d’utiliser ressemblent dans leur fonctionnement fortement aux objets que nous avions découverts la semaine dernière (notamment par l’utilisation du point de séparation (« . ») entre la variable et la sous-variable), en réalités, il ne s’agit ici que de variables boostées !
En effet, les objets sont bien plus puissants, et permettent de réaliser bien plus de choses, mais en contrepartie, ces derniers sont beaucoup plus complexes à utiliser !
Ils nécessitent entre autres chose d’être déclarés dans des modules spécifiques (appelés Modules de classe), ce que nous verrons bien plus tard dans cette formation !
Mais revenons-en à notre course automobile.
Pour déterminer quel bolide arrivera en tête de la course, nous devons également tenir compte du pilote.
Aussi, nous pouvons créer un nouveau type de variable structuré que nous allons appeler tPilote et qui nous permettra d’enregistrer les informations concernant les pilotes ::
Type tPilote
nom As String
age As Integer
forme As Integer
End Type
Puis, nous allons définir que cette variable est en réalité une sous-variable de tVoiture !
Type tVoiture
…
pilote As tPilote
End Type
Ce qui va alors nous permettre de définir les caractéristiques du pilote, comme étant un sous-sous-paramètre de chaque voiture :
Sub courseAutomobile()
Dim voitureJoueur1 As tVoiture
voitureJoueur1.marque = "Ferrari"
voitureJoueur1.modele = "F355"
voitureJoueur1.couleur = "Rouge"
voitureJoueur1.annee = 1995
voitureJoueur1.vmax = 290
voitureJoueur1.pilote.nom = "Tintin"
voitureJoueur1.pilote.age = 23
voitureJoueur1.pilote.forme = 81
Dim voitureJoueur2 As tVoiture
voitureJoueur2.marque = "Ford"
voitureJoueur2.modele = "Mustang"
voitureJoueur2.couleur = "Blanche et bleue"
voitureJoueur2.annee = 1964
voitureJoueur2.vmax = 190
voitureJoueur2.pilote.nom = "Milou"
voitureJoueur2.pilote.age = 17
voitureJoueur2.pilote.forme = 68
Dim voitureJoueur3 As tVoiture
voitureJoueur3.marque = "Porsche"
voitureJoueur3.modele = "911 Type 997 Carrera S"
voitureJoueur3.couleur = "Jaune"
voitureJoueur3.annee = 2008
voitureJoueur3.vmax = 293
voitureJoueur3.pilote.nom = "Haddock"
voitureJoueur3.pilote.age = 52
voitureJoueur3.pilote.forme = 59
End Sub
5. Combiner le meilleur des deux mondes
Maintenant que nous venons découvrir les différentes méthodes qui permettent de gérer les différents paramètres des voitures, notamment en utilisant les tableaux VBA (array) ainsi que les variables structurées (« type défini par l’utilisateur » dans le langage d’Excel), voyons comment optimiser le fonctionnement en combinant les avantages de ces deux techniques avancées de programmation.
Pour cela, il suffit simplement de déclarer directement les trois voitures en tant que tableau, tout en utilisant le typage personnalisé que nous venons de créer (tVoiture) !
Nous remplaçons alors la ligne :
Dim voitureJoueur2 As tVoiture
Par :
Dim voiture(1 To 3) As tVoiture
Puis nous supprimons les déclarations des deux autres voitures qui sont maintenant devenues sans objet.
Bien entendu, à présent nous devons modifier les noms de variables afin qu’ils correspondent à la nouvelle déclaration.
Pour cela, la méthode la plus rapide consiste à utiliser la méthode de remplacement, accessible à partir du menu Edition > Remplacer (ou en utilisant la combinaison de touches [Ctrl]+[h]) :
Ensuite dans la fenêtre de remplacement, nous saisissons maintenant le terme que nous voulons remplacer (« voitureJoueur1 ») par le terme que nous souhaitons insérer dans le code (ici « voiture(1) »)
(Nous sélectionnons l’ensemble du module et nous cliquons sur le bouton Remplacer tout pour réaliser l’opération sur toutes les lignes du module en cours).
Cela nous permettra maintenant de pouvoir passer en revue toutes les voitures déclarées en utilisant une boucle for, exactement comme nous l’avions fait dans les parties précédentes :
Dim i As Integer
For i = 1 To 3
Debug.Print voiture.pilote.nom & " conduit la " & voiture.marque
Next
6. Codons le moteur du jeu
Et voilà, maintenant que les variables, les tableaux et les variables structurées n’ont plus aucun secret pour vous, nous en arrivons à la partie fun de ce chapitre : le développement du jeu !
Celui-ci va se faire en deux temps : tout d’abord nous allons développer le moteur du jeu, c’est-à-dire la manière dont celui-ci va fonctionner.
Puis dans un second temps, nous créerons l’interface avec la feuille de calcul, afin de pouvoir interagir avec l’utilisateur.
Pour commencer, nous allons créer une nouvelle variable que nous allons appeler pourcentage et qui va nous permettre de stocker l’évolution de chaque voiture sur la piste.
Le point de départ est à 1%, la première voiture à 100% remporte la course !
Nous déclarons cette variable en tant que sous-variable de la variable structurée principale (tVoiture) :
Type tVoiture
…
pourcentage As Integer
End Type
Puis, nous affectons une valeur initiale pour chacune des voitures :
voiture(1).pourcentage = 1
voiture(2).pourcentage = 1
voiture(3).pourcentage = 1
Ensuite, nous instancions une seconde variable que nous appelons vitesse, laquelle va (comme son nom l’indique !) nous permettre d’enregistrer la vitesse de croisière des voitures et qui va commencer à 0km/h, lorsque les voitures seront à l’arrêt.
Ensuite, nous allons mettre en place une boucle While qui va permettre de répéter les opérations tant qu’aucune voiture n’a atteint les 100%.
Pour cela, nous créons ainsi une troisième variable qui va nous permettre de stocker le numéro de la voiture victorieuse (nous la nommons pour ce faire « gagnant »).
Tant qu’aucune voiture n’a atteint les 100%, cette variable va avoir pour valeur 0 (et donc la boucle peut se poursuivre) :
Dim gagnant As Integer
gagnant = 0
Do While gagnant = 0
Dim i As Integer
For i = 1 To 3
Debug.Print voiture.pilote.nom & " conduit la " & voiture.marque
Next
Loop
Nous encapsulons la boucle utilisée juste avant dans cette boucle While.
Attention de ne surtout pas lancer la macro à ce moment-là, sous peine de générer une boucle infinie et de faire planter le fichier !
En effet, étant donné que nous n’intervenons par encore pour modifier la valeur de gagant, celle-ci sera toujours égale à 0, et donc la boucle ne s’interrompra jamais !
Maintenant, nous allons pouvoir faire avancer les voitures !
Tout d’abord, nous allons augmenter la vitesse de chaque voiture :
Do While gagnant = 0
Dim i As Integer
For i = 1 To 3
voiture(i).vitesse = voiture(i).vitesse + (Rnd * 10 + voiture(i). pilote.forme / 20)
If voiture(i).vitesse > voiture(i).vMax Then voiture(i).vitesse = voiture(i).vMax
Next
Loop
Comme vous pouvez le constater, pour incrémenter la vitesse nous procédons en plusieurs étapes :
- Nous ajoutons à la vitesse en cours la valeur retournée par Rnd, qui permet de récupérer un nombre décimal compris entre 0 et 1,
- Ce nombre, nous le multiplions par 10 afin de pouvoir l’utiliser dans une variable typée en tant que Integer
- Ensuite, nous ajoutons une portion de la forme du pilote,
- Et enfin, sur la page suivante, nous vérifions que la vitesse calculée ne dépasse jamais de la vitesse maximale permise par la voiture
Une fois que nous avons défini la vitesse de croisière de chaque voiture, nous pouvons ajouter celle-ci au pourcentage de la course réalisé par chaque voiture :
voiture(i).pourcentage = voiture(i).pourcentage + voiture(i).vitesse / 10
Et enfin, nous vérifions si une voiture à un score au moins égal à 100% :
If voiture(i).pourcentage >= 100 Then
voiture(i).pourcentage = 100
gagnant = i
End If
Pour simplifier nous limitons la valeur maximale du pourcentage à 100% et enfin, nous enregistrons le numéro de la voiture dans la variable gagnant.
En inscrivant une valeur différente de zéro dans la variable gagnant, nous sortons de la boucle, ce qui marque la fin de la partie, nous pouvons alors afficher le message au joueur :
Debug.Print voiture(gagnant).pilote.nom & " a gagné la course !"
À ce moment-là, nous pouvons tester directement l’application afin de voir qui va remporter la course
Si vous avez défini les mêmes paramètres que moi et que vous vous amusez à tester plusieurs fois de suite la procédure, vous devriez vous rendre compte que Tintin risque le plus souvent de remporter la course :
Cela vient du fait que les paramètres définis le rendent bien plus fort que les concurrents.
En effet, nous seulement la Ferrari est presque aussi rapide que la Porsche, mais en plus Tintin à une forme bien meilleure que celle des deux autres concurrents !
Pour équilibrer les choses, nous pouvons par exemple diminuer cette force à 70, ce qui aura pour effet de rendre le résultat de la course plus équilibré !
Et voilà, maintenant que notre jeu est fonctionnel, donnons-nous rendez-vous la semaine prochaine pour la troisième et dernière partie de ce chapitre dans lequel nous créerons l’interface avec la feuille de calcul, afin de pouvoir interagir avec l’utilisateur.
7. Réaliser l’interface du jeu
Maintenant que le jeu est pleinement fonctionnel, il ne reste plus maintenant qu’à réaliser l’interface de celui-ci directement dans la feuille de calcul !
Pour cela, revenons sur celle-ci
Puis, commençons par sélectionner 100 colonnes (qui vont évidemment correspondre aux 100 pour cents que chaque voiture va devoir réaliser pour terminer sa course) :
Cela étant fait, nous allons les redimensionner afin de les faire apparaitre sur une seule largeur d’écran :
Une largeur de 12 pixels devrait suffire !
Ensuite nous sélectionnons une plage de 7 lignes par 100 colonnes :
À ces cellules nous donnons une couleur qui va correspondre à la couleur de la piste (par exemple du gris :
Puis nous dessinons les trois pistes d’une autre couleur (ici du gris aussi, mais plus clair) :
Ensuite, nous redessinons chacune des trois pistes afin de donner un nom aux plages de cellules correspondantes.
Pour pouvoir agir rapidement sur ces pistes, nous allons les nommer en saisissant le nom dans la zone dédiée située à gauche de la barre des formules (au-dessus de la feuille de calculs).
Nous les appelons piste1, piste2 et piste3.
Cela étant nous fait nous pouvons revenir dans le code afin de mettre en scène le jeu sur la feuille de calcul.
La première chose à faire est d’identifier les coordonnées des cellules qui correspondent à chacune des pistes, en ajoutant une nouvelle sous-variable pour chaque voiture.
Cette variable que nous appelons « piste » va permettre de stocker les noms de plages de cellules que nous venons de définir à l’instant :
Type tVoiture
…
piste As String
End Type
Puis affecter les noms lors de l’initialisation des voitures :
voiture(1).piste = "piste1"
voiture(2).piste = "piste2"
voiture(3).piste = "piste3"
Ensuite, il ne reste plus qu’à afficher un repère qui puisse permettre de visualiser la position de la voiture sur la piste.
Pour cela, revenons dans la boucle principale, là où nous avions calculé le pourcentage de chaque voiture, et ici nous allons simplement afficher un « o » sur la n-ième cellule de la piste qui correspond au pourcentage :
range(voiture(i).piste).Cells(voiture(i).pourcentage) = "o"
Nous partons ici de la plage de cellules qui correspond à la sous-variable piste que nous venons de déclarer juste avant, puis nous récupérons la cellule située à la position représentée par la variable pourcentage.
C’est d’ailleurs la raison pour laquelle les piste mesurent 100 cellules de long !
Avant de pouvoir lancer la procédure, nous allons créer un bouton sur la feuille de calcul (menu Insertion > Zone de texte)
Ensuite, après avoir dessiné la forme du bouton et saisi un texte d’explication de la fonction du bouton, effectuez un clic-droit > Affecter une macro
Dans la fenêtre qui s’affiche, choisissez la macro que nous venons de développer « mesVoitures » et validez en appuyant sur le bouton [OK] :
(vous constaterez au passage que le nombre de macros insérées dans le fichier de travail commence à être très important !)
À présent, il suffit d’appuyer sur ce bouton pour voir la course de dérouler sous vos yeux !
Sur cette illustration, la course le résultat a été serrée, mais Haddock en sort gagnant !
En revanche, pour illustrer l’évolution de la course, nous préfèrerons n’avoir qu’un seul symbole pour représenter les pilotes.
Pour cela, il suffit de supprimer le contenu des pistes avant d’afficher ce symbole en insérant simplement la ligne suivante (la méthode ClearContents permet d’effacer le contenu des cellules) :
Range(voiture(i).piste).ClearContents
Range(voiture(i).piste).Cells(voiture(i).pourcentage) = "o"
Peut-être avez remarqué que l’avancée des voitures ne s’actualise pas en temps réel ?
Pour corriger ce petit problème, il suffit d’appeler la méthode « DoEvents » pour permet de forcer la mise à jour de la feuille de calcul pendant le déroulement de la macro (à insérer à la toute fin de la boucle, juste avant le Loop) :
Do While gagnant = 0
…
DoEvents
Loop
Maintenant il ne reste plus qu’à rendre la partie un peu plus interactive, en permettant par exemple au joueur de réaliser un pari sur le vainqueur.
Nous allons simplement demander au joueur de sélectionner le nom du pilote qui va remporter la course et nous contrôlerons ensuite si celui-ci a vu juste !
Pour cela, nous utilisons la méthode MsgBox qui permet d’afficher un message à l’utilisateur, à laquelle nous allons adjoindre la valeur vbYesNo, qui permet d’afficher les boutons de réponse « Oui » et « Non » :
If MsgBox("Avez-vous sélectionné le nom d'un pilote", vbYesNo) = vbYes Then
Dim i As Integer
Do While gagnant = 0
…
Loop
Debug.Print voiture(gagnant).pilote.nom & " a gagné la course !"
End If
Nous insérons cette ligne juste avant la boucle Do While de manière à ne lancer le jeu que lorsque le joueur confirme avoir sélectionné un des pilotes.
Ensuite, nous allons remplacer le Debug.Print qui nous permet d’afficher le nom du vainqueur par un MsgBox afin de l’afficher à l’utilisateur.
MsgBox voiture(gagnant).pilote.nom & " a gagné la course !"
Et Pour finir nous allons maintenant pouvoir informer le joueur que celui-ci a vu juste, ou au contraire de son erreur :
MsgBox voiture(gagnant).pilote.nom & " a gagné la course !"
If voiture(gagnant).pilote.nom = ActiveCell.Value Then
MsgBox "Bravo, vous avez gagné !"
Else
MsgBox "Manqué !"
End If
La dernière petite amélioration que nous allons mettre en place va consister à modifier la couleur de fond des cellules sur lesquelles se trouve les bolides, en utilisant une mise en forme conditionnelle.
Pour cela, sélectionnons la première piste (en utilisant le nom que lui avons affecté un peu plus tôt : piste1) :
Puis rendons-nous dans le menu Accueil > Mise en forme conditionnelle > Nouvelle règle… :
Ensuite, nous sélectionnons le type de règle « Appliquer une mise en forme uniquement aux cellules qui contiennent », dans le menu déroulant, nous sélectionnons « égale à » et dans la zone de texte nous saisissons la lettre « o » :
Ensuite, nous cliquons sur le bouton [Format] afin de personnaliser le format à appliquer.
Dans cette fenêtre, nous choisissons une couleur de Police et de remplissage identique à la couleur que nous avons définie comme propriété de la voiture !
Nous répétons l’opération pour les deux autres voitures en course :
Et voilà, le moteur en lui-même est maintenant terminé, il ne restera plus qu’à nous retrouver la semaine prochaine pour mettre en scène le jeu sur la feuille de calcul !