Fonctionnement de la classe CHudBase

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

Introduction

On parle souvent de la classe CHudBase du côté client, mais je pense qu'il faut savoir l'utiliser, je vais donc en expliquer le fonctionnement, et donner quelques classes dérivées.

CHudBase c'est une classe du côté client qui est une classe mère (on va faire dériver d'autres classes de celle-ci), elle est le modèle pour faire tous les dessins du HUD (points de vie, armure, lampe torche, menu des armes, etc.).

C'est généralement aussi les classes qui récupèrent les messages envoyés par le serveur.

CHudBase, déclaration et explications

Regardons tout d'abord la déclaration de la classe, dans hud.h :

//
//-----------------------------------------------------
//
class CHudBase
{
public:
    POSITION      m_pos;
    int            m_type;
    int            m_iFlags; // active, moving,
    virtual        ~CHudBase() {}
    virtual int    Init( void ) {return 0;}
    virtual int    VidInit( void ) {return 0;}
    virtual int    Draw(float flTime) {return 0;}
    virtual void  Think(void) {return;}
    virtual void  Reset(void) {return;}
    virtual void  InitHUDData( void ) {}    // called every time a server is connected to
};

Voilà donc c'est pas très gros.

- m_pos et m_type, je ne sais pas ce que c'est, je ne m'en sers pas personnellement.
- m_iFlags, c'est ce qui retient l'état de la classe. C'est un "bitset" (allez voir le tut à ce sujet).
- ~CHudBase est le destructeur. Il est vide, on peut le redéfinir dans les classes dérivées (grâce au virtual).
- Init() et VidInit() servent à faire les initialisations de variables (VidInit pour les sprites).
- Draw() est exécutée à chaque frame, c'est elle qui va faire les tracés.
- Think() est également lancée à chaque frame, elle sert à faire des calculs (par exemple surveiller si le joueur clique), mais on ne l'utilise pas forcément (elle n'est pas indispensable au fonctionnement de la classe, contrairement aux autres).
- Reset() n'est pas non plus utilisée à chaque fois, elle sert juste à remettre toutes les variables à 0.
- InitHUDData() on ne s'en occupe pas.

Un exemple

On peut mettre un petit exemple de ce qu'il faut mettre pour que la classe marche (ça va rien dessiner, mais les fonctions seront lancées au bon moment). Disons également qu'on a un message qui s'appelle MonMessage.

hud.h, AVANT la déclaration de CHud :

//
// CHudMaClasse : un élément du HUD
//
class CHudMaClasse : public CHudBase
{
public:
    int Init( void );        //initialisation de variables
    int VidInit( void );      //initialisation de sprites
    int Draw( float flTime ); //tracé

private:
    int m_iMonInt;            //un int
    HSPRITE m_hMonSprite;    //un sprite
};

Ensuite plus bas dans hud.h :

CHudAmmo                    m_Ammo;
CHudHealth              m_Health;
CHudGeiger                m_Geiger;
CHudBattery            m_Battery;
CHudMaClasse          m_MonHUD;//bicou
CHudFog                m_Fog;
CHudTrain              m_Train;
CHudFlashlight          m_Flash;
CHudMessage            m_Message;
CHudStatusBar          m_StatusBar;
CHudDeathNotice        m_DeathNotice;
CHudSayText            m_SayText;
CHudMenu                m_Menu;
CHudAmmoSecondary      m_AmmoSecondary;
CHudTextMessage        m_TextMessage;
CHudStatusIcons        m_StatusIcons;
CHudSpectator          m_Spectator;

On a notre objet. Il reste encore quelques modifs dans hud.cpp :

m_Ammo.Init();
m_Health.Init();
m_Geiger.Init();
m_Train.Init();
m_Battery.Init();
m_MonHUD.Init(); //bicou
m_Fog.Init();
m_Flash.Init();
m_Message.Init();
m_StatusBar.Init();
m_DeathNotice.Init();
m_AmmoSecondary.Init();
m_TextMessage.Init();
m_StatusIcons.Init();
GetClientVoiceMgr()->Init(&g_VoiceStatusHelper, (vgui::Panel**)&gViewPort);
m_Spectator.Init();

et plus bas :

m_Ammo.VidInit();
m_Health.VidInit();
m_Geiger.VidInit();
m_Train.VidInit();
m_Battery.VidInit();
m_Fog.VidInit();
m_MonHUD.VidInit(); //bicou
m_Flash.VidInit();
m_Message.VidInit();
m_StatusBar.VidInit();
m_DeathNotice.VidInit();
m_SayText.VidInit();
m_Menu.VidInit();
m_AmmoSecondary.VidInit();
m_TextMessage.VidInit();
m_StatusIcons.VidInit();
GetClientVoiceMgr()->VidInit();
m_Spectator.VidInit();

Maintenant il faut créer un nouveau fichier, par exemple hud_amoi.cpp :

/***
*
*        Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*       
*        this product contains software technology licensed from Id
*        Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc.
*        All Rights Reserved.
*
*  Use, distribution, and modification of this source code and/or resulting
*  object code is restricted to non-commercial enhancements to products from
*  Valve LLC.  All other use, distribution, or modification is prohibited
*  without written permission from Valve LLC.
*
****/
//
// hud_amoi.cpp
//
// implementation of CHudMaClasse class
//

#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"

#include <string.h>
#include <stdio.h>

//On déclare le message "MonMessage" :
DECLARE_MESSAGE(m_MonHUD, MonMessage)

int CHudMaClasse::Init(void)
{
    HOOK_MESSAGE(Mana);
    m_iMonInt = 0;//on initalise la variable
    gHUD.AddHudElem(this);

    return 1;
};


int CHudMaClasse::VidInit(void)
{
    m_hMonSprite = 0;//on initalise le sprite

    return 1;
};

int CHudMaClasse:: MsgFunc_MonMessage( const char *pszName,  int iSize, void *pbuf )
{
    m_iFlags |= HUD_ACTIVE;//on indique que le HUD est actif

    BEGIN_READ( pbuf, iSize );//on commence la lecture

    m_iMonInt READ_BYTE();//on récupère l'entier

    return 1;
}

int CHudMaClasse::Draw(float flTime)
{
    if ( gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH )
        return 1;

    m_hMonSprite = gHUD.GetSprite( m_iMonInt ); // on récupère le sprite qui a
                                                // l'index envoyé par le serveur

    int x, y;
    //Affichage centré
    x = ScreenWidth/2;
    //Là on décale de la moitié de la largeur du sprite,
    //pour que le centre du sprite soit au centre de l'écran :
    x -= (gHUD.GetSpriteRect( m_iMonInt ).right - gHUD.GetSpriteRect( m_iMonInt ).left) / 2;
    //idem verticalement
    y = ScreenHeight/2;
    y -= (gHUD.GetSpriteRect( m_iMonInt ).top - gHUD.GetSpriteRect( m_iMonInt ).bottom) / 2;
       
    SPR_Set( m_hMonSprite, 255, 0, 0 );//on prépare le sprite a être affiché en rouge
    SPR_DrawAdditive( 0, x, y, NULL );//on affiche le sprite

    return 1;
}

Là, si le bit HIDEHUD_HEALTH est actif, la fonction ne va pas être lancée et le dessin n'aura pas lieu. Vous pouvez mettre d'autres conditions ou supprimer les 2 premières lignes pour changer les circonstances d'affichage du HUD.

Conclusion

Bon c'est pas trop compliqué... Maintenant si vous voulez un exemple d'application, c'est dans le tutorial consacré au fog (ajouter du brouillard sur une map).