VGUI partie 2 : Création d'une fenêtre

Écrit le 03/07/2003 par Kurim
Dernière mise à jour : 30/01/2006

Créer la fenêtre avec un bouton d'annulation

Bon si vous testez le code à ce stade ça ne marchera pas parce que la classe CMonMenuPanel, bien qu'elle a été déclarée au début de vgui_TeamFortressViewport.h, n'existe pas et ne contient rien. On va donc reprendre notre code de la partie 1, et créer la classe CMonMenuPanel et y intégrer pour le moment un simple cadre et un bouton d'annulation qui referme le menu. Dans vgui_TeamFortressViewport.h, à la fin du fichier, juste avant le #endif, mettez :

#include vgui_MonMenu.h

Puis créez le fichier vgui_MonMenu.h. Dedans, on va déclarer la classe du menu. Mettez-y donc :

class CMonMenuPanel : public CMenuPanel
{
private:
    CTransparentPanel *m_pWindow;
    CommandButton *m_pCancelButton;

public:
    CMonMenuPanel(int iTrans, int iRemoveMe, int x,int y,int wide,int tall);

    virtual void Initialize( void );
    virtual void Reset( void );
};

Quelques explications : on crée un nouveau TransparentPanel, c'est-à-dire un cadre qui contiendra le menu, puis un bouton, auquel on appliquera le code du bouton d'annulation. En private, on définit la fonction de définition, CMonMenuPanel, qui s'occupera de charger tous les éléments du menu, une fonction Initalize(), qu'on appelle dans le Initialize du Viewport (voir plus haut si vous vous souvenez plus) et Reset(), qu'on appelle pour remettre à zéro le menu avant de l'afficher.

Maintenant faites un nouveau fichier, vgui_MonMenu.cpp et mettez-y :

#include "vgui_int.h"
#include "VGUI_Font.h"
#include "VGUI_ScrollPanel.h"
#include "VGUI_TextImage.h"
#include "hud.h"
#include "cl_util.h"
#include "vgui_TeamFortressViewport.h"

Les includes nécessaires pour faire le VGUI. Vous remarquerez que vgui_MonMenu.h n'y est pas ; normal, il y est par vgui_TeamFortressViewport.h en fait. Ensuite, on va passer au define des dimensions. Comment ça marche ? et ben c simple, une dimension horizontale s'exprime par XRES() et une verticale par YRES(). Pour éviter les soucis avec les différentes résolution, les développeurs de valve ont eu la bonne idée de considérer ces dimensions en 640x480, c'est-à-dire que pour placer des éléments dans le menu, on considère que la taille de l'écran est de 640x480, et le jeu « strech » automatiquement le menu pour l'adapter à la résolution du joueur en gardant les proportions. Exemple si c pas encore clair : pour placer un truc au milieu de l'écran, ça serait XRES(320) et YRES(240), et ça marche même si le joueur joue en 1024x768...

Maintenant admettons qu'on veuille que le cadre de la fenêtre soit à une distance de 10 du bord de l'écran. Il faut définir son origine X, son origine Y, sa longueur et sa largeur. Les deux premiers seront XRES(10) et YRES(10), ensuite ça sera XRES(620) et YRES(460) pour longueur et largeur (car il faut aussi laisser un espace de 10 à droite et en bas !). Le bouton d'annulation sera en bas à gauche disons 20, 400, 120 et 20 pour ses dimensions. On va donc faire des defines selon ça :

#define MONMENU_WINDOW_X                  XRES(10)
#define MONMENU_WINDOW_Y                  YRES(10)
#define MONMENU_WINDOW_SIZE_X        XRES(620)
#define MONMENU_WINDOW_SIZE_Y        YRES(460)
#define MONMENU_CANCEL_BUTTON_X      XRES(20)
#define MONMENU_CANCEL_BUTTON_Y      YRES(400)
#define MONMENU_BUTTON_SIZE_X          XRES(150)
#define MONMENU_BUTTON_SIZE_Y          YRES(20)

On va faire la fonction pour créer tout ça maintenant :

CMonMenuPanel::CMonMenuPanel(int iTrans, int iRemoveMe, int x,int y,int wide,int tall) : CMenuPanel(iTrans, iRemoveMe, x,y,wide,tall)
{
    m_pWindow = new CTransparentPanel( 255, MONMENU_WINDOW_X, MONMENU_WINDOW_Y, MONMENU_WINDOW_SIZE_X, MONMENU_WINDOW_SIZE_Y );
    m_pWindow->setParent( this );
    m_pWindow->setBorder( new LineBorder( Color(255*0.7,170*0.7,0,0 )) );

    m_pCancelButton = new CommandButton( CHudTextMessage::BufferedLocaliseTextString( "Annulation" ),
                                        MONMENU_CANCEL_BUTTON_X, MONMENU_CANCEL_BUTTON_Y,
                                        MONMENU_BUTTON_SIZE_X, MONMENU_BUTTON_SIZE_Y);
    m_pCancelButton->setParent( m_pWindow );
    m_pCancelButton->addActionSignal( new CMenuHandler_TextWindow(HIDE_TEXTWINDOW) );

    Initialize();
}

Là explication s'impose. La première ligne de la fonction créer la fenêtre, entièrement transparente, avec les dimensions données par les defines. La ligne d'après indique que le parent de la fenêtre est le menu, la troisième indique de créer une bordure à cette fenêtre, de couleur jaune. Ensuite pour le bouton, on le crée avec le texte « Annulation » et ses dimensions, on lui met comme parent la fenêtre créer juste avant et on lui ajoute l'action de fermer le menu si on clique dessus. Vous vous souvenez pour le menu en lui même, on avait définit ses parents comme étant le Viewport. Je crois pas avoir expliquer ce qu'était le Viewport. Ben en fait, c'est le support des menus VGUI, on peux considérer ça comme une « couche » sur l'écran au dessus du jeu, c'est-à-dire au dessus de la 3D et du HUD, sur laquelle on « pose » le menu VGUI (enfin dans ce cas précis, parce qu'en fait ça sert de base de tout ce qui est VGUI). Le menu étant fils du VGUI, il va se poser sur cette « couche », puis la fenêtre sur le menu, puis le bouton sur la fenêtre. En résumé, voilà la liste des « couches » qu'on a créées :

  1. Jeu (3D + HUD)
  2. Viewport
  3. Base du menu (MonMenu)
  4. fenêtre (m_pWindow)
  5. Boutton (m_pCancelbutton)

Les liens de parentés servent donc à définir la « superposition » des éléments du VGUI. Pour le bouton, on verra le « signal » dans la partie sur la création des boutons. Après avoir créer tous ça, on initialise le menu, voilà donc cette fonction, qui ne contient rien vu la simplicité du menu pour le moment :

void CMonMenuPanel::Initialize( void )
{

}

Puis Reset(), pas tellement plus compliqué, puisqu'elle exécute la fonction Reset() de sa classe mère...

void CMonMenuPanel::Reset( void )
{
    CMenuPanel::Reset();
}

Voilà c'est tout pour cette partie...

Créer un titre et activer des schemes

On va donc reprendre notre code de la partie 1, et créer la classe CMonMenuPanel et y intégrer pour le moment un simple cadre et un bouton d'annulation qui referme le menu.

Maintenant on va créer le titre de notre menu. Pour ce faire, vous devez charger des schemes, qui correspondent à peu près à des polices. Dans vgui_monmenu.cpp, juste après :

CMonMenuPanel::CMonMenuPanel(int iTrans, int iRemoveMe, int x,int y,int wide,int tall) : CMenuPanel(iTrans, iRemoveMe, x,y,wide,tall)
{
    CSchemeManager *pSchemes = gViewPort->GetSchemeManager();

    SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle( "Title Font" );

    Font *pTitleFont = pSchemes->getFont( hTitleScheme );
    int r, g, b, a;

Donc ici d'abord on fait un pointeur pSchemes qui va nous permettre de récupérer les « polices », ensuite on charge la police des titres. Les différentes polices sont déclarées dans les xxx_textscheme.txt situé dans le dossier valve. xxx correspond à la résolution dans laquelle on utilise la police, parce que la police change (en taille) selon la résolution. Pour en créer des nouvelles ou modifier celle-là pour votre mod, copier ces fichiers dans le dossier de votre mod. Voici comment elles sont définies :

SchemeName = "Primary Button Text"    // Le nom de la police
FontName = "Arial"  // La police, justement....
FontSize = 12 // La taille, faire gaffe à la résolution
FgColor = "255 170 0 255" // Sa couleur primaire, le dernier paramètre c la transparence
BgColor = "0 0 0 141"  // Ca il me semble que c la couleur de fond du cadre de texte, le dernier paramètre c'est la transparence
FgColorArmed = "255 255 255 255"  // Pareil, mais si la police est utilisée pour un bouton, cette couleur sera utilisé pour la "surbrillance"
BgColorArmed = "255 170 0 67" // Et ça c'est donc la couleur de fond d'un bouton en surbrillance

Remarque : si vous en créer un nouveau, vous êtes pas obligé de préciser tous les paramètres, surtout si c'est pas pour un bouton... Une fois qu'on a chargé le scheme, on créer un élément de type Font qui récupère la police du scheme, puis on déclare les 4 variables r, g, b, a qui serviront pour appliquer les couleurs du scheme.

Ensuite on crée l'élément du menu qui contiendra le texte, c'est un Label, c'est-à-dire un cadre invisible qui affiche seulement du texte. Dans vgui_monmenu.h, ajoutez après la déclaration du bouton d'annulation :

Label *m_pTitle;

Puis dans le .cpp après la création du bouton, mettez :

m_pTitle = new Label( "", MONMENU_TITLE_X, MONMENU_TITLE_Y );
m_pTitle->setFont( pTitleFont );
m_pTitle->setParent( m_pWindow );
pSchemes->getFgColor( hTitleScheme, r, g, b, a );
m_pTitle->setFgColor( r, g, b, a );
pSchemes->getBgColor( hTitleScheme, r, g, b, a );
m_pTitle->setBgColor( r, g, b, a );
m_pTitle->setContentAlignment( vgui::Label::a_west );
char txtTitle[255];
sprintf( txtTitle, "Voici MonMenu");
m_pTitle->setText( txtTitle );

Donc d'abord on créer le Label avec le texte « Voici MonMenu » et ses coordonnés, qu'on a pas définit d'ailleurs, on va le faire tout de suite, mettez ça à la suite des autres défine :

#define MONMENU_WINDOW_TITLE_X        XRES(20)
#define MONMENU_WINDOW_TITLE_Y        YRES(20)

Remarque : on définit que l'origine du Label parce que sa longueur et sa largueur se définissent tout seul en fonction du texte.

Après donc, le setFont() récupère la police, on donne comme parent la fenêtre, getFgColor et getBgColor servent à récupérer la couleur du texte et du fond (transparent d'après le scheme) dans les variables qu'on a définit plus haut, puis setFgColor et setBgColor la définissent pour le label. Enfin, on donne le texte à afficher en passant par la variable txtTitle. Et voila c tout pour le titre, vous pouvez créer des Label pour faire autre chose aussi, des sous-titres ou nimp.

Créer un cadre de texte avec une barre de défilement

Bon maintenant on va faire un cadre dans lequel vous pouvez écrire du texte, mais pas comme le titre, là vous pourrez écrire plusieurs lignes. Donc on charge un nouveau scheme, mettez ça à la suite de l'autre :

SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle( "Title Font" );
SchemeHandle_t hInfoText = pSchemes->getSchemeHandle( "Briefing Text" );

Font *pTitleFont = pSchemes->getFont( hTitleScheme );
Font *pTextFont = pSchemes->getFont( hInfoText );

Ensuite faite la déclaration dans vgui_monmenu.h :

TextPanel *m_pInfoText;

Et des defines au début du .cpp :

#define MONMENU_TEXT_X                XRES(20)
#define MONMENU_TEXT_Y                YRES(80)
#define MONMENU_TEXT_SIZE_X            XRES(400)
#define MONMENU_TEXT_SIZE_Y            YRES(200)

ensuite créez la barre et le cadre après le titre :

m_pScrollPanel = new CTFScrollPanel( MONMENU_TEXT_X, MONMENU_TEXT_Y, MONMENU_TEXT_SIZE_X, MONMENU_TEXT_SIZE_Y );
m_pScrollPanel->setParent(m_pWindow);
m_pScrollPanel->setScrollBarVisible(false, false);

m_pInfoText = new TextPanel( "",  MONMENU_TEXT_X,MONMENU_TEXT_Y, MONMENU_TEXT_SIZE_X, MONMENU_TEXT_SIZE_Y );
m_pInfoText->setParent( m_pScrollPanel->getClient() );
m_pInfoText->setFont( pTextFont );
pSchemes->getFgColor( hInfoText, r, g, b, a );
m_pInfoText->setFgColor( r, g, b, a );
pSchemes->getBgColor( hInfoText, r, g, b, a );
m_pInfoText->setBgColor( r, g, b, a );
char txtInfoText[255];
sprintf( txtInfoText, "Voici du texte\nblablabla\nblablabla");
m_InfoText->setText( txtInfoText );
m_pScrollPanel->validate();

Donc là on définit la barre de défilement, avec comme parent la fenêtre, et le cadre pour lequel on définie les parents avec une fonction de la barre de défilement pour lui indiquer qu'il doit fonctionner avec celle-ci. C'est pour cette raison qu'on créer la barre avant le cadre. Le reste je l'ai déjà expliqué. Le \n dans le texte sert à aller à la ligne, le m_pScrollPanel->validate() sert à mettre à jour la barre de défilement avec le cadre.