Godot : Listes et Boutons

De Centre de Ressources Numériques - Labomedia
Révision de 22 février 2018 à 20:16 par Serge (discussion | contributions) (Code final)

(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à : navigation, rechercher

Créer un diaporama pour se familiariser avec les listes et le système de contrôle.

Présentation

Un des attraits de Godot réside dans le système de contrôle qu'il intègre. De nombreux nœuds sont mis en place afin que l'utilisateur puisse interagir avec le programme. Boutons, sliders, entrée texte, et autres ScrollBars permettent de développer rapidement de petits utilitaires adaptés aux besoins de chacun.

Le présent tutoriel propose de créer un simple diaporama (ici plus précisément un trombinoscope en l'honneur des acteurs de votre film préféré) connecté à deux boutons afin de comprendre les interactions possibles entre le l'utilisateur et le programme.

Note sur les fichiers

Le but de cette page n'étant pas d'expliquer la manipulation des fichiers, les images qui seront utilisées seront déjà redimensionnées à la même échelle, et seront chargés manuellement. J'entends par là qu'un programme plus complexe utiliserait un système de redimensionnement automatique, et également un système d'import des fichiers, mais que pour des raisons pratiques, ce tutoriel ne couvrira pas ces deux points.

Préparation

Commencez par créer un nouveau projet, aux dimensions 600*600 et intitulé Diaporama.

Téléchargez le dossier d'image et extrayez le dans le dossier data.

Enfin, attachez un script à la scène "main".

Système d'affichage

Au poil. La première de nos opérations va être de charger chacune des images au sein du programme. La ligne de code est la suivante :

var image1 = preload("chemin/image1")

Le chemin, pour rappel, se décompose comme suit : "res://dossier1/dossier2/fichier".

Dans notre cas, la première image devrait ressembler à ça :

var image1 = preload("res://data/trombines/wayne.png")

Créez une variable pour chacune de vos idoles et le code devrait ressembler à ça :

Godot diapo a.png

Il s'agit maintenant de créer un Sprite qui va pouvoir afficher ces images. Plutôt que de créer une nouvelle scène, nous allons cette fois créer le Sprite directement via la code. Premièrement, nous allons créer une variable qui permettra de le stocker. Nous pouvons la laisser vide pour l'instant. Elle s'appellera s (pour Sprite).

Godot diapo b.png

Nous allons maintenant nous occuper de la régler. Dans la fonction _ready():, nous allons d'abord indiquer à Godot qu'il s'agit d'un Sprite, en utilisant la commande Sprite.new(). .new() permet de rajouter des nœuds à l'intérieur même du code. Nous la réutiliserons tout-à-l'heure.

Puis nous allons la positionner. Au milieu de l'écran sur l'axe des X (à 300 pixels). Sur l'axe des Y, nous souhaiterions qu'il y ait un écart de 10 pixels entre le haut de l'image et le haut de la fenêtre. Un peu de maths : les images font 400 pixels de haut. Comme l'image est centrée sur la position du Sprite, l'image commencera donc 200 pixels au-dessus de sa position, et finira 200 pixels au-dessous. En mettant le Sprite à 210 sur l'axe des Y, l'image commencera donc à 10 pixels du haut de la fenêtre. Nous pouvons donc utiliser la fonction set_pos() comme suit :

s.set_pos( Vector2( 300, 210 ) )

Ainsi, nous utilisons la fonction set_pos() de s pour le situer à la position Vector2(300,210).

Dans la même logique, nous allons pour l'instant lui attribuer l'image1 grâce à sa fonction set_texture() :

s.set_texture( image1 )

Enfin, nous l'ajoutons à la scène :

add_child( s )

Le code ressemble désormais à cela (n'oubliez pas d'indenter !) :

Godot diapo c.png

Et le programme affiche bien la trombine de l'homme le plus classe du monde :

Godot diapo wayne.png

Si vous changez la variable à l'intérieur de set_texture(), par exemple en mettant s.set_texture( image4 ), le programme change également :

Godot diapo d.png
Godot diapo gable.png

C'est grâce à ce principe que nous allons créer notre diaporama. Pour cela, nous n'allons pas directement nous servir des variables, mais les stocker dans une liste, comme ceci :

Godot diapo e.png

Une liste permet de stocker une suite de variable, puis d'y accéder en utilisant un index.

Nous créons d'abord la liste :

var liste_image = [ image1, image2, image3, image4, image5 ]

Et si nous utilisons ensuite le code :

liste_image[0]

Nous obtiendrons la première variable de la liste.

liste_image[1]

Pour la seconde. Et cætera. Attention ! L'index commence à 0, non pas à 1, de plus, une erreur se produira si vous essayez d'accéder à un index qui ne contient pas de données (par exemple, ici, liste_image[42]).

Revenons à nos moutons et essayons de modifier notre fonction set_texture() :

Godot diapo f.png
Godot diapo redford.png

Haha, sacré Robert ! Bon, plus qu'une étape avant que vous ne compreniez l'intérêt de la méthode. Créez une nouvelle variable, image_n, égale à 0, et remplacez set_texture( image_list[2] ) par set_texture( image_list[image_n] ).

Godot diapo g.png

Lancez la scène principale pour retrouver l'homme le plus classe du monde, bien vivant.

Godot diapo wayne.png

Je pense que vous avez saisi : nous n'avons maintenant plus qu'à modifier image_n pour changer l'image du Sprite. En modifiant image_n, cela modifiera également l'index que nous allons chercher en tapant liste_image[image_n], ce qui modifiera donc l'image que nous attachons au Sprite en faisant set_texture( liste_image[image_n] ). Les deux boutons ne servirons donc qu'à augmenter ou diminuer image_n de 1.

Contrôle qu'on trolle

Le système de contrôles de Godot, permettant de créer l'interface utilisateur, est extrêmement polyvalent. Pensé pour permettre d'exporter ses programmes sur différentes tailles d'écrans, il est au début assez complexe à appréhender. Le but ici est de survoler les grands principes sans rentrer dans les détails, ce qui nous noierait sous des montagnes d'explication.

Le plus simple est généralement de regrouper tous les contrôles dans une fenêtre dédiée à ceux-ci. Il existe un nœud pour ce faire : Panel. Commençons par en créer un dans la fonction _ready(): :

Godot diapo h.png

Si nous lancions la scène maintenant, il n’apparaîtrait pas, car nous ne lui avons pas assigné de dimensions. Les contrôles sont tous des rectangles, qui se positionnent à l'aide de la fonction set_margin(). set_margin() prend deux arguments, c'est-à-dire qu'il y aura deux nombres entre parenthèses : le premier définit le bord du rectangle que nous souhaitons positionner, le deuxième sa position en pixel. Le premier nombre peut être 0 pour le bord gauche, 1 pour le bord haut, 2 pour le bord droit et 3 pour le bord gauche.

Dans notre cas, nous souhaiterions aligner les bord de ce panel sur les bords de l'image. Le bord gauche de l'image est situé à 100 pixels, son bord droit à 500 pixel. Nous allons donc redimensionner le panel comme ceci :

panel.set_margin( 0, 100 )

Pour mettre le bord gauche ( 0 ) à 100 pixels.

panel.set_margin( 2, 500 )

Pour mettre le bord droit ( 2 ) à 500 pixels.

Ensuite, nous souhaiterions faire commencer le haut du panel 10 pixels en-dessous de l'image, soit à 420 pixels, et le faire finir à 10 pixels du bas de la fenêtre du programme, soit à 590 pixels.

panel.set_margin( 1, 420 )

panel.set_margin( 3, 590 )

Godot diapo i.png

Lorsque nous lançons le programme, celle-ci apparaît maintenant aux bonnes positions !

Godot diapo j.png

Nous allons maintenant rajouter un premier bouton, bouton_gauche. Nous n'allons cependant pas l'ajouter à la scène. Nous allons l'ajouter à l'intérieur de panel :

Godot diapo k.png

Lançons le programme sans avoir précisé la position du bouton.

Godot diapo l.png

Celui-ci n'apparaît pas en haut à gauche de la fenêtre, mais en haut à gauche de panel ! En effet, rajouter un contrôle à l'intérieur d'un autre contrôle rend sa position relative au contrôle auquel il a été ajouté. Plus précisément, elle la rend relative au coin haut/gauche du contrôle parent.

Premièrement, pour des raisons esthétiques, nous allons situer le bouton à 10 pixels du bord de panel, en le modifiant avec set_margin() :

bouton_gauche.set_margin( 0, 10 )

bouton_gauche.set_margin( 1, 10 )

Godot diapo m.png
Godot diapo n.png

Le bord gauche du bouton est maintenant situé à 10 pixels du bord gauche du panel, et son bord haut à 10 pixel du bord haut du panel. Bien. Maintenant, élargissons le :

bouton_gauche.set_margin( 2, 195 )

Godot diapo o.png

Le bord droit du bouton est maintenant situé à 195 pixel du bord gauche du panel.

Godot diapo p.png

Nous pourrions faire la même chose pour son bord bas mais il existe une meilleure méthode. Par défaut, les bords bas et haut d'un contrôle sont situés relativement au bord haut de leur contrôle parent. Mais la fonction set_anchor() permet de changer ce réglage :

bouton_gauche.set_anchor( 3, 1 )

Le bord bas du bouton ( 3 ) est maintenant situé relativement au bord bas du panel, selon la règle 1 de set_anchor().

bouton_gauche.set_margin( 3, 10 )

Nous mettons maintenant le bord bas du bouton ( 3 ) à 10 pixels de décalage.

Godot diapo q.png

En lançant la scène principale, on remarque que le bouton est maintenant aux bonnes dimensions ! Quel est l'intérêt de cette méthode ? Eh bien si nous changions maintenant la taille du panel, cela redimensionnerai également le bouton, puisque la taille du bouton est définie en fonction de la taille du panel !

Godot diapo r.png

Maintenant que nous avons notre bouton, il est l'heure de le connecter. Comment cela fonctionne-t-il ? Les contrôles, lorsqu'ils reçoivent une commande, émettent un signal. Dans le cas d'un bouton, il émettra le signal "button_down" lorsque l'on cliquera dessus. La ligne de commande suivante va permettre de connecter le signal "button_down" à une fonction que nous appellerons "photo_precedente" :

Godot diapo s.png

Cela signifie que lorsque bouton_gauche émettra le signal "button_down", le nœud self (qui fait référence ici à main.tscn) exécutera la fonction photo_precedente() . Que nous allons maintenant créer :

Godot diapo t.png

Comme nous l'avions dit, le but de ce bouton est de baisser image_n de 1. Il y a cependant un risque. Si image_n est à 0, elle passera à -1. Or, il n'existe pas d'index -1 dans liste_image, ce qui risque de générer une erreur. Dans le cas où image_n serait égale à 0, nous aimerions qu'elle repasse à l'index maximum de liste_image. Il existe une fonction qui permet de récupérer l'index maximum d'une list : .size() . liste_image.size() donnera un nombre égal au nombre de variable dans image_list, en partant de 1. Comme l'index part de 0, il faudra donc, si image_n est égale à 0 et que nous cliquons sur le bouton pour aller à l'image précédente, que image_n devienne égale à liste_image.size() - 1 . Dans le cas contraire, il faut baisser image_n de 1 :

Godot diapo u.png

Enfin, comme l'image a changée, il faut mettre à jour le Sprite :

Godot diapo v.png

Lancez le programme et appuyez sur le bouton : vous pouvez maintenant, dans un sens, naviguer entre les images !

Maintenant, nous allons nous occuper du deuxième bouton. Copiez-collez le premier bouton et changez le nom de la variable par bouton_droite:

Godot diapo w.png

Les bords haut et bas sont aux bonnes positions, mais les bords gauche et droit sont aux mêmes endroits que ceux du côté gauche. Pourtant, il n'est quasiment pas nécessaire de toucher aux dimensions du bouton. Plutôt que de positionner ses côtés par rapport au côté gauche du panel, nous allons les positionner par rapport au côté droit du panel :

bouton_droit.set_anchor( 0, 1 )

bouton_droit.set_anchor( 2, 1 )

Puis intervertir les valeurs de set_margin( 0, a ) et set_margin( 2, b ) :

Godot diapo x.png

Quand nous lançons le programme, les deux boutons sont correctement placés.

Godot diapo y.png

Maintenant, il s'agit de changer la connexion du bouton droit pour la fonction "photo_suivante".

Godot diapo z.png

Ensuite, copier-coller photo_precedente(), changer son nom par photo_suivante(), et inverser sa logique : si image_n est égale à liste_image.size() - 1, alors image_n = 0. Sinon, on augmente image_n de 1. Puis on met à jour l'image !

Godot diapo aa.png

Voilà, vous pouvez maintenant lancer le programme et admirer le trombinoscope de vos rêves !

Conclusion

Vous savez maintenant utiliser une liste et accéder aux éléments qui la composent. Même si ça n'a l'air de rien de prime abord, c'est un outil très puissant pour ranger les variables par catégories. Mieux encore, une liste peut elle-même contenir d'autres listes afin d'obtenir un système de classement complexe.

Nous avons aussi effleuré le concept de contrôle bien qu'il nécessite un peu de pratique pour être totalement appréhendé. Je vous conseille de faire un tour dans la documentation pour regarder les différents types de contrôles et les signaux qu'ils émettent, afin d'avoir une meilleure vision des programmes que vous pourriez mettre en place.

Sur ce, j'me taperai bien une 'ouiche.

Code final

main.gd

extends Node2D

var image1 = preload("res://data/trombines/wayne.png")
var image2 = preload("res://data/trombines/newman.png")
var image3 = preload("res://data/trombines/redford.png")
var image4 = preload("res://data/trombines/gable.png")
var image5 = preload("res://data/trombines/fonda.png")

var liste_image = [ image1, image2, image3, image4, image5 ]

var image_n = 0

var s

func _ready():
	s = Sprite.new()
	s.set_pos( Vector2( 300, 210 ) )
	s.set_texture( liste_image[image_n] )
	add_child( s )
	
	var panel = Panel.new()
	panel.set_margin( 0, 100 )
	panel.set_margin( 2, 500 )
	
	panel.set_margin( 1, 420 )
	panel.set_margin( 3, 590 )
	add_child(panel)
	
	var bouton_gauche = Button.new()
	bouton_gauche.set_margin( 0, 10 )
	bouton_gauche.set_margin( 1, 10 )
	bouton_gauche.set_margin( 2, 195 )
	
	bouton_gauche.set_anchor( 3, 1 )
	bouton_gauche.set_margin( 3, 10 )
	
	bouton_gauche.connect("button_down", self, "photo_precedente")
	panel.add_child( bouton_gauche )
	
	var bouton_droit = Button.new()
	
	bouton_droit.set_anchor( 0, 1 )
	bouton_droit.set_anchor( 2, 1 )
	
	bouton_droit.set_margin( 0, 195 )
	bouton_droit.set_margin( 1, 10 )
	bouton_droit.set_margin( 2, 10 )
	
	bouton_droit.set_anchor( 3, 1 )
	bouton_droit.set_margin( 3, 10 )
	
	bouton_droit.connect("button_down", self, "photo_suivante")
	panel.add_child( bouton_droit )

func photo_precedente():
	if image_n == 0:
		image_n = liste_image.size()-1
	else:
		image_n -= 1
	s.set_texture( liste_image[image_n] )

func photo_suivante():
	if image_n == liste_image.size()-1:
		image_n = 0
	else:
		image_n += 1
	s.set_texture( liste_image[image_n] )