Retour au projet Project Elyria

Architecture d'un système d'interface utilisateur

Publié le 22 août 2026

Projet Elyria : Architecture d'un système d'interface utilisateur (UI) déclaratif en C#

Dans le développement de moteurs de jeu personnalisés, l'interface utilisateur est souvent le composant le plus sous-estimé et le plus complexe à implémenter. Afficher un modèle 3D est une chose ; gérer des calques superposés, des événements de clic qui ne traversent pas l'écran, et des mises en page dynamiques en est une autre.

Pour Projet Elyria, j'ai refusé l'approche des interfaces codées "en dur". J'ai conçu un système d'UI déclaratif, fortement inspiré des technologies web, mais accéléré matériellement via l'API graphique Veldrid.

Le fonctionnement repose sur un parseur XML personnalisé. L'interface est décrite dans des fichiers XML (utilisant des balises comme VBox, HBox, Button ou ScrollView), que le moteur lit au démarrage pour générer un arbre DOM (Document Object Model) manipulable en C#. Cette architecture offre une séparation nette entre le design de l'interface et la logique de jeu.

Le véritable défi technique a résidé dans le système d'événements et le rendu. J'ai dû implémenter un mécanisme de propagation (Bubbling) pour m'assurer qu'un clic sur une fenêtre d'inventaire ne déclenche pas le déplacement du personnage situé derrière. Côté GPU, la gestion des listes défilantes (ScrollView) m'a poussé à utiliser le Scissor Test pour masquer matériellement les éléments qui débordent de leur conteneur. Construire ce système de zéro a été un défi d'ingénierie majeur, mais il offre aujourd'hui à Elyria une flexibilité de développement digne des moteurs modernes.

Exemple de code XML

<UI>
    <Panel Width="800" Height="600" BackgroundColor="#222222">
        <Text X="50" Y="50" Color="#FFD700">Project Elyria - Menu</Text>

        <ScrollView X="50" Y="100" Width="300" Height="200" BackgroundColor="#1A1A1A" ScrollSpeed="40" Direction="Vertical" ShowScrollbars="true" ScrollbarColor="#00FF00" ScrollbarThickness="10">

            <VBox Spacing="15" Padding="10">

                <Button Id="BtnNewGame" Width="200" Height="40" BackgroundColor="#8B0000" HoverColor="#FF0000" PressedColor="#4B0000">
                    <Text X="45" Y="10" Color="#FFFFFF">Nouvelle Partie</Text>
                </Button>

                <ProgressBar Width="200" Height="20" Value="0.75" BackgroundColor="#111111" FillColor="#00FF00" />

                <HBox Width="200" Height="24" Spacing="10">
                    <Checkbox Id="ChkFullscreen" Width="24" Height="24" BackgroundColor="#333333" CheckColor="#00FF00" IsChecked="true" />
                    <Text Y="2" Color="#FFFFFF">Plein écran</Text>
                </HBox>

                <Text Color="#FFFFFF">Volume Audio</Text>
                <Slider Id="SldVolume" Width="200" Height="24" BackgroundColor="#111111" HandleColor="#AAAAAA" MinValue="0" MaxValue="100" Value="50" />

                <Text Color="#FFFFFF">Nom du Joueur</Text>
                <Input Id="TxtPlayerName" Width="200" Height="32" BackgroundColor="#333333" TextColor="#FFFFFF" Placeholder="Pseudo..." MaxCharacters="16" />
            </VBox>
        </ScrollView>

        <Image X="450" Y="50" Width="300" Height="300" Src="Game/Data/Textures/container.jpg" TintColor="#888888" />

        <Text X="50" Y="330" Color="#FFD700">Sélection de Classe</Text>

        <ScrollView X="50" Y="360" Width="700" Height="130" BackgroundColor="#1A1A1A" ScrollSpeed="40" Direction="Horizontal" ShowScrollbars="true" ScrollbarColor="#FF8800" ScrollbarThickness="6">

            <HBox Spacing="15" Padding="10">

                <Button Width="150" Height="100" BackgroundColor="#333333" HoverColor="#444444" PressedColor="#222222">
                    <Text X="45" Y="40" Color="#FFFFFF">Guerrier</Text>
                </Button>

                <Button Width="150" Height="100" BackgroundColor="#333333" HoverColor="#444444" PressedColor="#222222">
                    <Text X="55" Y="40" Color="#FFFFFF">Mage</Text>
                </Button>

                <Button Width="150" Height="100" BackgroundColor="#333333" HoverColor="#444444" PressedColor="#222222">
                    <Text X="50" Y="40" Color="#FFFFFF">Voleur</Text>
                </Button>

                <Button Width="150" Height="100" BackgroundColor="#333333" HoverColor="#444444" PressedColor="#222222">
                    <Text X="45" Y="40" Color="#FFFFFF">Paladin</Text>
                </Button>

                <Button Width="150" Height="100" BackgroundColor="#333333" HoverColor="#444444" PressedColor="#222222">
                    <Text X="45" Y="40" Color="#FFFFFF">Archer</Text>
                </Button>

                <Button Width="150" Height="100" BackgroundColor="#333333" HoverColor="#444444" PressedColor="#222222">
                    <Text X="15" Y="40" Color="#FFFFFF">Nécromancien</Text>
                </Button>

            </HBox>
        </ScrollView>

    </Panel>
</UI>