Annexe : Interface utilisateur en Xna

Retourner au sommaire des cours  

L'interface utilisateur constitue le lien principal entre le joueur et le jeu. Dans l'univers Xna, la notion de widget est inexistante : le framework se veut générique et la notion de "bouton", de "TextBox" est inexistante sous une Xbox. Quiconque veut réaliser une interface est confronté à un dilemn : Soit il se branche au monde WinForm et profite de l'ensemble des contrôles de l'API .net Soit il réalise son propre système graphique.

Le premier cas limite le cadre d'utilisation de vos jeux à la plate-forme Windows. Elle demande la conciliation de deux univers graphique différents (Winform et Xna) et, de ce fait, peche par un manque de performances. Toutefois, cette méthode est sans aucun doute la plus rapide a mettre en place et la plus complète.

Le second cas ne souffre d'aucun de ces défaut mais demande un temps d'implémentation relativement conséquent.Le developpeur doit effectivement s'occuper du rendu de chaque contrôle, et ce, pour tous les états que ceux ci peuvent avoir, mais aussi du système événementiel et enfin du modèle objet sous jacent.Une interface puissante et customizée est à ce prix. Nous aborderons ce cas dans cette annexe. Vous pouvez vous reporter à l'annexe "Xna et Winform" pour apprendre à développer une interface utilisateur à l'aide du namespace System.Windows.Form.

Cette annexe est en cours d'écriture.

UI ou non ?

Le projet en téléchargement ici est en cours de développement et n'est pas terminé à ce jour.

Le projet explicité vise un seul but : offrir au développeur un moyen de développer une interface graphique aussi simplement que sous Windows avec .Net. Le principe est donc de pouvoir réaliser des instructions de type :

bouton = new Button();
bouton.Width *= 2;
bouton.Location = new Point(200, 200);
bouton.MouseDown += new MouseEventHandler(button_MouseDown);
bouton.Text = "Click on me";
contener.Controls.Add(bouton);

et de voir apparaitre un bouton à l'écran. Facile à dire, facile à mettre en oeuvre : c'est effectivement ce que nous ferons :).

Il y'a toutefois quelques nota bene à preciser avant de partir dans l'aventure la fleur au fusil. Sous Windows n'importe quel bouton de n'importe quelle application se ressemble, et pour cause : la notion de skin, d'univers graphique, de cohérence graphique n'a pas vraiment lieu d'être. Microsoft publie ainsi une documentation assimilable à une chartre graphique que tout développeur d'application Windows doit respecter. La chartre de Winsows Vista est accessible ici : http://msdn2.microsoft.com/en-us/library/aa511258.aspx. Comme on le voit, l'utilisateur y apprend la taille des boutons, les couleurs des messages, la façon dont il faut agencer les widgets (un widget est un élément graphique), il lui est même précisé comment afficher les messages d'erreur et d'alerte. Quel raison à tout cela ? l'UCO bien sûr. Sous cet acronyme se cache l'User Cost of OwnerShip. C'est à dire le coût d'aprehension d'une application par un utilisateur. Microsoft se base sur un principe simple : si toutes les applications respectent les mêmes normes alors l'utilisateur verra le cout de sa formation à un nouveau produit reduit. Nous nous en rendons compte tous les jours : nous voulons faire un copier collé ? Qui ne connait pas Ctrl +C / Ctrl + V ? Qui ne sait pas que ces commandes se trouvent dans le menu "Edition" ? Qui ne sait pas que dans le menu Fichier précédent ce menu se trouve la commande pour quitter l'application ? Qui ne sait pas que la barre d'état se trouve toujours en bas d'une fenêtre ? Quelqu'un a t'il déjà vu le bouton Annuler à gauche du bouton Ok dans une fenêtre de confirmation ?

Toutes ces normes sont importantes dans un environement qui se veut professionnel. Mais dans le monde du jeu video l'ambiance et l'univers d'un jeu oblige les développeur à créer des widgets en conformité avec l'ambiance que doit dégager un jeu. Notre projet devra ainsi permettre à l'utilisateur de pouvoir créer sa propre skin, de pouvoir donner à un bouton, une combo box ou un slider l'aspect qu'il désire. Enfin et pour terminer sur les contraintes, nous devrons faire en sorte que le projet sois maléable et que l'ajout de nouveaux type de Widget soit d'une grande simplicité. Inutile de passer 80% du développement d'un jeu à créer des controles et seulement 20% consacré à la réalisation du jeu en lui-même.


Imagineriez vous cet écran avec les boutons Winsows classiques ?


et celui-ci avec les boutons d'age of empire ?

 

Utilisation du projet

Jusqu'à trois projets sont nécessaires pour utiliser une interface utilisateur en Xna. Le premier projet correspond à notre API graphique. Il contient la définition des différents Widgets et la skin de base des widgets. Pour information cette skin de base est la même que le sample de l'API DirectX. Le second projet est facultatif. Il contient la définition des Widgets customisés par l'utilisateur pour son jeu ainsi que la ou les skins adaptées à l'univers du jeu développé. Le troisième projet correspond au projet principal (contenant généralement la classe héritant de Game).

Comment fonctionne le premier projet ? Il ressemble en grande partie à l'assembly System.Windows.Form (en plus simple toutefois). L'assembly repose principalement sur la classe Control dont hérite tous les Widgets. Elle contient la plupart des membres, événements et méthodes utilisés par les développeurs Winforms. La classe UIManager est responsable de la gestion de l'interface utilisateur sur la surface de rendu Xna. C'est à elle qu'on rajoute les contrôle situés sur le Z-index le plus bas. Cette classe s'occupe en interne des différents rouages qui gèrent la souris, le clavier et les interractions avec les contrôles. Le troisième élément important correspond au répertoire Configuration. Il contient le schéma définissant le fichier Xml de définition de Widget et de Skin. Il contient de même un fichier par défaut (pour l'interface et les Widgets de base). On y trouve enfin un fichier xsl. Ce fichier permet la transformation d'un fichier Xml en un ensemble de classe métier utilisable par le développeur s'occupant de l'interface graphique. Deux outils s'occupent de cette transformation : msxsl.exe et xsd.exe. Ils sont eux aussi inclus dans ce repertoire.

Fichier de configuration

Le fichier de configuration permet de réaliser des skins pour donner diverses apparences à ses objets. La définition d'un bouton se fait ainsi :

<Widget Name="Button">
<
Elements>

<
Element Identifier="BackFocused" DedicatedState="Focused" Left="0" Top="0" Width="136" Height="54" EffectName="" TextureName="GenericTexture" ColorR="255" ColorB="255" ColorG="255" ColorA="255" />
<
Element Identifier="FrontFocused" DedicatedState="Focused" Left="136" Top="0" Width="116" Height="54" EffectName="" TextureName="GenericTexture" ColorR="255" ColorB="255" ColorG="255" ColorA="255" />
<
Element Identifier="BackNormal" DedicatedState="Normal" Left="0" Top="0" Width="136" Height="54" EffectName="" TextureName="GenericTexture" ColorR="255" ColorB="255" ColorG="255" ColorA="140" />
<
Element Identifier="FrontNormal" DedicatedState="Normal" Left="136" Top="0" Width="116" Height="54" EffectName="" TextureName="GenericTexture" ColorR="255" ColorB="255" ColorG="255" ColorA="0" />
<
Element Identifier="BackMouseOver" DedicatedState="MouseOver" Left="0" Top="0" Width="136" Height="54" EffectName="" TextureName="GenericTexture" ColorR="0" ColorB="0" ColorG="0" ColorA="255" />

<Element Identifier="FrontMouseOver" DedicatedState="MouseOver" Left="136" Top="0" Width="116" Height="54" EffectName="" TextureName="GenericTexture" ColorR="255" ColorB="255" ColorG="255" ColorA="153" />

<Element Identifier="BackPressed" DedicatedState="Pressed" Left="0" Top="0" Width="136" Height="54" EffectName="" TextureName="GenericTexture" ColorR="255" ColorB="255" ColorG="255" ColorA="217" />

<Element Identifier="FrontPressed" DedicatedState="Pressed" Left="136" Top="0" Width="116" Height="54" EffectName="" TextureName="GenericTexture" ColorR="0" ColorB="0" ColorG="0" ColorA="64" />

</Elements>

<Events>

<Event State="MouseOver">

<Instructions>

<Instruction Property="OffsetX" Value="-1"></Instruction>

<Instruction Property="OffsetY" Value="-2"></Instruction>

<Instruction Property="ForeColor" Value="Black"></Instruction>

</Instructions>

</Event>

<Event State="Pressed">

<Instructions>

<Instruction Property="OffsetX" Value="1"></Instruction>

<Instruction Property="OffsetY" Value="2"></Instruction>

<Instruction Property="ForeColor" Value="White"></Instruction>

</Instructions>

</Event>

<Event State="Normal">
<
Instructions>
<
Instruction Property="ForeColor" Value="Black"></Instruction>
</
Instructions>
</
Event>
</
Events>
<Properties>
<
DefaultWidth>75</DefaultWidth>
<DefaultHeight>23</DefaultHeight>
</
Properties>
</Widget>

Désolé pour l'affichage chaotique, mais le blog ne permet plus l'ajoute de text ordonné :/

Si on analyse ce code Xml, on se rend compte que la portion de code réalise plusieurs travaux. En plus de définir un control nommé Bouton elle :

  • Créé un ensemble d'élément issu d'une texture donnant un aspect au bouton (la skin)
  • Spécifie des éléments par défaut (largeur, hauteur ...)
  • Créé des instructions qui repondent à des événements

Cette portion de Xml couplé à l'outil msxsl.exe et au fichier xsl génère une classe en sortie dans le fichier Widget.Designer.cs :

/// <summary>
/// <para>Defines a Button.</para>

/// </summary>

public partial class Button : Control

{

/// <summary>

/// <para>Instanciate a new <see cref="Button"/> object.</para>

/// </summary>

public Button()

{

this.Bounds = new Rectangle(0, 0, 75, 23);

}

public override void Update()

{

base.Update();

//Manage instructions for each events

if (GetState((int)States.MouseOver))

{

//add instructions

this.OffsetX = -1;

this.OffsetY = -2;

this.ForeColor = Color.Black;

}

if (GetState((int)States.Pressed))

{

//add instructions

this.OffsetX = 1;

this.OffsetY = 2;

this.ForeColor = Color.White;

}

if (GetState((int)States.Normal))

{

//add instructions

this.ForeColor = Color.Black;

}

}

public override void Load(Microsoft.Xna.Framework.Graphics.GraphicsDevice device)

{

base.Load(device);

//add skins

this.Elements.Add(new Element( "BackFocused", "GenericTexture", "", new Microsoft.Xna.Framework.Rectangle(0, 0, 136, 54), States.Focused, new Color((byte)255, (byte)255, (byte)255, (byte)255)));

this.Elements.Add(new Element( "FrontFocused", "GenericTexture", "", new Microsoft.Xna.Framework.Rectangle(136, 0, 116, 54), States.Focused, new Color((byte)255, (byte)255, (byte)255, (byte)255)));

this.Elements.Add(new Element( "BackNormal", "GenericTexture", "", new Microsoft.Xna.Framework.Rectangle(0, 0, 136, 54), States.Normal, new Color((byte)255, (byte)255, (byte)255, (byte)140)));

this.Elements.Add(new Element( "FrontNormal", "GenericTexture", "", new Microsoft.Xna.Framework.Rectangle(136, 0, 116, 54), States.Normal, new Color((byte)255, (byte)255, (byte)255, (byte)0)));

this.Elements.Add(new Element( "BackMouseOver", "GenericTexture", "", new Microsoft.Xna.Framework.Rectangle(0, 0, 136, 54), States.MouseOver, new Color((byte)0, (byte)0, (byte)0, (byte)255)));

this.Elements.Add(new Element( "FrontMouseOver", "GenericTexture", "", new Microsoft.Xna.Framework.Rectangle(136, 0, 116, 54), States.MouseOver, new Color((byte)255, (byte)255, (byte)255, (byte)153)));

this.Elements.Add(new Element( "BackPressed", "GenericTexture", "", new Microsoft.Xna.Framework.Rectangle(0, 0, 136, 54), States.Pressed, new Color((byte)255, (byte)255, (byte)255, (byte)217))); this.Elements.Add(new Element( "FrontPressed", "GenericTexture", "", new Microsoft.Xna.Framework.Rectangle(136, 0, 116, 54), States.Pressed, new Color((byte)0, (byte)0, (byte)0, (byte)64)));

}

}

Cette classe est générée à chaque build par l'intermédiaire d'un prebuild event ajouté aux propriétés du projet :

"$(ProjectDir)Configuration\msxsl.exe" "$(ProjectDir)Configuration\StandardUI.xml" "$(ProjectDir)Configuration\configuration.cs.xslt" -o "$(ProjectDir)Widgets.Designer.cs" 

Pour le reste une étude du code qui reste simple vaut mieux que plusieurs milliers de lignes d'explications.

Ajouter une interface graphique

Nous voilà enfin sur le terrain pour tester notre produit. L'exercice reste simple, il nous faut d'abord ajuter une référence vers le projet Arcane.Xna.Windows.Form. Puis créer dans le contreur un UIManager (le fameux manageur de controle). Ce manager se créé à l'aide d'un objet de type GameServicesContainer :

_uiContainer = new UIManager(this.Services);

 Lorsque l'objet est créé dans la méthode Initialize on le charge et on ajoute alors de la même manière qu'en Winform l'ensemble de ses contrôles :

private void InitializeUI()

{

_uiContainer.Load();

//Stream stream = Assembly.GetAssembly(typeof(Arcane.Xna.Windows.Form.Custom.Class1)).GetManifestResourceStream("Arcane.Xna.Windows.Form.Custom.Configuration.MyGameUI.xml");

//_uiContainer.LoadCustomUI(stream);

for (int i = 0; i < 15; i++)

{

Button z = new Button();z.Location = new Point(0, 23 + 23 * i);

_uiContainer.Controls.Add(z);

}

 

textBox =
new TextBox(); textBox.BackColor = Color.DarkRed;

//t.Location = new Point(300, 300);

textBox.Bounds = new Rectangle(100, 300, 200, 30);textBox.TextChanged += new EventHandler(t_TextChanged);

_uiContainer.Controls.Add(textBox);

label = new Label();

label.Bounds = new Rectangle(400, 50, 75, 30);

label.Click +=new EventHandler(label_Click);

this._uiContainer.Controls.Add(label);

this.IsMouseVisible = true;

bouton = new Button();

bouton.Width *= 2;

bouton.Location =
new Point(200, 200);

bouton.MouseDown += new MouseEventHandler(button_MouseDown);

bouton.Text = "Click on me";

_uiContainer.Controls.Add(bouton);

}

Deux étapes restent encore : l'appel à la méthode Update du manager et l'appel à la sa méthode Draw. Au final on obtient à l'affichage :

On y voit une douzaine de bouton, une texbox et un label. Une partie de ces controles interragit avec l'utilisateur par l'intermédiaire d'événements.

 

telecharger Vous pouvez télécharger le sample ici.   

 

Le projet en téléchargement est en cours de développement et peux souffrir de bugs dus à l'absence d'autres composants en cours de développement où du fait que je ne peux pas penser à tout :p

Merci néanmoins de me préciser les problèmes que vous pouvez rencontrer.

Lorsque le projet sera terminé, la présente annexe sera grandement etoffée pour expliciter au mieux ce projet.

 

 23/05/2007

 TextBox

correction bug sur l'entrée de certains caractères
Ajout des propriétés SelectionStart, SelectionLength, SelectionText
Amélioration de la selection de text
Correction bug selection de text rémanente quand on tape du texte
TextBox rendue plus indépendante de Control
Selection de texte avec Shift et fleches
Ajout propriété TextLength
Ajout propriété MaxLength
Gestion touches Home et End (avec shift ou non)

UIGénérale

correction bug sur la touche Tab pour passer d'un control à un autre (et donc Shift Tab aussi)

Control

Ajout d'events FontChanged, SizeChanged, LocationChanged

Bug trouvé à corriger

Si on clique sur un control et qu'on relache sur un autre, cet autre est selectionné

 24/05/2007

 Label

Correction d'un bug sur le backcolor
Le label prend la taille du text qu'il contient désormais

Radiobutton

Ajout d'un nouveau controle : le RadioButton


Panel

Ajout d'un nouveau controle : le panel (obligatoire pour grouper les Radio et Check Buttons par parents communs)

General

Gestion parent/enfant améliorée
gestion de position absolute / relatif

COnfiguration

Possibilité de rajouter une description aux widgets
Les effets sont optionnels
Ajout d'un nouvel etat : None

Bug :

correction d'un bug sur la selection avec *** + fleche

Bug dans la sauvegarde du state du device corrigé

 

 

 25/05/07


Checkbox

Ajout d'un nouveau controle : la Checkbox

ComboBox

Ajout d'un nouveau controle : la combobox (en cours de développement)

TextBox

Ajout de la propriété ReadOnly


Control

Ajout des méthodes protected d'accès aux events got et lost focus
Ajout d'une propriété ControlStyles pour gérer l'affichage

General

Ajout d'un nouvel evenement mouseenter et mouseleave

  

  01/06/07


UserInterfaceManager

Nouveau non de uicontainer
Hérite de GameComponent
Simplification de la syntaxe


GameCanvas

Nouveau controle Gamecanvas
Game canvas peut être focused
Game cavas repond seul à l'évent sizechanged

ListControl

Ajout d'un nouveau control : le listcontrol, parent de combobox et de listbox


ComboBox

Gestion du texte dans la combobox
ajout propriété selectedindex
ajout propriété sorted
ajout propriété index

ScrollBox

Ajout d'un nouveau control : la scrolbox


TextBox

Ne gère plus le clavier mais ecoute le keybord manageur
Ajout d'une propriété HideSelection

 

Control

Ajout des méthodes protected d'accès aux events got et lost focus
Ajout d'une propriété ControlStyles pour gérer l'affichage
Ajout d'un nouvel event : DeviceLoaded
Gestion du Visible

 

General

Ajout d'un manageur pour Keyboard et Mouse
Gestion de l'event KeyPress
Les controls sont visibles en tant que composants possibilité d'utiliser le designer de Visual Studio
Reponse à l'event check pour le checkbox afin de cacher/afficher le panel
Meilleur gestion de l'update
Gestion d'update et Render avec ou sans GameTime en parametre.


Bug

Selection a la souris impossible
Bouton cliqué sur un control et relaché en dehors donnait le focus au control sur lequel le bouton était relaché

 

 

 

Retourner au sommaire des cours 

Published Mon, May 21 2007 14:10 by valentin

Comments

# re: Annexe : Interface utilisateur en Xna

Thursday, May 24, 2007 6:45 AM by Patrick

Trés sympa ce systéme..

# re: Annexe : Interface utilisateur en Xna

Monday, May 28, 2007 5:04 AM by Patrick

C'est assez impressionnant, mais il y a un probléme avec la gestion des caractéres. Les minuscules accentuèes ne sont pas prises en compte par ton interface. Si je tape "éric" dans une zone de saisie, il s'affiche "2ric"..

# re: Annexe : Interface utilisateur en Xna

Monday, May 28, 2007 3:23 PM by valentin

il n'y a pas de caractères accentués car le fichier .spritefont ne les contient pas

donc la touche 2 c'est la touche 2 :)

tu as vu rien d'autre ?

ca a été téléchargé 100 fois déjà et personne me fait de retour ...

# re: Annexe : Interface utilisateur en Xna

Tuesday, May 29, 2007 4:51 AM by Patrick

Je vois. Il faudras plonger dans le fichier XML décrivant la police de caractères pour lui ajouter les minuscules accentuées.

Non, je n'ai rien vu d'autre. Mais j'ai juste joué quelques minutes avec le programme d'exemple, sans plus, par manque de temps. Je t'en dirais davantage quand j'aurais passé plus de temps sur la béte.

# re: Annexe : Interface utilisateur en Xna

Saturday, June 02, 2007 4:14 AM by Patrick

J'ai prévus d'ici quelques semaines, quand j'aurais du temps, de me lancer dans la création d'une petite UI pour BXOX 360 gérant la manette de jeu et le clavier.

# re: Annexe : Interface utilisateur en Xna

Sunday, June 03, 2007 5:56 AM by QuanticBlade

Bonjour, je suis impressionné par votre GUI. C'est un système extrêmement sympa, avec gestion des events à la manière WinForms (ayant plus de recul avec cette technologie, c'est un bonheur de gérer un bouton avec ce système)...

J'ai pas encore eu le temps de tester "profondément" le GUI, car en ce moment je lis votre article sur le content pipeline (j' ai écrit un p'tit prog sur l'utilisation par fichier XML des vibreurs de la manette XBox 360, et je cherchais des tutos pour le faire en temps que content).

A ce propos, le sample concernant le livre blanc sur le content pipeline n'est plus téléchargeable...

Sur ce, bonne continuation sur le GUI.

# re: Annexe : Interface utilisateur en Xna

Sunday, June 03, 2007 8:49 AM by valentin

merci bcp pour tes remarques.

Je vais regler de même le probleme du sample.

# re: Annexe : Interface utilisateur en Xna

Friday, June 22, 2007 2:34 PM by DimR0C

can it run on the xbox360?

# re: Annexe : Interface utilisateur en Xna

Friday, June 22, 2007 6:11 PM by valentin

yes

# re: Annexe : Interface utilisateur en Xna

Wednesday, July 11, 2007 1:02 PM by Shaoz

Salut Valentin,

J'ai trouvé un bug lié à la combobox:

Tu choisis Placeholder puis après tu surlignes holder  par exemple (une selection sur la fin du mot/phrase ) et tu apres tu choisis three par exemple. Visiblement il essaye de réappliquer la selection sur le texte plus court et plante logiquement

# re: Annexe : Interface utilisateur en Xna

Thursday, July 12, 2007 5:12 PM by valentin

merci !!! je regarde ça et je le corrige :)

# re: Annexe : Interface utilisateur en Xna

Friday, July 13, 2007 2:33 PM by Shaoz

Allez un autre pour la forme :p

A droite sur "My Textbox", tu selectionnes ces 2 mots, et apres tu clicks sur le bouton TextBox automaticaly resized

La selection de "My Textbox" reste présente mais il y a un "M" devant qui apparait

# re: Annexe : Interface utilisateur en Xna

Friday, July 13, 2007 3:00 PM by Shaoz

Un autre plantage :p

Tu prends le coin inférieur gauche ou droit, et tu redimensionnes au mini mini, jusqu'a pu rien voir. Ca plante !

# re: Annexe : Interface utilisateur en Xna

Friday, July 13, 2007 3:09 PM by Shaoz

Le dernier pour ce soir! :p

Tu clicks sur la liste de selection, tu passes la souris  sur un des elements pour qu'il soit surligné et apres tu agrandis directement ta fenetre => crash. Au moins sur xbox360 ça doit pas arriver je pense :p

# re: Annexe : Interface utilisateur en Xna

Monday, July 16, 2007 2:49 AM by valentin

cool tout ces retours :)

merci bcp !

# SteveKr.devBlog &raquo; GUI-Framework f??r XNA

Sunday, December 28, 2008 11:50 AM by SteveKr.devBlog » GUI-Framework f??r XNA

Pingback from  SteveKr.devBlog » GUI-Framework f??r XNA

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above:  
Powered by Community Server (Commercial Edition), by Telligent Systems