MTProd > Dev4all > Articles > APIs > DirectX > Votre premier jeu Windows - C++/DirectX > 4 InitApp()
Rechercher21 Personnes en-ligne
Votre premier jeu Windows - C++/DirectX

4  InitApp()

Une fonction non-obligatoire mais du moins indispensable dans notre application, nous allons dans cette fonction, créer notre fenêtre et initialiser nos donnnées.



4.1  Listing

Listing 4.1.1 : InitApp()
(
ouvrir dans une nouvelle fenêtre)

 1  static HRESULT InitApp( HINSTANCE hInstance, int nCmdShow )
 2  {
 3      DDSURFACEDESC2            ddsd; // structure de description de surface
 4      DDSCAPS2                ddscaps; // structure de capacités de surface
 5  
 6      // Crée et déclare la classe de fenêtre
 7      wc.style = CS_HREDRAW | CS_VREDRAW;
 8      wc.lpfnWndProc = WindowProc;
 9      wc.cbClsExtra = 0;
 10      wc.cbWndExtra = 0;
 11      wc.hInstance = hInstance;
 12      wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
 13      wc.hCursor = LoadCursor(NULL, IDC_ARROW);
 14      wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
 15      wc.lpszMenuName = NAME;
 16      wc.lpszClassName = NAME;
 17      RegisterClass(&wc);
 18  
 19      // Crée notre fenêtre
 20      hWnd = CreateWindowEx(
 21      WS_EX_TOPMOST,
 22      NAME,
 23      TITLE,
 24      WS_POPUP,
 25      0,
 26      0,
 27      GetSystemMetrics(SM_CXSCREEN),
 28      GetSystemMetrics(SM_CYSCREEN),
 29      NULL,
 30      NULL,
 31      hInstance,
 32      NULL);
 33  
 34      if (!hWnd)
 35          return FALSE;
 36      ShowWindow(hWnd, nCmdShow);
 37      UpdateWindow(hWnd);
 38      SetFocus(hWnd);
 39  
 40  
 41      // Crée l'objet principal DirectDraw
 42      if ( FAILED( DirectDrawCreateEx( NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL ) ) )
 43          return Fail( hWnd, "Impossible de créer l'objet DirectDraw !" );
 44      
 45      // Obtient mode exclusif.
 46      if ( FAILED( lpDD->SetCooperativeLevel( hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ) ) )
 47          return Fail( hWnd, "Impossible de définir le niveau coopératif." );
 48  
 49      // Prend comme mode vidéo les valeurs spécifiée dans la structure screen
 50      // avec des couleurs sur 16 bits
 51      if ( FAILED( lpDD->SetDisplayMode( RES_X, RES_Y, 16, 0, 0 ) ) )
 52          return Fail( hWnd, "Impossible de définir le mode vidéo." );
 53      
 54      // Crée la surface primaire avec un back buffer
 55      ZeroMemory(&ddsd, sizeof(ddsd));
 56      ddsd.dwSize = sizeof(ddsd);
 57      ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
 58      ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
 59                                                  DDSCAPS_FLIP |
 60                                                  DDSCAPS_VIDEOMEMORY |
 61                                                  DDSCAPS_COMPLEX;
 62      ddsd.dwBackBufferCount = 1;
 63      if ( FAILED( lpDD->CreateSurface( &ddsd, &surfFront, NULL ) ) )
 64          return Fail( hWnd, "Impossible de créer la surface primaire." );
 65      
 66      // Obtient un pointeur vers le back buffer
 67      ZeroMemory(&ddscaps, sizeof(ddscaps));
 68      ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
 69      if ( FAILED( surfFront->GetAttachedSurface( &ddscaps, &surfBack ) ) )
 70          return Fail( hWnd, "Impossible de trouver le back buffer" );
 71  
 72  
 73      // Crée les sprites en chargant leurs images
 74      surfCurseur = DDLoadBitmap( lpDD, "data\\curseur.bmp", 0, 0 );
 75      if ( surfCurseur == NULL )
 76          return Fail( hWnd, "Impossible de chargé l'image curseur.bmp" );
 77  
 78      surfSprite = DDLoadBitmap( lpDD, "data\\sprite.bmp", 0, 0 );
 79      if ( surfSprite == NULL )
 80          return Fail( hWnd, "Impossible de chargé l'image sprite.bmp" );
 81  
 82      // Initialise les coulours transparentes
 83      DDSetColorKey(surfCurseur, RGB(TRANS_R, TRANS_G, TRANS_B));
 84      DDSetColorKey(surfSprite, RGB(TRANS_R, TRANS_G, TRANS_B));
 85  
 86  
 87      // INITIALISATION DE DIRECT_INPUT
 88      if( !InitDirectInput( hWnd ) )
 89          return Fail( hWnd, "Impossible d'initialiser DirectInput" );
 90  
 91  
 92      // Lance la fonction d'initialisation du jeu
 93      InitGame();
 94  
 95  
 96      return DD_OK;
 97  }


En ligne 1 nous déclarons notre fonction, il s'agit d'une fonction de type static HRESULT.
static oblige la variable (ou fonction) suivant ce mot-clé à être alloué au lancement du programme et désalloué à la fin.
Ce mot-clé sert aussi à cacher cette fonction aux autres fichiers de notre application, vous ne pourrez donc pas appeller cette fonction ailleurs que dans le fichier jeu_exemple1.cpp.

Voici les 2 arguments de InitApp :

  • HINSTANCE hInstance : L'handle de l'instance courante. Un handle est une variable qui identifie un objet ; une référence indirecte à une ressource du système d'exploitation

  • int nCmdShow : Le status de la fenêtre, spécifie comment notre fenêtre doit être vue


En ligne 3 et 4 , nous déclarons deux variables de type DDSURFACEDESC2 et DDSCAPS2 nommées ddsd et ddscaps, il s'agit de types DirectDraw (DD), nous les utiliserons pour nos surfaces plus loin.




4.2  Création d'une classe fenêtre

Ligne 7 à 17, nous définissons la classe de notre fenêtre nommé wc pour "Window Class" et déclarée dans notre fichier globals.h, il s'agit d'une structure Windows appellée WNDCLASS.
Voyons en détails cette déclaration :

  • Ligne 7 : Nous définissons les styles de notre classe (chaque style est combiné par l'opérateur "bitwise" OR (|),
    ici CS_HREDRAW (ClassStyle_HorizontalRedraw) afin de redessiner complétement notre fenêtre si un mouvement ou un ajustement de ça taille change la largeur de la zone client client area.
    Nous utilisons aussi CS_VREDRAW (ClassStyle_VerticalRedraw) afin de redessiner complétement notre fenêtre si un mouvement ou un ajustement de ça taille change la hauteur de la zone client client area.
    En plus claire, Windows redessinera notre fenêtre si elle a bougé ou changé de taille

  • Ligne 8 : Nous faisons pointer lpfnWndProc vers notre fonction WindowProc() définit plus haut

  • Ligne 9 et 10 : Nous définissons le nombre de bytes extra à allouer, ici 0 (ce n'est pas important dans notre exemple)

  • Ligne 11 : Nous fixons l'handle de l'instance que nous utiliserons pour notre fenêtre, ici nommé hInstance

  • Ligne 12 : Nous fixons l'handle de l'icône pour notre fenêtre, ici l'icône IDI_MAIN_ICON définit dans nos ressources (fichier resource.rc et resource.h)

  • Ligne 13 : Nous fixons l'handle de notre curseur, ici IDC_ARROW, la jolie fleche standard de Windows

  • Ligne 14 : Nous fixons l'handle du fond de notre application, ici BLACK_BRUSH, noir

  • Ligne 15 : Nous définissons la ressource utilisée pour notre menu, nous utilisons la chaine NAME définit dans le fichier globals.h, la ressource nommée "JEU_EXEMPLE1" (valeur de NAME) n'existe pas mais c'est au cas ou nous voudrions créer un menu, nous l'appelerons alors "JEU_EXEMPLE1"

  • Ligne 16 : Le nom de notre classe, ici le macro NAME (définit dans le fichier globals.h et valant "JEU_EXEMPLE1")

  • Ligne 17 : En fin nous enregistrons notre classe grace à la fonction RegisterClass() en lui passant l'adresse (&) de wc


Fabuleux, fabuleux, la classe de notre fenêtre est maintenant créee et enregistrée, mais que nous manque-t-il ?
La fenêtre proprement dit biensur ! Cela tombe bien, nous allons justement la créer maintenant




4.3  Création d'une fenêtre

C'est très facile, il suffit d'appeller la fonction CreateWindowEx() comme en ligne 20, notre fenêtre hWnd (déclaré dans globals.h) prendra alors la valeur retournée.
J'ai placé cet appel sur plusieurs lignes afin que les paramètres passés soit plus clairs, voyons ces derniers :

  • Ligne 21 : Les styles étendus, ici WS_EX_TOPMOST afin de spécifier que notre fenêtre doit être placé et rester en dessus de toutes les autres fenêtres ouvertes

  • Ligne 22 : Le nom de la classe, ici le macro NAME comme nous l'avons appellé dans la définition de cetter dernière plus haut

  • Ligne 23 : Le titre (son nom sous Windows), nous utilisons le macro TITLE définit comme toujours dans le fichier globals.h

  • Ligne 24 : Le style, ici WS_POPUP, une fenêtre "pop-up", style une question Windows

  • Ligne 25 : La position x, ici 0 (à gauche)

  • Ligne 26 : La position y, ici 0 (en haut)

  • Ligne 27 : La largeur, nous appellons la fonction GetSystemMetrics() qui retourne des pixels, nous lui passons la valeur SM_CXSCREEN (largeur de la résolution de l'écran)

  • Ligne 28 : La hauteur, nous appellons la fonction GetSystemMetrics() qui retourne des pixels, nous lui passons la valeur SM_CYSCREEN (hauteur de la résolution de l'écran)

  • Ligne 29 : Un handle vers la fenêtre parente de notre fenêtre, ici NULL pour spécifier que notre fenêtre est la fenêtre parente

  • Ligne 30 : Un handle vers le menu, ici NULL (nous n'avons pas de menu)

  • Ligne 31 : Avec quel handle la fenêtre doit-elle être associé ? Notre handle hInstance

  • Ligne 32 : Il s'agit du paramêtre lParam qui sera passé à notre fonction WindowProc() pour cette fenêtre, nous n'en avons pas besoin, nous lui passons donc la valeur NULL


Voilà, vous saurez maintenant comment créer une fenêtre en détails, c'est toujours utile sous Windows
Continuons notre petite (quoique) explication.
En ligne 34 nous testons la valeur de hWnd, si elle ne vaut rien cela signifie que notre fenêtre n'a pas été crée, nous retournons FALSE -> notre fonction InitApp a échouée.
Ligne 36, nous affichons notre fenêtre grace à la fonction ShowWindow() avec comme second paramêtre nCmdShow.
Ligne 37, nous mettons à jour notre fenêtre afin que Windows la mette en forme avant de lui donner le focus en ligne 38.




4.4  DirectDraw - Les modes

Passons à DirectDraw, nous allons l'utiliser pour nos surfaces et pour toutes les fonctions de dessins.
Ligne 42, nous essayons de créer l'object principal de DirectDraw avec la fonction DirectDrawCreateEx(), voyons ces paramêtres :

  1. Le driver graphique qui sera utilisé par DirectDraw, ici NULL -> nous utilisons les drivers actuels

  2. L'adresse de notre objet DirectDraw (lpDD) déclaré dans globals.h

  3. Quel version de DirectDraw allons-nous utiliser ? Ici la 7 -> IID_IDirectDraw7

  4. Ce paramêtre n'est pas utilisé pour l'instant, nous passons la valeur NULL


Si notre création n'a pas pû s'effectuer prévu nous retournons une erreur avec la fonction Fail().

Ligne 46, nous essayons d'obtenir le mode exclusif de DirectDraw grace à la fonction SetCooperativeLevel() avec comme 2ème paramêtre DDSCL_EXCLUSIVE et DDSCL_FULLSCREEN, le mode exclusif est bien plus rapide que le mode rapide car il s'execute en plein écran, de plus l'application est la seul à pouvoir contrôler DirectDraw.
La aussi si la fonction échoue on retourne une erreur avec la fonction Fail().

Ligne 51, nous essayons de changer le mode d'écran grace à la fonction SetDisplayMode() dont voici les paramêtres :

  1. La largeur de l'écran en pixel, ici RES_X (640) que nous avons définit dans globals.h

  2. La hauteur de l'écran en pixel, ici RES_Y (480) que nous avons définit dans globals.h

  3. Le nombre de bits par pixel (BPP), ici 16 bits (65536 couleurs)

  4. Le taux de raffraichissement, ici 0 signifie que l'interface IDirectDraw sera utilisé

  5. D'éventuels options, ici aucune, nous passons alors la valeur 0


Si la fonction échoue, nous retournons une erreur via Fail().




4.5  DirectDraw - Surfaces et problème de déchirure

Nous allons maintenant créer nos surfaces, or avant de ce faire nous allons parler d'un problème bien connu des développeurs de jeux-vidéo, à savoir "La déchirure d'images"...
Un processeur de nos jours exécute des millions d'instructions par seconde alors que pendant cette même seconde l'écran est dessiné moins de cent fois, vous comprendrez bien qu'il reste du temps au procésseur pour faire des "bêtises".
Effectivement, imaginons que nous déplaçons une balle, au temps a (voir figure 4.5.1) la balle se trouve à gauche de l'écran dans la mémoire vidéo et n'a pas encore été dessiné sur le moniteur vidéo.
Au temps b, la balle est au milieu de l'écran dans la mémoire vidéo, le moniteur commence à la dessiner.
Le problème survient au temps c, la balle a été calculée par le procésseur comme étant à droite de l'écran et ne se sousciant pas de savoir si le moniteur à fini de la dessiner, il place la balle dans la mémoire vidéo, le moniteur continue à dessiner la balle, or elle se trouve maintenant à droite de l'écran, ce qui produit un effet de déchirure comme vous pouvez le constater sur la figure ci-dessous :



Figure 4.5.1: Problème de déchirure

La solution à ce problème est simple : n'actualisez pas le tampon vidéo pendant que l'image est dessiné à l'écran !
C'est là qu'interviennent les intervalles vides verticaux (les canons à électrons sont arrêtés et sont en cours de repositionnement afin de se préparer au dessin du prochain écran). Si vous attendez un intervalle vide vertical pour modifier le tampon vidéo, vous pouvez être assuré qu'un écran complet a été dessiné. Durant cette intervalle vous pouvez faire ce que vous voulez en mémoire vidéo sans crainte de déchirement.
Or cet intervalle est bref et ne peut convenir qu'a des des animations et jeux simples, pour nos fantastiques graphismes et futur fabuleux jeux il nous faut plus de temps, l'idéal serait donc de pouvoir modifier la mémoire vidéo à tout moment sans se préocuper des déchirements.
Le double tamponnage (double buffering) est une technique courante qui permet de répondre à nos demandes, le principe est simple, nous n'écrivons jamais dans la mémoire vidéo directement mais dans un tampon auxiliaire (back buffer), ce tampon sera alors basculé (flip) avec le tampon vidéo pendant un intervalle vide vertical.
DirectDraw permet cette technique, pour cela nous avons déclaré deux surfaces, notre surface primaire surfFront et notre "back buffer" surfBack toujours dans le fichier globals.h.

Reportez-vous au listing 4.1.1. Avant de créer une surface il nous faut définir une structure de description de surface (DDSURFACEDESC2) nommé ddsd (déclaré en ligne 3).
Tout d'abord nous effaçons le contenu de cette structure grâce à la fonction ZeroMemory(), ligne 55.
Ligne 56, nous initialisons la taille de notre structure avec sizeof(ddsd).
Ligne 57, nous définissons les drapeaux (flags) de controles optionnels avec DDSD_CAPS et DDSD_BACKBUFFERCOUNT spécifiant qu'une surface "back buffer" sera utilisée avec cette surface.
Ligne 58 à 61, nous définissons les capacités (caps pour capabilities) de notre surface :

  • DDSCAPS_PRIMARYSURFACE : Il s'agit de la surface primaire

  • DDSCAPS_FLIP : Cette surface est une partie d'une surface basculable (flipping)

  • DDSCAPS_VIDEOMEMORY : Cette surface existe dans la mémoire vidéo

  • DDSCAPS_COMPLEX : Il s'agit d'une surface complexe (attachée a d'autres surfaces, ici notre futur "back buffer")


Ligne 62, nous fixons le nombre de surface "back buffer" à 1 : ddsd.dwBackBufferCount = 1;
Enfin en ligne 63, nous tentons de créer cette surface avec la fonction CreateSurface() de notre objet DirectDraw lpDD, les arguments de cette fonction sont tout simple :

  1. L'adresse de la structure de description de surface

  2. L'adresse de notre surface

  3. Inutilisé pour l'instant


Sinon, nous retournons une erreur Fail().

Nous allons maintenant obtenir un pointeur vers notre surface "back buffer" :
Ligne 67, nous vidons le contenu de la structure de capacités (DDSCAPS2) nommé ddscaps et déclaré en ligne 4.
Ligne 68, nous fixons les capacités de notre surface avec DDSCAPS_BACKBUFFER et nous tentons en ligne 69, d'obtenir la surface attachée à la surface primaire (surfFront) avec la fonction GetAttachedSurface(), nous faisons pointer notre objet surfBack vers cette surface.
Si le traitement échoue, nous retournons comme d'habitude une erreur Fail().

Voilà, nous avons créer nos deux surfaces, à savoir la surface primaire surfFront et la surface "back buffer" surfBack.




4.6  Créer et charger des sprites

Charger des sprites dans une surface est un jeu d'enfant grâce à la fonction DDLoadBitmap() définit dans le fichier ddutil.cpp.
Nous allons commencer par charger l'image de notre curseur de souris en ligne 74 avec DDLoadBitmap() qui prend les arguments suivant :

  1. Un objet DirectDraw, ici lpDD

  2. Le chemin du bitmap, relatif à l'emplacement du fichier executable une fois compilé, ici nous avons placé cette nos images dans le répertoire data, le chemin sera donc data\\curseur.bmp, nous devons placer deux "back slashs" car il s'agit d'un caractère réservé tout comme %

  3. La largeur désirée, 0 signifie toute la largeur

  4. La hauteur désirée, 0 signifie toute la hauteur


Ligne 75, nous testons si notre bitmap a été correctemnt chargé dans notre surface, ici surfCurseur, s'il la valeur de cette dernière est NULL alors nous retournons une erreur Fail().
Ligne 78 à 80, nous effetuons les même opérations pour notre sprite (une sorte de balle contenu dans le fichier bitmap sprite.bmp).

Il nous maintenant définir la couleur transparente pour nos sprites, tous les pixels de cette couleur contenu dans nos sprites ne seront pas dessiné lors d'un blitting (§ 6.2).
Nous définissons cette couleur en ligne 83 et 84 avec la fonction DDSetColorKey() qui prend comme premier paramêtre la surface à affecter et comme deuxième une structure COLORREF au format RGB (Red, Green, Blue), les valeurs sont les macros TRANS_R, TRANS_G et TRANS_B définit dans globals.h.




4.7  Le reste

S'en est finit pour l'initialisation de DirectDraw et ses surfaces, occupons-nous maintenant de DirectInput, qui lui, gère les périphériques d'entrés comme la souris et le clavier notamment.
Ligne 88, nous appellons la fonction InitDirectInput() (définit dans le fichier input.h), si la fonction échoue, nous retournons, là encore, une erreur Fail().
Notez que nous reviendrons en détails sur cette fonction dans le chapitre du même nom.

La ligne 93 appelle simplement la fonction InitGame() (disponible dans le fichier fonctions.h), nous ne reviendrons pas sur cette fonction qui se limite à initialiser nos variables, rien de bien grandiose.
Nous avons avons crée une autre fonction d'initialisation afin de pouvoir l'appeller lorsque nous désirons redémarrer le jeu sans devoir recharger et effectuer toutes les opérations que nous avons vues plus haut.

La dernière ligne (96) retourne DD_OK, constantes signifiant que la requete DirectX s'est correctement déroulée !





<<  3  WindowProc()Sommaire5  WinMain()  >>

 Accés rapide

1  Introduction
2  Les fichiers et répertoires
3  WindowProc()
4  InitApp()

Listing

Création d'une classe fenêtre

Création d'une fenêtre

DirectDraw - Les modes

DirectDraw - Surfaces et problème de déchirure

Créer et charger des sprites

Le reste

5  WinMain()
6  UpdateFrame()
7  fonctions.h
8  input.h
9  Conclusion
Voir le sommaire complet

 Liens utiles

  • Publier un article
  • Envoyer cette page
  • Ecrire à l'auteur

  •  Mini-Chat

    Thienou (00h11): salut
    Thienou (00h13): Oula mon inscription date de 11 ans je me sent vieux :)
    neowolf25 (17h59): MMF2 en "pay what you want" jusqu'à demain sur
    neowolf25 (17h59): https://www.hu
    mblebundle.com/
    weekly

    Miuka (21h15): Coin coin de 2014
    Miuka (21h15): Des gens qui ont migré sur le forum Clickteam ou ailleurs ?
    Strike (09h45): Salut les vieux !
    Hikarion (12h46): Salut les djeunz
    Hikarion (13h38): A qui profite le scandale ?
    Hikarion (13h44): le chat irc est toujours actif ?

    Votre message



     Archives

     Dev4all Newsletter

    Restez à jour avec la newsletter mensuelle !

    Votre e-mail


    1800 abonnés

     Recommander Dev4all

    Recommandez Dev4all à un ami. Cela fera grandir notre communauté !

    E-mails de vos amis




    [ Accueil | S'inscrire | Mon Dev4all | Communauté | Téléchargements | Articles | Forums | Chat ]

    [ A propos de Dev4all | Aide | La charte Dev4all | Contact ]

    © 2000-2018 MTProd. Tous droits réservés.
    L'utilisation de Dev4all implique l'acceptation et le respect de la charte Dev4all.