Arma: Créer une interface utilisateur (part 2)

Dans cet article, je vais décrire en détail le fonctionnement des interfaces graphique utilisateur. L’objectif est de te donner des clefs pour que tu puisses à partir du Wiki de Bis composer des menus adaptés à tes missions.

Tout d’abord, pour que les choses soient claires, une interface graphique sous Arma s’appelle un display.

Il existe deux types d’interfaces :

  • Dialog: une seule et unique interface qui va interrompre les actions du joueurs, et interagir avec lui. (Exemple un menu inventaire)
  • Hud: une/plusieurs interfaces qui s’affichent en même temps, et qui n’interrompent pas les actions du joueurs (Exemple une barre de vie, des informations concernant les armes, etc.)

L’ensemble des éléments qui composent ces interfaces (boutons, textes, chechbkox, etc) s’appellent des contrôles.

Déclarer son interface

Pour utiliser une interface, il faut dans un premier temps la décrire, c’est à dire la déclarer en dur dans un fichier sous forme de classe. Cette déclaration va permettre de définir :

  • à quoi l’interface va ressembler,
  • les contrôles qui seront utilisées,
  • le positionnement des contrôles,
  • le style des contrôles,
  • etc…

Est-ce que tu crées ?

  • une mission: la déclaration se fera alors dans le fichier description.ext, ou dans un autre fichier toninterface.hpp inclus dans description.ext via la macro commande #include
  • un addon: la déclaration se fera dans le fichier config.cpp

Si tu prévois de faire un addon, c’est plus simple de déclarer ton interface via le fichier description.ext pour commencer et de la déplacer ensuite dans le fichier config.cpp. La raison à cela est qu’il suffit de relancer la mission, pour que les modifications soient prise en compte.

Il faut également savoir qu’une erreur de syntaxe dans la déclaration de ton interface peut entrainer un plantage du jeu. Dans ce cas là, il faut relire tranquillement le fichier pour retrouver l’erreur et relancer le jeu. Au début, je te conseille d’avancer pas à pas, modifier, tester, modifier, tester pour identifier plus rapidement les bugs et comprendre le fonctionnement général.

Les interfaces de type Dialog

Les interfaces de type Dialog sont interactives et peuvent contenir un ou plusieurs contrôles. Tu peux déclarer autant d’interfaces Dialog que tu le souhaites du moment qu’elles portent des noms différents.

Règle importante, tu ne peux afficher qu’une seule et unique interface Dialog à la fois. Si une interface Dialog est déjà affichée, et que tu en crées une nouvelle, l’interface existante va disparaitre jusqu’à temps que la nouvelle soit fermée.

Nous allons maintenant déclarer une interface Dialog simple utilisant un contrôle texte qui affichera le texte « hello world ». Place ce code dans ton fichier description.ext.

class moninterface {                              
    // id de l'interface (id display)
    idd = 20000;
    // ne peut pas être déplacée
    movingEnable = 0;
    // conteneur pour les contrôles objects
    objects[] = { };
    // conteneur pour les contrôles dans le background
    controlsBackground[] = { };
    // conteneur pour les contrôles dans le foreground
    class Controls {
        class MyMsg {
        // id du contrôle (id control)
        idc = 20000;
        // type de contrôle(text)
        type = 0;
        // le texte est centré
        style = 2;                          
        // position du contrôle
        x = 0.3; y = 0.4; 
        // la taille du contrôle
        w = 0.4; h = 0.05;                  
        // couleur du texte
        colorText[] = {1, 1, 1, 1};         
        // couleur de fond du controle
        colorBackground[] = {1, .5, 0, .6}; 
        // la police
        font = "TahomaB";                   
        // la taille de la police
        SizeEx = 0.04;
        // le texte
        text = "Hello World";
        };
    };
}; 

Je ne rentrerais pas ici dans le détail de chacune des propriété d’une interface de type Dialog, et de ses contrôles, car il y en a beaucoup. Par contre, tu peux trouver des descriptions plus ou moins détaillées sur le Biki de BIS.

Les choses importantes à retenir sont les suivantes. Une interface Dialog est elle même composée de 3 classes conteneurs qui contiennent les contrôles.

Les classes conteneurs utilisent le principe de calques qui se superposent (comme sur des logiciels de type photoshop). Un calque permet de définir, l’ordre d’affichage des contrôles, qui est devant, qui est derrière.

Les 3 classes conteneurs sont:

  • ControlsBackground: les contrôles déclarés dans ce conteneur apparaitront dans le fond de l’interface, derrière les classes conteneurs controls et objects. Le dernier contrôle déclaré se trouvera à chaque fois par dessus le calque du contrôles précédent de cette classe conteneur
  • Controls: Le dernier contrôle déclaré se trouvera à chaque fois par dessus le calque du contrôles précédent de cette classe conteneur. Le premier contrôle déclaré est celui qui a par défaut le focus (exemple détermine quel bouton est actif).
  • Objects: Ce conteneur est utilisé pour les contrôles qui permettent par exemple d’afficher des objets en 3d

Chaque interface à un numéro d’identifiant unique (idd: id display), et chaque contrôle a lui aussi un numéro d’identifiant unique (idc : id control). Ces identifiants peuvent être paramétré à -1 quand tu veux qu’ARMA gère l’id lui même. Les identifiants sont utiles, car ils permettent par la suite de retrouver les display / control via le script et d’en modifier les propriétés.

Dans notre exemple d’interface, je n’ai utilisé que le conteneur controls. Mais tu peux très bien ajouter des contrôles en plus dans les autres conteneurs.

Pour afficher cette interface Dialog dans le jeu, tu dois utiliser le nom de l’interface que tu as précédemment déclaré avec la commande createDialog.

_ok = createDialog "moninterface";

Celle-ci affichera donc ton message « Hello World ».

Une interface Dialog peut être fermée soit en appuyant sur la touche « Esc », soit en utilisant la commande closeDialog et renvoyer un code retour à l’event handler onUnload de l’interface.

closeDialog "uncoderetourpoureventOnUnLoad";

On voit couramment dans les scripts créant des interfaces Dialog ce type de code, ce qui permet à la fermeture de l’interface de reprendre l’exécution du script ou il en était resté.

_ok = createDialog "moninterface";
waitUntil { !dialog }; // attend jusqu'à temps que l'interface soit fermée
hint "Mon interface est fermée";

Voici, un exemple pour retrouver l’interface, et son contrôle et opérer un changement sur le contrôle.

_display = findDisplay 20000;
_ctrl = _display displayCtrl 20000;
_ctrl ctrlSetText "hello there";

Bien, je pense que tu en connais maintenant assez pour faire tes armes sur les interfaces de type Dialog. Parlons maintenant un peu des interfaces de type HUD.

Les interfaces de type HUD

Les interfaces de type HUD sont utiles quand on veut montrer continuellement des informations, sans que cela affecte les actions courantes de l’utilisateur. La différence avec les interfaces Dialog est que l’utilisateur ne pourra pas les cliquer ou y saisir de la donnée. L’arme sélectionné, le nombre de munitions affichés par défaut dans le jeu, sont un exemple d’interface HUD.

Les interfaces de type HUD se déclarent également dans le fichier description.ext mais sont toutes des sous classes de RscTitles { }. Cela signifie qu’il faudra toujours dans vos missions/addons vérifier ou se trouve la déclaration de la classe RscTitles { } pour y ajouter l’ensemble de vos déclarations d’interface HUD.

Par convention, le nommage des interfaces HUD est Rscquelquechose, Rsc signifiant ressources. Tu peux accéder à l’ensemble des déclarations des Rsc dans le jeu de base via l’éditeur, dans le viewer de config.

La déclaration des interfaces HUD se fait en utilisant les mêmes propriétés. Il faudra également ajouter dans ta classe la propriété duration qui correspond au temps d’affichage en secondes, et en le positionnant à 999999, si tu veux que l’affichage soit permanent.

Les interfaces HUD utilisent les même contrôles que les interfaces de type Dialog (hormis les contrôles interactifs, exemple boutons, checkbox, etc.)

Ce qui change également est la façon de les afficher, et d’arrêter leur affichage.

Pour afficher une interface de type HUD, on utilisera les commandes titleRsc ou cutRsc associées au nom de l’interface. Ici, seul la commande cutRsc nous intéresse. La syntaxe de cette commande est la suivante:

layerNumber cutRsc [resourceClass, behaviour, durationMultiplier, showOverMap];

layerNumber– le numéro de calque n’est pas obligatoire. Par contre, si tu utilises consécutivement 2 commandes cutRsc avec le même numéro de calque, la deuxième commande écrasera la première.

resourceClass: c’est le nom de ta classe.

behaviour peut avoir plusieurs valeurs:

  • « PLAIN » – affichage par défaut
  • « PLAIN DOWN » – comme l’affichage plain
  • « BLACK » – L’écran devient noir avec un fondu, et reste noir tant qu’un calque ne le remplace pas
  • « BLACK FADED » – L’écran devient noir avec un fondu, et reste noir pour le temps défini dans la configuration de la classe, puis revient à la normal avec un fondu.
  • « BLACK OUT » – Même chose que « BLACK »
  • « BLACK IN » – Même chose que « BLACK FADED » avec une durée de 0s
  • « WHITE OUT » – Même chose que « BLACK OUT » mais avec du blanc
  • « WHITE IN » – Même chose que « BLACK IN » mais avec du blanc

durationMultiplier paramètre non obligatoire. Temps d’affichage du HUD qui est égale à : (fondu entrée + durée + fondu de sortie) * ce paramètre. Ce paramètre a donc un impact sur le temps d’affichage du hud. 0.5 divisera par 2 le temps d’affichage, 2 multipliera par 2 le temps. 0 sera ignoré, 1 est le paramétrage par défaut.

showOverMap – paramètre non obligatoire. Si tu presses M, et affiche la carte du jeu, le hud que tu affiches sera par dessus la carte. Pour cacher le hud, il faut positionner le paramètre à false.

Arrêt d’affichage d’une interface HUD

Pour arrêter d’afficher une interface de type HUD, il suffit donc d’appeler l’affichage de la ressource « Default » sur le même calque.

Si tu veux afficher deux interfaces en même temps, tu dois donc les mettre sur des calques différents. Plus le numéro de calque est grand, plus il sera par dessus les autres.

Les interfaces de type HUD sont détruites lors de l’apparition de la fenêtre de retour au menu Arma. Il faut donc penser à les faire réapparaitre dès que le jeu repart.

Référence des interfaces de type Hud

Une autre chose également à savoir concernant les interfaces HUD est qu’il n’est pas possible de les récupérer par la suite en utilisant la commande findDisplay. La référence du HUD doit donc être stocké au moment de son chargement dans une variable, en utilisant l’event onLoad de la classe.

onLoad = "moninterface = _this select 0";

On vient de faire un petit tour d’horizon des deux types d’interface sur Arma, ce qui devrait te permettre de choisir une solution plus adaptée pour tes futures interfaces. Maintenant, nous allons survoler les derniers sujets pour terminer complètement le tour d’horizon.

Déclaration des contrôles dans tes classes

Quand tu déclares tes contrôles dans les 3 classes conteneurs, tu dois t’assurer au minimum qu’ils possèdent certaines propriétés. Si, ça n’est pas le cas, au lancement de ta mission, ARMA3 te généra une erreur t’indiquant qu’il manque la définition d’une propriété.

Liste des propriétés obligatoires:

  • nom: comme la classe, ce nom peut être ce que tu veux, mais il doit être unique
  • idc: ID control. Chaque contrôle doit avoir un idc, ou être positionné sur -1 si Arma doit les gérer automatiquement.
  • type: indique le type de contrôle qui sera utilisé: text, boutton, listbox, etc. Chaque contrôle a son propre identifiant décimal ou hexadecimal, courament les développeurs utilisent une macro qui permet de saisir du libellé texte en lieu et place de ce code.
  • style: permet de paramétrer le style du contrôle (avec ou sans bord, alignement du texte, etc.). De la même façon, les styles utilisent un identifiant décimal, hexa, ou une macro. par défaut, 0 = alignement texte à gauche
  • x: haut gauche du contrôles relatif à X
  • y: bas gauche du contrôles relatif à Y
  • w: longueur relative du contrôle
  • h: hauteur relative du contrôle
  • font: police utilisée par défaut
  • sizeEx: taille de la font
  • colorBackground: couleur de fond du contrôle {r,g,b,a}
  • colorText – couleur du contrôle {r,g,b,a}
  • text: texte ou fichier image à charger (jpg, paa)

Idd réservés par ARMA3
Par défaut, Arma3 utilise l’IDD 46 pour afficher son interface. D’autres IDD sont également reservés pour l’usage du moteur. Il faut donc s’assurer de ne pas utiliser des IDD identiques pour éviter des conflits de déclaration.

User Interface Event Handlers
Arma3 met à ta disposition un large panel d’event handlers qui permettent d’améliorer considérablement les interactions des utilisateurs avec les interfaces. Ces events handlers doivent être inséré dans ta déclaration d’interface, ou de contrôle.

Précédemment, je t’ai parlé de l’event onLoad qui permet de récupérer la référence du display d’un HUD lors de la création de l’interface. Il existe également l’event onUnload qui permet d’exécuter du code quand l’interface se ferme.

Une bonne partie des events sont associés à des contrôles interactifs et sont donc prévu pour des interfaces de type dialog. Les events permettent par exemple de gérer les interactions souris, clavier, joystick, les interactions avec les contrôles comme la pression sur les boutons, etc.

Voici un exemple qui permet de déclencher une fonction générique quand une touche est pressée.

(findDisplay 46) displayAddEventHandler ["keyDown", "_this call functionName_keyDown"];

Je t’invite à aller directement sur le BIKI pour regarder la liste complète de ces events et des paramètres pour les utiliser.

L’héritage / les feuilles de style
Arma3 permet également à travers l’héritage de définir des apparences. C’est un peu le principe des feuilles de styles WEB(..) Grâce à cela tu vas donner à tes contrôles des propriétés communes. Ce principe d’héritage permet de déclarer une apparence à un seul et unique endroit, qui sera appliquée partout.

Il est donc vivement recommandé d’utiliser les feuilles de style. Le principe est assez simple. Tu déclares une classe parente, et tu indiques ensuite à toutes tes autres classes qu’elles sont les filles de la classe parente. Une fois cela fait elles hériteront donc des mêmes propriétés.

classe parente décrivant un contrôle texte

class RscText 
{
  type = CT_STATIC;
  idc = -1;
  style = ST_LEFT;
  colorBackground[] = {0, 0, 0, 1};
  colorText[] = {1, 1, 1, 1};
  font = FontM;
  sizeEx = 0.04;
  h = 0.04;
  text = "";
};

classe fille réutilisant la classe parente

//My_BlueText hérite des propriétés de RscText
//Il est donc inutile de redéclarer les propriétés déjà déclarées
class My_BlueText : RscText 
{
  colorText[] = {0, 0, 1, 1};
  x = 0.1;
  w = 0.4;
};

Interface et fonctions Gui
Sache qu’il est également possible d’opérer un ensemble d’opération de façon dynamique durant l’exécution du programme sur tes displays et contrôles directement à travers les fonctions GUI

Ces fonctions seront très utile par exemple pour remplir des contrôles comme des listebox avec de la donnée, de changer le contenu d’un texte. Je t’invite donc à parcourir cette page.

Te voila maintenant prêt à construire tes propres interfaces sous Arma3, tu connais les différents types d’interface, le fonctionnement des contrôles, et également les feuilles de style, et tu sais qu’il existe un nombre important de fonctions pour interagir avec les interfaces à partir des scripts.

Maintenant, laisse moi te révéler un secret (..)

Il existe un premier outil disponible à partir de l’éditeur 3d d’arma en solo qui apparait quand tu presses la touche ESC qui permet d’éditer une interface graphique. Tu pourras trouver l’ensemble de la liste des contrôles sur cette page.

Mais mieux encore, il existe le GUI EDITOR qui permet à la fois de générer une interface en important tes propres styles, et de l’exporter à la fois en déclaration de classes mais aussi en code SQF objet (OOP)

Pour ainsi dire, cette dernière solution permet en l’espace de quelques minutes de générer dynamiquement des centaines voir des milliers de lignes que tu aurais du taper toi même à la main. Je te recommande donc vivement d’utiliser ces solutions pour éviter de perdre du temps, et te concentrer vraiment sur ce qui est important, ce que tu veux faire !

Cet article a été rédigé grâce à plusieurs sources d’informations, les documentations VBS, ARMA, des articles de Killzone Kid, et l’aide de Minipopov.

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google

Vous commentez à l’aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s