Sites


Utiliser Godot Game Engine 3.0

Ajouter des mécaniques en GDScript

Nous avons donc ajouté et organisé toutes les ressources que nous voulions utiliser dans l’éditeur. Mais comment faire en sorte que des choses se produises ? Comme le joueur va il interagir avec le monde ? Comment allons-nous calculer le score ? Les scripts vont nous permettre tout cela.

Pour le code, Godot utilise son propre langage : le GDScript, qui emprunte beaucoup à Python. Il est fait pour être simple à apprendre, lisible et offre de nombreuses classes prédéfinies qui accélère le temps de développement. Il n’a pas besoin d’être compilé, mais bien sûr, cela n’accélère pas le rendu mais son apprentissage. La principale optimisation est opérée au niveau du moteur et le GDScript améliore considérablement les performances par rapport à Python dans le contexte du jeu.

Pour lire ce chapitre, il est préférable d’avoir quelques bases du concept de programmation. Vous pouvez trouver quelques informations sur Python dans notre manuel.

La partie principale de la structure du code est donnée par Godot, directement. Les scriptes sont attachés aux Nodes et étendent les classes sur lesquelles ces Nodes sont basés. Nous avons simplement besoin de définir ce qui est spécifique au jeu comme les variables et les fonctions.

Variables

Il existe deux types de valeurs dans GDSript :

  1. Les constantes : Une fois définis, elle n’est plus modifiable.

  2. Les variables : Même une fois définis, sa valeur et son type sont modifiable à tout moment.

Les variables sont explicitement déclarées :

  • En haut du fichier d’un script si elles ont besoin d’être partagées dans les fonctions,

  • Dans les fonctions elles même.

Qu’importe le type de la valeur, sa portée est une donnée importante. Si elle est définie dans une fonction, elle ne pourra pas atteindre une autre fonction et s’y cantonnera. A la fin de la lecture de la fonction, la valeur n’existe donc plus. Pour la rendre atteignable n’importe quand par n’importe quelle fonction, il suffit de la définir en dehors de celles-ci. Une variable définis qui n’est pas utilisée est indiquée dans l’output d’erreur pour vous inciter à la supprimer.

Utilisez le mot clef var pour déclarer la variable. Puisque GDScript est un langage dynamique, il n’y a pas besoin de définir la nature d’une variable. Cette information sera définie lorsqu’une valeur lui sera attribuée, sinon, elle est nulle (ce qui reste un format de variable).

Var speed

Il est évidement possible de leur attribuer des valeurs dès le début :

Var speed = 100

Ou bien à n’importe quel moment dans n’importe quelle fonction :

Speed = 100

Il est également possible de la contraindre à un type de variable grâce au typage de la 3.1 :

var speed : int = 1

Cela contiendra la variable au type de variable Integer.

GDScript intègre des types de variables standards (comme les int, float, string, bool, null, array, dict…) mais également quelques types spécifiques à celui-ci pour certains calcules mathématique (comme les Rect2, Vector2, Vector3…) et quelques types relatifs au moteur de jeu (Color, Image, Object, InputEvent…). L’objectif de ce livre n’est pas de vous les décrire, vous allez pouvoir en apprendre plus sur l’aide intégrée à l’éditeur

Notez qu’il est aussi possible de définir des constantes avec le mot clef const.

Nous allons parfois avoir besoin « d’exporter » la variable d’un script. Une fois fait, la variable est visible et modifiable dans l’inspecteur. C’est une option importante que certains codeurs feraient mieux d’utiliser pour leurs variables importantes. Cette option aide les Game designer à tester certaines valeurs pour améliorer le jeu sans casser tout le code.

Export var speed = 100

Fonctions

Pour déclarer une fonction il suffi d’utiliser le mot clef func ainsi :

Func function_name() : 
# le contenue viens ici

Si vous jetez un coup d’œil à Python, vous aurez remarqué qu’il n’y à pas de def ni de self qui est considéré comme implicite. Sinon, les fonctions while et for sont identiques.

Les fonctions prédéfinis de Godot commencent avec un Under score « _ ». Par exemple _ready(), qui se joue à l’entré du script dans l’arborescence.

Func _ready() :

Evidemment, à mesure de l’étendue du script, chaque fonction est une méthode d’une classe et peut être appelée en tant que membre.

Onerady Var player = get_node(«player ») 
Player.do_this()

Donnons de la vie aux éléments avec un script

Assez parlé, voyons comment utiliser un script.

  1. Sélectionnez la racine de la scène et cliquez sur le bouton script en haut de la page de l’éditeur.
  2. Créez un script dans la fenêtre Fichier > Nouveau. Modifiez les préférences du pop-up, nommez le script et cliquez sur créer.
  3. L’éditeur de script apparait et affiche le contenue du fichier. Si le modèle par défaut est sélectionné, on peut voir l’extension Node2D (ce qui signifie que toutes les variables et les méthodes du Node2D sont utilisables dans le script) et la fonction _ready().
  4. Dans la fonction _ready(), supprimez les commentaires et le pass. Ajoutez un print(« test »). Sauvegardez le script et jouez la scène. La scène se joue et la fenêtre de Sortie Debugger reste visible. Si tout se passe bien, la Sortie devrait écrire « test » dans l’output.
  5. C’est un premier pas et on peut voir que tout fonctionne, continuons.
  6. Dans notre scène, ajoutons un Node Label. Comme vous pouvez le voir, le Label est vide… Sa valeur peut être modifiée dans l’inspecteur mais dans cet exemple, nous allons le modifier depuis un script car elle doit changer de manière dynamique dans le cours du jeu.
  7. Créons une fonction pour cela que nous appelons score. Si nous voulons qu’il soit appelé dès le début, il faut placer l’appel dans la fonction _ready() : on se retrouve avec :
func score() :
print(« lecture de la fonction score»)
func _ready()
print(« test »)score()
  1. Si on joue la scène, les messages « test » et « lecture de la fonction score » devraient apparaitre dans la Sortie. S’il y a une erreur, vous ne pouvez pas la rater ! l’éditeur de Script affiche l’erreur en rouge en précisant la ligne et le numéro de charactère. De plus, la Sortie précisera aussi les erreurs rencontrées. Dans l’exemple plus bas, Il y a une fin de ligne imprévue (unexpected End Of Line : EOL) à la ligne 8 car il manque une parenthèse.

    Pensez à bien fermer le jeu après avoir fait une erreur pour pouvoir sauvegarder.
  2. Si vous souhaitez avoir plus de messages d’erreur et mettre en pause le jeu en cours, vous pouvez placer des breakpoint. Pour cela, cliquez sur le coté gauche du numéro d’une ligne ou cliquez sur F9. Si la ligne du script est lue par le jeu, il se met en pause et permet d’observer les valeurs sorties par les print. Il est alors possible, dans la fenêtre Débogueur, de cliquer sur le bouton continuer pour reprendre l’exécution du script.
  3. On peut maintenant changer le texte du Label. Pour commencer, nous avons besoin d’accéder au Node Label. Une méthode très simple pour y parvenir est d’utiliser la méthode get_node(). Vérifions tout d’abord si le Node existe :
If (get_node(« label »))
Print(« Label found »)
Else :
Print(« No label node »)
  1. Si un Node nommé Label est trouvé, le premier message apparaitra. Sinon, modifiez le nom du Label pour que les deux noms coïncident. Enfin, nous avons besoin de savoir comment modifier le texte dans le jeu. Si vous ne savez pas quoi utiliser, servez-vous de l’outil rechercher dans l’aide pour trouver le Label. Cliquez dessus pour avoir accès à toutes les méthodes de la classe. En haut, nous avons la méthode public appelée set_text() qui semble appropriée. Cliquez dessus pour afficher les détails : « définis le texte du Label ». Utilisons-la pour modifier le texte affiché dans le Label !
if (get_node("Label")):
get_node("Label").set_text("0")
 else:
print("No label node")

Notez qu’il est également possible de modifier toutes les variables associées à un Node (mêmes celles définis depuis un script !). Par exemple, on peut voir dans l’inspecteur que le Node Label à une variable nommée text et qu’on y définis son contenue, on peut alors modifier cette variable pour changer le texte ainsi  :

if (get_node("Label")): 
get_node("Label").text = « mon texte »

Modifier le score

La prochaine étape va être de remplacer le texte par un integer (type de variable nombre entier) qui sera modifiée chaque seconde. Pour cela, il nous faut d’abord attribuer la variable score au Node racine du niveau afin de la redistribuer plus facilement à tous les autres Nodes de la scène, notamment à notre interface. Pour cela, on créer un script sur le Node2D du niveau et on définis :

var score = 0

Ensuite, on créer un chronomètre qui appel une fonction chaque seconde. Pour cela, on créer un Node Timer dans notre arborescence qu’on peut nommer score_timer. On modifie la propriété Wait Time à 1 et on coche Autostart. Dans l’onglet Noeud > Signaux, on retrouve des fonctions qui sont appelées selon diverses conditions. La première d’entre elles se nomme timeout et est appelée dès que le temps du Timer arrive à 0. En cliquant dessus, on à la possibilité de connecter ce signal à un script en sélectionnant un Node qui y est rattaché, il nous suiffe alors de sélectionner le Node racine de notre scène pour intégrer au script une fonction qui est lancée dès que le chronomètre est terminé !

Ensuite, dans la fonction on_score_timer_timout() qui s’est intégrée au script, on peut incrémenter de 1 la variable score et relancer le timer en appelant la méthode start() présenta dans la documentation :

score += 1 
s_timer.start()

Maintenant, nous allons devoir récupérer cette variale depuis le Node de l’interface. Pour cela, on commence par récupérer le Node racine du niveau dans une variable :

 

onready var niveau = get_node("../")

le chemin ../ permet de revenir au Node précédent, ici en occurrence le Node précédent est le Node racine du niveau puisqu’il y est directement instancié. Ensuite, il nous suffi de mettre à jour chaque frame la variable score et de modifier le texte du Label (on pourrait à la place créer un signal personnaliser, mais la démarche est plus compliquée).

func _process(delta): 
get_node("points").text = str(niveau.score)

Dans notre interface, on va également créer un Node Label qui va s’afficher à la mort du personnage pour l’inviter à cliquer sur « r » pour relancer le jeu. Pour cela on créer une variable game_runing dans le Node racine du niveau. Cette variable va permettre de savoir si le joueur est mort ou non. À la mort du personnage, Le Node de la chauve-souris modifie la variable sur le Node racine du niveau et s’auto-détruit.

func bat_die(): 
get_node("../").game_runing = false
self.queue_free()

L’interface vérifie ensuite s’il doit afficher ou non le message.

if niveau.game_runing == false : 
get_node("reload_txt").show()

Les obstacles

passons plus rapidement sur le code pour faire apparaître les obstacles :

func _on_cloud_timer_timeout(): 
if game_runing == true :
var obs = load("res://Scene/obstacle_haut.scn").instance()
obs.set_position(Vector2(bat.position.x + 1000,rand_range(-200, 400)))
add_child(obs)
print("Obstacle spawned at ", obs.get_position())
c_timer.wait_time = rand_range(1, 5)
c_timer.start()

On commence donc, comme pour le score, par créer un Node Timer et on connecte son signal timeout() au script du niveau. On vérifie ensuite si le joueur n’est pas mort et on créer une variable qui vas contenir une instance du nuage. On définis ensuite la position de l’instance : en abscisse on récupère la position x du joueur et on y ajoute une distance dépassant la vision de la caméra, en ordonné on génère un nombre aléatoire entre -200 et 400 (correspondant aux valeurs des extrémités hautes et bases de la caméra dans notre scène). Puis on intègre l’instance à l’arborescence du jeu pour l’intégrer au niveau.

Pour finir, on génère à nouveau un nombre aléatoire pour déterminer le Wait Time du Timer et on le relance. Pour résumer, la fonction permet de générer des obstacles à une hauteur aléatoire à un intervalle de temps également aléatoire.

Il y a une erreur de communication avec le serveur Booktype. Nous ne savons pas actuellement où est le problème.

Vous devriez rafraîchir la page.