Date de publication : 24/04/08 , Date de mise à jour : 24/04/08
Par
Thierry GASPERMENT (arkham46.developpez.com/)
Comment développer un jeu en VBA avec Gdi+.
Permière partie : un jeu de tir.
I. Introduction
II. Spécifications fonctionnelles
III. Spécifications techniques simplifiées
IV. Création du formulaire
V. Création du module de classe clGdiplus
VI. Déclaration et initialisation de la classe clGdiplus
VII. Dessin du fond étoilé
VII-A. Création de l'image Gdi+
VII-B. Dessin des étoiles
VII-C. Affichage à l'écran
VIII. La minuterie
VIII-A. Activation de l'événement "Sur Minuterie"
VIII-B. Choix de l'intervalle de minuterie, la théorie.
VIII-C. Choix de l'intervalle de minuterie, la pratique.
IX. Création, affichage et déplacement du vaisseau
IX-A. Création d'un module de classe
IX-B. Déclaration et initialisation de l'objet dans le formulaire
IX-C. Chargement et affichage de l'image du vaisseau
IX-D. Gestion de la transparence
IX-E. Gestion des déplacements
IX-F. Réinitialisation de l'image à chaque itération
X. Création, affichage et déplacement des ennemis
X-A. Création d'un module de classe
X-B. Déclaration et initialisation de l'objet dans le formulaire
X-C. Création des ennemis
X-D. Chargement de l'image des ennemis
X-E. Affichage et déplacement des ennemis
XI. Création, affichage et déplacement des missiles
XI-A. Création d'un module de classe
XI-B. Déclaration et initialisation de l'objet dans le formulaire
XI-C. Création des missiles
XI-D. Affichage et déplacement des missiles
XII. Gestion des collisions
XII-A. Création d'une région pour le vaisseau du joueur
XII-B. Création d'une région pour chaque ennemi
XII-C. Création d'une région pour chaque missile
XII-D. Test de collision entre le vaisseau et les ennemis
XII-E. Test de collision entre les missiles et les ennemis
XIII. Le son
XIV. Conclusion
XV. Téléchargements
I. Introduction
L'objectif de ce tutoriel est de développer un jeu de tir avec Access.
Pour gérer l'affichage on utilisera une classe spécifique clGdiPlus qui s'appuie sur la librairie Gdi+ de Microsoft.
Voici le résultat en image :
II. Spécifications fonctionnelles
Avant toute chose, nous allons écrire les spécifications fonctionnelles du jeu.
- Un écran de jeu fenêtré (pas en plein écran) pour obtenir une fluidité acceptable
- Le fond d'image est un ciel étoilé statique
- Un vaisseau est dirigé par le joueur sur les deux axes (horizontal et vertical) avec les flèches du clavier
- Le tir du vaisseau du joueur est déclenché avec la touche espace du clavier
- Le tir du vaisseau du joueur est vertical
- Les ennemis apparaissent en haut de l'image et se déplacent vers le bas à une vitesse aléatoire
- Le score est affiché à côté de l'image
- Lorsque le vaisseau du joueur entre en collision avec un ennemi, le jeu s'arrête
III. Spécifications techniques simplifiées
Ci-dessous et en bref, les techniques de base utilisées pour le développement.
Tout sera plus détaillé dans les chapitres suivants.
- Utilisation du timer du formulaire : l'image est mise à jour à chaque événement de ce timer.
- Utilisation de Gdi+ pour le graphisme, avec l'aide de la classe clGdiplus.
- Création d'un module de classe pour chaque famille d'objet (vaisseau, tir, ennemis, ...).
- Utilisation d'une collection pour les objets multiples (tir, ennemis).
- Le vaisseau du joueur et les ennemis sont des images stockées à l'extérieur de la base de données.
- Les tirs du vaisseau seront dessinés à base de rectangle.
IV. Création du formulaire
On crée un formulaire dans lequel on place :
- Un contrôle image nommé Img pour l'affichage du jeu
- Une zone de texte TxtScore pour l'affichage du score
La zone de texte est utilisée en affichage uniquement, donc on défini ses propriétés :
- Activé => Non
- Vérrouillé => Oui
Le formulaire n'est pas utilisé pour afficher des données, donc on peut supprimer les éléments inutiles dans ses propriétés :
- Afficher sélecteur => Non
- Boutons de déplacement => Non
- Diviseur d'enregistrement => Non
V. Création du module de classe clGdiplus
Téléchargez la classe au format texte
(HTTP)
Dans l'éditeur VBA :
1 - Créez un nouveau module de classe (dans le menu :
insertion ->
module de classe).
2 - Collez-y le contenu du fichier texte.
3 - Sauvegardez le module avec le nom
clGdiPlus.
4 - Compiler (
Débogage -->
Compiler)
Attention : le nom sous lequel vous sauvegardez ce module est important.
VI. Déclaration et initialisation de la classe clGdiplus
On va écrire notre code dans le module du formulaire.
Cliquez sur Affichage --> Code pour ouvrir ce module.
Vérifiez que vous avez l'instruction Option Explicit en haut du module.
Sinon rajoutez cette ligne, elle impose la déclaration de toutes les variables et évite ainsi les erreurs de saisie dans leur nom.
| En-tête de module |
Option Compare Database
Option Explicit
|
Pour pouvoir utiliser la classe il est nécessaire de la déclarer et l'initialiser.
La classe est un objet dont le type est le nom sous lequel on a sauvegardé notre module de classe.
Elle se déclare comme n'importe quelle autre variable.
Ensuite on initialise la classe dans l'événement Sur Ouverture du formulaire.
| Déclaration de la classe |
Private clGdip As ClGdiPLus
|
| Initialisation de la classe |
Private Sub Form_Open(Cancel As Integer)
Set clGdip = New clGdiPlus
End Sub
|
Pensez à libérer la classe à la fermeture du formulaire.
(même si celle-ci est libérée automatiquement, je préfère la libérer explicitement).
Dans les propriétés du formulaire, définissez [Procédure événementielle] dans l'événement Sur Fermeture.
Cliquez sur les trois petits points [...] pour générer l'événement dans le code.
A l'intérieur de la procédure Form_Close on va libérer la classe : il suffit de lui donner la valeur Nothing si elle n'a pas déjà cette valeur.
| Libération de la classe |
Private Sub Form_Close()
If Not clGdip is Nothing Then Set clGdip = Nothing
End Sub
|
VII. Dessin du fond étoilé
VII-A. Création de l'image Gdi+
Pour pouvoir dessiner il faut d'abord créer un bitmap (=une image) avec la fonction CreateBitmap.
La taille de ce bitmap doit être exprimée en pixel, donc nous utilisons les fonctions PointsToPixelsX et PointsToPixelsY
pour convertir la taille du contrôle Img en pixel.
Le bitmap obtenu est alors de la même taille que le contrôle Img.
Notez que nous conservons la taille de l'image dans des variables privés du module afin de les réutiliser plus tard.
Autre remarque : l'image est noire et transparente par défaut; nous remplissons donc l'image de noir opaque pour éviter des soucis de superposition d'image plus tard.
| Déclaration en en-tête de module |
Private gWidth As Long, gHeight As Long
|
| Création du bitmap dans Form_Load |
gWidth = clGdip.PointsToPixelsX(Me.Img.Width)
gHeight = clGdip.PointsToPixelsY(Me.Img.Height)
clGdip.CreateBitmap gWidth, gHeight
clGdip.FillColor vbBlack
|
VII-B. Dessin des étoiles
Les étoiles sont des points dessinés à l'aide de la fonction DrawPixel.
La couleur et la position de ces points est aléatoire.
| Déclaration à ajouter dans Form_Load |
Dim lCpt As Long
Dim lCalc As Long
|
| Dessin des étoiles après création du bitmap |
Randomize Timer
For lCpt = 1 To 300
lCalc = 55 + Rnd * 200
clGdip.DrawPixel Rnd * gWidth, Rnd * gHeight, RGB(lCalc, lCalc, lCalc)
Next
|
VII-C. Affichage à l'écran
Pour l'instant, si on exécute le code, rien ne s'affiche.
On a uniquement dessiné en mémoire.
Utilisons la fonction RepaintControl pour injecter l'image dans le contrôle Img.
| Affichage à l'écran, à la suite du code précédent |
clGdip.RepaintControl Me.Img
|
Afficher maintenant le formulaire pour voir le fond étoilé.
VIII. La minuterie
Pour faire un jeu il faut rafraîchir la position des objets et l'affichage à intervalle régulier.
Pour cette opération nous utilisons le timer du formulaire.
VIII-A. Activation de l'événement "Sur Minuterie"
Celui-ci est accessible dans les propriétés du formulaire dans l'onglet événements:
Définissez [Procédure événementielle] dans l'événement Sur minuterie.
Puis cliquez sur les trois petits points [...] pour générer l'événement dans le code.
Définissez ensuite l'intervalle de minuterie.
VIII-B. Choix de l'intervalle de minuterie, la théorie.
L'intervalle de minuterie ne doit pas être choisi au hasard.
Il défini le nombre d'image qui sera dessiné par seconde.
Par exemple, en théorie, pour un intervalle de 20 millisecondes :
1000/20 = 50 donc on obtient 50 images par secondes.
Cela peut paraître beaucoup, mais si vous souhaitez déplacer pixel par pixel un objet
sur toute la hauteur d'un écran de 800 pixels, il faudra compter :
800/50 = 16 secondes.
VIII-C. Choix de l'intervalle de minuterie, la pratique.
Pour tester notre minuterie, nous allons afficher le nombre d'exécutions par seconde sur le formulaire.
Créer une zone de texte nommée TxtRate.
Dans l'événement Form_Timer on va compter le nombre d'exécutions par seconde :
| Affichage du framerate |
Private Sub Form_Timer()
Static sTimer As Double
Static sNb As Long
Dim lTimer As Double
Dim lInterval As Long
lTimer = Timer
If lTimer - sTimer >= 1 Then
Me.TxtRate.Value = CLng((sNb + 1) / (lTimer - sTimer))
sNb = 0
sTimer = lTimer
Else
sNb = sNb + 1
End If
End Sub
|
Définissez un intervalle de minuterie de 25 ms et exécuter le formulaire.
On s'attend à voir un framerate de 1000/25 = 40.
Pourtant l'affichage nous indique environ 33 ce qui correspond à un intervalle de 30.
En fait la minuterie n'a pas une résolution de 1 ms.
C'est à dire qu'on ne peut pas définir notre intervalle à la milliseconde près.
On ne peut définir l'intervalle que par pas de 10, 15 ou 55 suivant l'environnement :
- 55 Pour Windows 95 et 98.
- 15 Pour Windows supérieur à 98 et Dual Core.
- 10 Pour Windows supérieur à 98 sans Dual Core.
Choisir un intervalle de 15 ms permet d'avoir 50 images/s sans Dual Core et 66 images/s avec un Dual Core.
C'est cet intervalle de minuterie que nous choisissons pour ce tutoriel.
IX. Création, affichage et déplacement du vaisseau
Comme son titre l'indique, nous allons dans cette section gérer le vaisseau du joueur.
IX-A. Création d'un module de classe
Notre vaisseau est un objet avec des propriétés telles que sa position, sa vitesse, ...
Commençons par créer un module de classe nommée clShip.
Inscrivons-y les propriétés évidentes :
- la position : X et Y
- la vitesse : Speed
| Classe clShip |
Option Compare Database
OPtion Explicit
Public X As Long, Y As Long
Public Speed As Long
|
IX-B. Déclaration et initialisation de l'objet dans le formulaire
Comme il n'y a qu'un vaisseau, on lui crée une variable objet gShip qu'on initialise à l'ouverture du formulaire
et qu'on libère à la fermeture du formulaire.
A l'initialisation, on défini la position du vaisseau et sa vitesse en pixel.
| Déclaration en en-tête de formulaire |
|
| Initialisation de l'objet vaisseau Form_Load |
Set gShip = New ClShip
gShip.X = gWidth / 2
gShip.Y = 0.9 * gHeight
gShip.Speed = 5
|
| Libération de l'objet vaisseau dans Form_Close |
|
IX-C. Chargement et affichage de l'image du vaisseau
Préparer une image avec votre éditeur de dessin favoris.
Pour ce tutoriel, j'ai simplement utilisé
Paint et
Microsoft photo editor.
Eviter le format Jpeg qui compresse l'image, sinon vous ne pourrez plus travailler l'image pour rendre le tour transparent (il faut qu'il soit uni).
Au chargement du formulaire, ajouter l'image du vaisseau dans la classe clGdiplus.
Vous pouvez également redimensionner cette image.
| Chargement et redimensionnement de l'image dans Form_Load |
clGdip.ImageListAdd "ship", CurrentProject.Path & "\img\ship.bmp"
clGdip.ImageListResize "ship", 80
|
L'affichage est géré dans la procédure Form_Timer.
Nous utilisons la fonction DrawImage pour dessiner le vaisseau sur l'image principale.
Le paramètre GdipSizeModeAutoSize indique que l'image est dessinée à sa taille réelle et centrée sur les coordonnées
gShip.X et gShip.Y.
Puis la fonction FastRepaint redessine l'image du jeu sur le formulaire.
Noter que nous n'injectons pas l'image dans le contrôle avec la fonction RepaintControl car ce serait trop lent.
| Dessin du vaisseau |
clGdip.DrawImage "Ship", gShip.X, gShip.Y, , , , GdipSizeModeAutoSize
clGdip.FastRepaint Me.Img
|
IX-D. Gestion de la transparence
Si l'image est dans un format qui ne supporte pas la transparence, le vaisseau est dessiné dans un rectangle plein.
Il faut rendre ce fond transparent :
1 - soit en remplaçant la couleur du fond par une couleur transparente
|
clGdip.ImageListAdd "ship", CurrentProject.Path & "\img\ship.bmp"
clGdip.ImageListResize "ship", 80
clGdip.ImageListReplaceColor "ship", vbWhite, vbBlack, , 0
|
2 - soit en définissant la couleur de transparence à l'affichage.
(remarque : l'image étant transformée lors du redimensionnement, cette méthode ne donne pas les meilleurs résultats).
|
clGdip.DrawImage "ship", gShip.X, gShip.Y, , , vbWhite, GdipSizeModeAutoSize
|
3 - soit en sauvegardant l'image dans un format qui supporte la transparence.
Le format PNG donne de bons résultats.
Il n'y a pas alors besoin de gérer la transparence dans le code.
Voici l'image du vaisseau au format PNG avec transparence.
IX-E. Gestion des déplacements
Le déplacement du vaisseau se fait avec les touches fléchées.
Lorsqu'on appuie ou relâche une touche, un événement KeyDown puis KeyUp est déclenché.
Si on laisse une touche appuyée, des événements KeyDown successifs sont déclenchés.
Modifier la position du vaisseau directement sur cet événement rendrait un mouvement très saccadé et fonction
de la configuration de la répétition des touches.
Pour avoir un mouvement fluide, on va définir 4 variables booléennes (une par touche) dont on modifiera l'état (Vrai ou Faux)
lors des événements KeyDown et KeyUp.
| Déclaration en en-tête de formulaire |
Private gKeyLeft As Boolean
Private gKeyRight As Boolean
Private gKeyUp As Boolean
Private gKeyDown As Boolean
|
| Sur touche appuyée |
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case 37
gKeyLeft = True
Case 38
gKeyUp = True
Case 39
gKeyRight = True
Case 40
gKeyDown = True
Case Else
End Select
End Sub
|
| Sur touche relâchée |
Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case 37
gKeyLeft = False
Case 38
gKeyUp = False
Case 39
gKeyRight = False
Case 40
gKeyDown = False
Case Else
End Select
End Sub
|
Dans l'événement "Sur minuterie", on vérifie alors l'état des touches grâce à ces variables et on modifie la position du vaisseau en fonction de sa vitesse.
| Dans Form_Timer avant l'affichage du vaisseau |
If gKeyLeft Then gShip.X = gShip.X - gShip.Speed
If gKeyRight Then gShip.X = gShip.X + gShip.Speed
If gKeyUp Then gShip.Y = gShip.Y - gShip.Speed
If gKeyDown Then gShip.Y = gShip.Y + gShip.Speed
|
IX-F. Réinitialisation de l'image à chaque itération
Si vous exécuter le formulaire et déplacer le vaisseau, vous vous apercevez que le vaisseau est dessiné
à chaque fois par dessus l'image précédente.
Il faut, avant chaque dessin de l'image dans
Form_Timer, réinitialiser l'image de fond avant de dessiner les objets dessus.
| Dans Form_Load après le dessin des étoiles |
|
Avoir rempli l'image de fond de noir opaque à sa création est utile ici sinon le fond serait transparent et ça ne marcherait pas.
Vous pouvez maintenant exécuter le formulaire et utilisez les flèche pour déplacer le vaisseau.
X. Création, affichage et déplacement des ennemis
Comme son titre l'indique, nous allons dans cette section gérer les vaisseaux ennemis.
X-A. Création d'un module de classe
Les ennemis sont, comme le vaisseau du joueur, des objets avec des propriétés telles que leur position, leur vitesse, ...
Commençons par créer un module de classe nommée ClEnnemi.
Inscrivons-y les propriétés évidentes :
- la position : X et Y
- la vitesse : Speed
| Classe clEnnemi |
Option Compare Database
OPtion Explicit
Public X As Long, Y As Long
Public Speed As Long
|
X-B. Déclaration et initialisation de l'objet dans le formulaire
Comme il peut y avoir plusieurs ennemis, on crée une collection qu'on initialise à l'ouverture du formulaire
et qu'on libère à la fermeture du formulaire.
Les ennemis seront créés puis insérés dans cette collection.
| Déclaration en en-tête de formulaire |
Private gEnnemis As Collection
|
| Initialisation de la collection d'ennemis dans Form_Load |
Set gEnnemis = New Collection
|
| Libération de la collection d'ennemis dans Form_Close |
|
X-C. Création des ennemis
Les ennemis sont créés en cours de jeu dans la procédure Form_Timer puis ajoutés à la collection.
Nous avons donc besoin d'une variable lEnnemi de type clEnnemi.
Un ennemi apparaît par exemple toutes les demi-secondes.
Il nous faut donc une variable pour conserver le timer lors de la dernière création d'ennemi.
| Déclaration en début de procédure Form_Timer |
Static sLastEnnemi As Double
Dim lEnnemi As ClEnnemi
|
Si on n'a pas créé d'ennemis depuis plus d'une demi-seconde, on crée un objet ennemi avec sa position et sa vitesse.
L'ennemi apparaît en haut de l'écran, avec une position horizontale et une vitesse aléatoire.
| Création d'un ennemi en début de procédure Form_Timer |
If Timer - sLastEnnemi > 0.5 Then
sLastEnnemi = Timer
Set lEnnemi = New ClEnnemi
lEnnemi.X = Rnd * (gWidth) + 1
lEnnemi.Y = 0
lEnnemi.Speed = Rnd * 9 + 1
gEnnemis.Add lEnnemi
Set lEnnemi = Nothing
End If
|
Remarque : l'objet est libéré après son ajout à la collection.
Cela ne signifie pas que l'objet est détruit, car il est toujours contenu dans la collection.
C'est là tout l'avantage d'avoir créé une collection en en-tête de module.
X-D. Chargement de l'image des ennemis
Préparez un fichier image de la même manière que pour le vaisseau puis chargez cette image à l'ouverture du formulaire.
Image utilisée dans ce tutoriel :
Vous pouvez également redimensionner cette image.
| Chargement et redimensionnement de l'image dans Form_Load |
clGdip.ImageListAdd "ennemi", CurrentProject.Path & "\img\ennemi.png"
clGdip.ImageListResize "ennemi", 80
|
X-E. Affichage et déplacement des ennemis
L'affichage et le déplacement des ennemis sont gérés dans la procédure Form_Timer.
Comme il y a plusieurs ennemis, il faut faire une boucle sur la collection d'ennemis.
On souhaite également retirer l'ennemi de la collection s'il dépasse le bas de l'écran.
C'est pour cela que le compteur lCpt est à rebours; on peut supprimer un élément et continuer la boucle vers les éléments d'indice moins élevé.
| Déclaration en début de procédure Form_Timer |
|
| Dessin et déplacement des ennemis dans Form_Timer |
For lCpt = gEnnemis.Count To 1 Step -1
Set lEnnemi = gEnnemis.item(lCpt)
clGdip.DrawImage "ennemi", lEnnemi.X, lEnnemi.Y, , , , GdipSizeModeAutoSize
lEnnemi.Y = lEnnemi.Y + lEnnemi.Speed
If lEnnemi.Y > gHeight Then
gEnnemis.Remove lCpt
End If
Next
|
XI. Création, affichage et déplacement des missiles
Nous allons dans cette section gérer les missiles tirés par le vaisseau du joueur.
XI-A. Création d'un module de classe
Les missiles sont également des objets.
Commençons par créer un module de classe nommée ClMissile.
Inscrivons-y les propriétés évidentes :
- la position : X et Y
- la vitesse : Speed
- la taille : Size
| Classe ClMissile |
Option Compare Database
OPtion Explicit
Public X As Long, Y As Long
Public Speed As Long
Public Size As Long
|
XI-B. Déclaration et initialisation de l'objet dans le formulaire
Comme il peut y avoir plusieurs missiles, on crée une collection qu'on initialise à l'ouverture du formulaire
et qu'on libère à la fermeture du formulaire.
Les missiles seront créés puis insérés dans cette collection.
| Déclaration en en-tête de formulaire |
Private gMissiles As Collection
|
| Initialisation de la collection de missiles dans Form_Load |
Set gMissiles = New Collection
|
| Libération de la collection de missiles dans Form_Close |
|
XI-C. Création des missiles
Les missiles apparaissent lorsqu'on appuie sur la barre d'espace.
On procède de même que les touches fléchées, avec une variable qui contient l'état de la touche espace.
| Déclaration en en-tête de formulaire |
Private gKeySpace As Boolean
|
| Sur touche relâchée |
Case 32
gKeySpace = False
|
Les missiles sont créés en cours de jeu dans la procédure Form_Timer puis ajoutés à la collection.
Nous avons donc besoin d'une variable lMissile de type ClMissile.
Pour éviter de créer des missiles trop fréquemment (un à chaque minuterie), on utilise une variable static pour conserver le timer lors de la dernière création de missile.
| Déclaration en début de procédure Form_Timer |
Static sLastMissile As Double
Dim lMissile As ClMissile
|
Si la touche espace est appuyée et qu'on n'a pas créé de missile depuis plus de 0.2 seconde, on crée un nouvel objet missile.
Le missile doit apparaître à un emplacement bien défini, en fonction de la position actuelle du vaisseau.
En fait on crée deux missiles car notre vaisseau a deux canons.
| Création de missiles en début de procédure Form_Timer |
If gKeySpace Then
If Timer - sLastMissile > 0.2 Then
Set lMissile = New ClMissile
lMissile.X = gShip.X - 27
lMissile.Y = gShip.Y - 10
lMissile.Speed = 10
lMissile.Size = 5
gMissiles.Add lMissile
Set lMissile = Nothing
Set lMissile = New ClMissile
lMissile.X = gShip.X + 23
lMissile.Y = gShip.Y - 10
lMissile.Speed = 10
lMissile.Size = 5
gMissiles.Add lMissile
Set lMissile = Nothing
sLastMissile = Timer
End If
End If
|
XI-D. Affichage et déplacement des missiles
L'affichage et le déplacement des missiles sont gérés dans la procédure Form_Timer.
Comme il y a plusieurs missiles, il faut faire une boucle sur la collection de missiles.
On souhaite également retirer le missile de la collection s'il dépasse le haut de l'écran.
C'est pour cela que le compteur lCpt est à rebours; on peut supprimer un élément et continuer la boucle vers les éléments d'indice moins élevé.
Pour le dessin du missile, nous n'utilisons pas d'image. Un simple rectangle jaune avec un fond rouge suffit.
| Dessin et déplacement des missiles dans Form_Timer après le dessin du vaisseau |
For lCpt = gMissiles.Count To 1 Step -1
Set lMissile = gMissiles.item(lCpt)
clGdip.DrawRectangle lMissile.X - 1, lMissile.Y - lMissile.Size, lMissile.X + 1, lMissile.Y, vbRed, vbYellow, 1
lMissile.Y = lMissile.Y - lMissile.Speed
If lMissile.Y <= 0 Then
gMissiles.Remove lCpt
End If
Next
|
Le projet est déjà bien avancé, on peut déplacer le vaisseau du joueur, des ennemis sont affichés, et on peut tirer des missiles.
Il reste un gros travail à faire : gérer les collisions entre les objets.
XII. Gestion des collisions
Nous allons gérer les collisions entre :
- le vaisseau et les ennemis => si collision alors fin du jeu
- les missiles et les ennemis => si collision alors destruction de l'ennemi et du missile
XII-A. Création d'une région pour le vaisseau du joueur
On pourrait utiliser les paramètres pRegion* de la fonction DrawImage pour créer automatiquement
une région constituée des points non transparents de l'image.
Ce serait la solution la plus simple à développer, mais également la plus lente à l'exécution.
Nous allons donc, pour accélérer la vitesse d'exécution du programme, créer des régions polygonales.
.
Dans la classe clShip, nous ajoutons une variable Polygon de type Variant.
Comme il n'est pas possible de définir un tableau directement dans la déclaration de la variable, nous définissons ce tableau
à la création de la classe dans l'événement Class_Initialize.
| Ajout à la classe clShip |
Public Polygon As Variant
Private Sub Class_Initialize()
Polygon = Array(9, 63, 23, 60, 23, 41, 31, 42, 31, 60, 46, 55, 46, 29, 59, 10, 74, 6, 93, 14, 102, 30, 102, 56, _
117, 61, 117, 41, 125, 42, 125, 60, 140, 64, 139, 96, 9, 96, 9, 63)
End Sub
|
Ce tableau est constitué de points en pixels (X1,Y1,X2,Y2, ...) pris sur le fichier image ship.png.
Ces points forment un polygone fermé qui détermine les limites du vaisseau.
Après avoir dessiné le vaisseau, nous créons une région à partir de ce polygone.
Pour tester visuellement notre région, la fonction FillRegion rempli la région de rouge en semi-transparence.
| Dans Form_Timer |
clGdip.DrawImage "ship", gShip.X, gShip.Y, , , , GdipSizeModeAutoSize
clGdip.CreateRegionPolygon "ship", gShip.Polygon
clGdip.FillRegion "ship", vbRed, , , 150
|
Affichez le formulaire : vous visualisez en rouge la région créée.
Cette région ne suit pas le déplacement du vaisseau et est plus grande que l'image du vaisseau.
Le problème de taille est dû au fait qu'on a redimensionné l'image après l'avoir chargée.
Les coordonnées prise sur l'image avant redimensionnement doivent être corrigée.
Le facteur de correction est, pour l'image de ce tutoriel : 80 / 156
- 80 taille avant redimensionnement
- 156 taille après redimensionnement
| Correction de taille |
clGdip.CreateRegionPolygon "ship", gShip.Polygon
clGdip.ScaleRegion "ship", 80 / 156, 80 / 156
clGdip.FillRegion "ship", vbRed, , , 150
|
Pour repositionner la région, il faut la déplacer en fonction de la position et de la taille de l'image du vaisseau.
| Correction de position |
clGdip.CreateRegionPolygon "ship", gShip.Polygon
clGdip.ScaleRegion "ship", 80 / 156, 80 / 156
clGdip.TranslateRegion "ship", gShip.X - clGdip.ImageListWidth("ship") / 2, gShip.Y - clGdip.ImageListHeight("ship") / 2
clGdip.FillRegion "ship", vbRed, , , 150
|
Affichez le formulaire : vous voyez maintenant la région en rouge se déplacer avec le vaisseau.
Vous pouvez retirer maintenant la ligne de code de remplissage de la région.
XII-B. Création d'une région pour chaque ennemi
Comme pour le vaisseau du joueur, on utilise des régions polygonales.
.
Pour les ennemis cependant, chacun doit pouvoir être identifié de manière unique.
Chaque instance d'une classe possédant un pointeur unique, nous allons l'utiliser.
Dans la classe ClEnnemi, nous ajoutons une variable Polygon de type Variant et sa définition dans l'événement Class_Initialize.
| Ajout à la classe ClEnnemi |
Public Polygon As Variant
Public Id as long
Private Sub Class_Initialize()
Id = ObjPtr(Me)
|