Les Flags

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

Introduction

Le bitset ? Qu'est ce que c'est que ça ??? Ben ça n'est vraiment pas compliqué. Prenons un exemple simple : le joueur peut posséder plusieurs armes différentes. Pour savoir « l'état d'une arme » sur un joueur il n'y a que deux cas possibles : il l'a ou il l'a pas. Il faudrait donc retenir autant de bool que le joueur possède d'armes... Ça fait pas très propre et c'est chiant à déclarer, etc.

Pour cela on va utiliser le binaire (binaire = 2 soit allumé ou éteint, 1 ou 0) pour retenir pour chaque arme si le joueur la possède.

NB : le bitset c'est un mot à moi (ça existe peut être en anglais... chais pas), mais plus souvent on dit des flags (drapeaux).

Le binaire

Pour ceux qui n'ont pas de livre ou qui n'ont rien compris, je vais essayer de vous expliquer. L'informatique ça marche en binaire, c'est comme ça que les informations sont enregistrées dans la mémoire. Bon je ne vais pas m'attarder trop, on va se concentrer sur Half-Life tout simplement.

Un nombre entier, vous le déclarez avec int, c'est bien ça. Un int c'est codé sur 4 octets, soit 32 bits (un octet ça fait 8 bits pour les incultes). Voici un petit tableau qui nous montre comment sont enregistrés les nombres (pour simplifier ça va porter sur un seul octet) :

Décimal Binaire
1 00000001
2 00000010
3 00000011
4 00000100
5 00000101
6 00000110
7 00000111

Normalement, vous devez avoir compris. Mais vous pouvez utiliser la calculatrice Windows pour convertir en hexa.

Et sinon il existe des opérateurs binaires : & (ET), | (OU), ~ (NOT), ^ (OU exclusif), << (Décalage à gauche), >> (décalage à droite) et c'est tout je crois. Enfin les autres ne sont pas utilisés. & | ~ ^ servent à faire des tests, << et >> servent à décaler les bits. J'explique tout :

L'opérateur ET

On a un premier octet comme ceci : 00010101, et un second : 00001000.

int un, deux;
un = 21; //00010101
deux = 8; //00001000
//Maintenant utilisons les opérateurs :

if ( un & deux )
{
    //blablabla
}

On a donc fait un test qui équivaut à : 00010101 & 00001000. Je rappelle que ET ça veut dire « Ce bit égal à 1 ET celui-là aussi ».

Si on transfère ça bit à bit ça nous fait :

ET (&) Résultat
0 0 0
0 0 0
0 0 0
1 0 0
0 1 0
1 0 0
0 0 0
1 0 0

On obtient donc 00000000 qui est égal à 0. Notre test ne sera donc pas effectué : if ( 0 )

L'opérateur OU

Si on fait pareil avec les autres opérateurs :

if ( un | deux )
{
    //blablabla
}

Je rappelle que OU ça veut dire « Ce bit égal à 1 OU celui-là aussi OU les deux sont égaux à 1 ».

OU (|) Résultat
0 0 0
0 0 0
0 0 0
1 0 1
0 1 1
1 0 1
0 0 0
1 0 1

Ca nous fait 00011101 soit 29. Le contenu du test sera effectué : if ( 29 )

OU exclusif

Maintenant le OU exclusif. Je rappelle que OU exclusif ça veut dire « Ce bit égal à 1 ou celui-là mais pas les deux à la fois » :

OU EX (^) Résultat
0 0 0
0 0 0
0 0 0
1 0 1
0 1 1
1 0 1
0 0 0
1 0 1

On obtient de nouveau 00011101 soit 29, le test est effectué.

L'opérateur NOT

Le NOT signifie : « l'inverse de ce bit ». NB : c'est un opérateur unaire, ça veut dire qu'il ne s'applique qu'à une seule variable (ou plusieurs mises entre parenthèses).

un = ^deux; 
//ou 
un = ^(deux | trois);

NOT (~) Résultat
0 1
0 1
0 1
0 1
1 0
0 1
0 1
0 1

Voilà c'est tout con celui-là. Je n'explique pas le 2e exemple : il suffit de d'abord effectuer le OU (|) et ensuite l'inversion des bits.

Les opérateurs de décallage de bit

Maintenant les deux derniers opérateurs. Ils s'utilisent généralement avec des #define :

#define  BIT_OPTION1  (1<<0)
#define  BIT_OPTION2  (1<<1)
#define  BIT_OPTION3  (1<<2)
#define  BIT_OPTION4  (1<<3)

C'est très simple : le premier « argument » est la valeur du bit. On met 1, et le deuxième argument c'est la valeur du décalage. Encore un petit tableau d'exemples :

<< Résultat
1 0 00000001
1 1 00000010
1 2 00000100
1 3 00001000
1 4 00010000
1 5 00100000
1 6 01000000
1 7 10000000

Bon vous voyez ça décale le bit du nombre de fois qu'on veut. La valeur décimale importe peu, c'est le binaire qui nous intéresse (enfin si vous êtes curieux vous verrez que ça fait 1, 2, 4, 8, 16, 32, 64, 128, ...).

Bon le décalage à droite c'est la même chose, sauf qu'on décale le bit... à droite.

Applications

Là on va faire une application avec le SDK de Half-Life.

Bon alors déjà c'est pas bien d'avoir sauté tout le chapitre Binaire parce qu'il était trop long. Si vous calez rien au binaire, jetez-y un oeil quand même. Pour les sérieux qui ont bien lu le premier chapitre ou ceux qui savent manier le binaire, on va voir l'application dans Half-Life, avec nos armes et le joueur...

Ben c'est tout con : le binaire, c'est un bit mis à 1 ou 0, et ça pourrait nous dire si le joueur possède l'arme ou pas :-)

Concrètement, on va déclarer des armes :

#define ARME_NUM1  (1<<0)
#define ARME_NUM2  (1<<1)
#define ARME_NUM3  (1<<2)
#define ARME_NUM4  (1<<3)
#define ARME_NUM5  (1<<4)

Bon voilà, notre mod a 5 armes. Maintenant on sait qu'il existe la structure entvars_t qui retient plein d'infos sur le joueur, notamment ses armes : int weapons.

Ben quand le joueur pPlayer va ramasser une arme :

pPlayer->pev->weapons |= ARME_NUM1;

Rappel : <variable1> |= <variable2> ça revient à <variable1> = <variable1> | <variable2>.

Maintenant si dans le code on veut tester si le joueur a l'arme n°1 :

if (pPlayer->pev->weapons & ARME_NUM1)
    ALERT( at_console, "Vous avez l'arme n°1 !!!\n" );

Formidable :-)

Conclusion

Voilà, maintenant vous pouvez appliquer cette méthode à n'importe quoi, par exemple une variable du côté client qui va retenir quelles touches sont enfoncées (pour un équivalent du pev->button du serveur).