Sommaire
Intro Objectifs Ressources et liens
Le moteur Godot L'interface Le language GDscript
Premiers pas Creer un projet Scène principale Un joueur moche Premier lancement
Joueur Scene joueur.tscn Scene enfant Votre premier script Joueur.gd Un joueur qui tombe... ...Et remonte Second lancement (ça ressembles plus à un jeu)
Variables Global Sigleton Globals.gd Mise à jour du script Joueur.gd
Menu Pricipal Scene MenuPrincipal.tscn Connecter un signal Passer d'une scene à une autre
Obstacles Scene Obstacle.tscn Instancier un obstacle Deplacer un obstacle Instancier DES obstacles Varier la hauteur des obstacles
Detecter une colision HitBox du joueur HitBox d'un obstacle Signal entre le joueur et un obstacle
Game Over Scene GameOver.tscn Bouton rejouer et quitter
Intro
Objectifs
Le but ici est de se familiarisé avec moteur godot. Pour ça, nous allons creer un petit jeu simple, qui ressemblera à ceci :
[ESPACE] pour sauter
Ressources et Liens
Un peu de mise en place. Commencez par telecharger Godot :
Le Moteur Godot
L'interface
Petit tour de l'interface.
Pour commencer, sur la gauche, on trouve la hiérachie de la scene, pour l'instant les seules options sont de creer de nouveaux noeuds. Mais ça chagera par la suite.
Dans l'onglet Importation, c'est ici qu'on va gerer l'importation de nos assets.
En bas à gauche, l'explorateur du projet. Nos dossier scripts, ect, ect...
Au centre, le viewport. C'est ici qu'on va éditer les scènes de notre jeu
Sur la droite, lorsque l'on va cliquer sur un object de notre jeu, le Moteur va nous afficher les option liée à l'objet ici.
Puis enfin, dans l'onglet signals, on va pouvoir Connecter les signaux d'un object à nos scripts.
Le language GDscript
Pour pouvoir définir des comportements sur nos objets, on va utiliser le language GD script. La synthaxe ressembles un peu à un mélange entre du python et du javascript.
Pour creer un attribut on utilise : var jump_force = 0.0 Et pour définir une fonction : func ma_fontion():
Premiers pas
Créer un projet
Vous avez maintenant votre executable de Godot, lancer le. Vous devriez voir cette fenetre :
Cliquez sur
pour creer un nouveau projet.
Appelez le "FlappyBird", et selectionnez un dossier vide dans lequel sera
creer le porjet du jeu.
Le choix du moteur ici n'a pas vraiment d'importance. Laissez l'option de base avec OpenGL 3.0 .
Vous devriez avoir plus oou moins ces option la :
Cliquez ensuite sur :
ce qui vous ouvre l'éditeur sur votre projet.
Vous êtes normalement maintenant dans l'éditeur et devriez voir ça :
Scène principale
Parfait, on va pouvoir modéliser notre scène principale.
Pour un flappy bird, ce n'est pas trop complexe, on veut juste notre joueur (aka l'oieau),
et des tuyaux en guise d'obstacles.
Commencez par creer un noeud racine.
On fait un jeu en 2d, du coup un va utiliser un Noeud2D.
Cliquez sur "Scène 2D" pour creer votre scène :
La vue du viewport a changer pour montrer un éditeur 2d, et la hiérachie de la scene
a changée pour afficher le noeud racine de la scène.
Renomez le noeud racine "Node2D" en "MainScene".
Une fois ceci fait sauvegardez votre scène (Ctrl + S) ou [Scène][Sauvegarder],
dans le nouveau dossier ../Scenes/MainScene.
Votre projet doit ressembler à ça :
Un joueur moche
Bien, nous avons mis en place la scene principale, ajoutons un joueur, puis nous lancerons notre jeu.
Ajoutons un noeud enfant à "MainScene" avec le bouton :
On va chercher "Node2D", car notre joueur est un objet qui évolue en 2d.
Renomez ce noeud "Node2D" en "Joueur", puis vu qu'il a besoin d'une représentaion
visuelle, ajoutez comme noeud enfant au joueur, un noeud de type :
(Utilisez la bare de recherche, car il existe une tone de noeud)
Il faut toutefois ajouter une image au Sprite, pour que l'on puisse voir quelque chose.
Cliquez sur le noeud "Sprite" et regardez l'onglet de l'inspecteur à droite.
Pour ajouter un visuel à votre joueur, faite glisser une image dans le paramètre "Texture" :
Faites glisser votre joueur au millieu de l'écran du jeu (il est représenter par un rectangle bleu pale).
Et sauvegardez votre scene avec les dernier changements.
Premier lancement
Normalement vous devriez avoir quelque chose comme ceci :
Vous etes fin pret à lancer votre jeu.
cliquez sur :
en haut à droite, selectionnez "MainScene.tscn" comme scène principale et ...
Bravo, votre jeu se lance. Mais rien ne se passe encore...
En même temps, nous n'avons rien coder...
Joueur
Scene joueur.tscn
Fort sympatique ce jeu ou rien ne se passe, et où tout est définit dans une seul scène, non ?
Il est temps de faire bouger ce joueur. Mais tout d'abord, on va lui donner ça propre scène.
Supprimez le noeud "Joueur" et son sprite dans la MainScene.
Puis creez une nouvelle scène avec l'icone + au dessus du viewport :
cette scène sera en 2d, donc cliquez sur "Creer scene 2D".
Renomez le noeud racine en "Joueur".
Ajoutez un noeud enfant de type [Sprite] à "Joueur", et donnez lui une texture pour qu'il s'affiche.
Sauvegardez la Scène, dans ../Scenes/Joueur/Joueur.tscn.
vous devriez avoir ça pour la scène du Joueur :
(Faite attention à ce que le joueur soit bien au cordonnées (0, 0), pas besoin de le mettre au centre de l'ecran cette fois.)
Scene enfant
la scène principale est un peu vide maintenant. Pourtant c'est bien là que notre joueur va exister.
Il faut donc instancier la scène du joueur dans la MainScene.
Pour ça, allez sur votre scène principale et cliquez sur : .
Puis ajoutez la scène du joueur :
Re-déplacez le jouer au centre de l'écran, et lancer votre jeu pour voir si votre joueur existe dans la scène principale.
Votre premier script Joueur.gd
Il est enfin temps de coder ! Pour pouvoir ajouter des comportement à notre joueur, on va devoir lui associer un script.
Retournez dans la scène du Joueur.
Avec le noeud racine de la scène Joueur, cliquez sur :
Un petit, menu va pop, dans l'option chemin, nommez le script "Joueur.gd" et mettez le dans le même dossier que la scène du joueur.
Laissez les autres option par défaut.
Vous allez remarquer que votre viewport, laisse place à un éditeur affichant votre nouveau script.
Il est enfin temps de coder.
Un joueur qui tombe...
On veut faire en sorte que notre jouer tombe.
Ce qui veut dire augmenter la valeur de sa cordonnées Y.
Et ça, à chaque nouvelle image /frame du jeu.
Décommentez les lignes qui de la fonction func _process(delta)
Ajoutez ces ligne, de façon à ce que la function _process ressemble à ceci:
func _process(delta):
position.y += 200.0 * delta
Ou position est la cordonnées en x et y du joueur.
position.y la cordonée en y du joueur.
200.0 la force de la chute en pixel/seconde.
delta le teps qui s'est écoulé depuis la dernière image rendu du jeu e appel de cette fonction.
Lancez vote jeu, et vous verrez que le jouer tombe...
... à l'infinie (oups). Patchons ça.
2 façon sont possibles :
soit :
func _process(delta):
position.y += 200.0 * delta
# si on sort du cadre.
if position.y > 500.0:
position.y = 500.0
Ou :
func _process(delta):
position.y += 200.0 * delta
position.y = min(500.0, position.y) # Même effet que le if
Bien, relancez votre jeu. Normalement vous avez patch un bug, bravo.
...Et remonte
Un flappy bird où l'on ne pas faire remonter sont joueur n'est pas super fun.
Faisont remonter notre joueur.
On veut qu'a chaque appuie de la bare espace, le joueur remonte un peu.
Ajoutons une action "jump" dans les controles de notre jeu.
Allez dans les paramètres du projet :
Allez dans l'onglet "Contrôles" et ajoutez l'action "jump" tapant le nom de l'action puis [ajoutez].
Scrollez en bas pour trouvez votre action et cliquez +,
puis [Touche] et appuillez sur espace.
De retour dans le script du Joueur, il nous faut creer un attribut pour representer la force
de notre saut au cours du jeu.
Creez un attribut "jump_force" de valeur 0 dans le script.
extends Node2D
var jump_force = 0.0
# ... le reste du script
Il nous faut detecter quand l'action jump est pressée. C'est ce que fait ce code :
#...
func _input(event):
if Input.is_action_just_pressed("jump"):
jump_force = 400
la fonction _input est appelée à chaque entrée de l'utilisateur. Ce qui tombe bien, on a juste à detecter si c'est l'action "jump" qui vient d'etre effectuée, et si c'est le cas, on donne une force de saut à notre joueur.
Mettons à jour la fonction _process :
Il nous faut detecter quand l'action jump est pressée. C'est ce que fait ce code :
extends Node2D
var jump_force = 0.0
func _process(delta):
position.y += (200.0 - jump_force) * delta
position.y = min(500.0, position.y)
func _input(event):
if Input.is_action_just_pressed("jump"):
jump_force = 400
Si vous testez, vous verrez que le joueur remonte ... mais ne tombe plus. Et en plus il sort du cadre !
Pas de panique, il faut juste que la force du saut décremente avec le temps. Et que nous empechions le joueur d'aller trop haut.
Ajoutez ceci à la fonction _process :
extends Node2D
var jump_force = 0.0
const MAX_HEIGHT = 500.0
const MIN_HEIGHT = 0.0
func _process(delta):
position.y += (200.0 - jump_force) * delta
position.y = min(MAX_HEIGHT, max(MIN_HEIGHT, position.y) )
jump_force -= 350 * delta
jump_force = max(0.0, jump_force)
#...
Cette fois les controlles du joueur sont terminé !!!
Second lancement (ça ressembles plus à un jeu)
Bravo, si vous en êtes là c'est que vous avez fini les controlles du joueur.
Enfin, il est sûrment possible d'améliorer ce code... de le rendre plus générique peut être ?
Variables Global
Sigleton Globals.gd
Vous avez un joueur qui tombe, et qui est soumis à la gravitée.
Cette valeur de gravitée vous l'avez écrite en dur, dans le script du Joueur,
mais il serait plus juducieu de creer une varible global que n'importe quels script peut utiliser.
Imaginez, vous décider de creer un power-up qui tombe et est soumis à la même gravitée que le joueur,
ça serait assez moche de réécrire en brut la valeur de la gravitée.
Bref, tout ça pour dire, nous allons creer un script global qui sera accessible partout.
Creer un dossier Scripts à la racine du projet. Et faites click droit, puis
"nouveau script". Appelez le "Globals.gd".
Dans ce script, creez un attribut gravity :
# Globals.gd
extends Node
var gravity = 200.0
Retournez dans les paramètres du projet et allez dans l'onglet "Autoload".
Dans celui ci ajoutez le chemin votre script "Globals.gd" et [Ajouter].
Mise à jour du script Joueur.gd
la variable "gravity" du script "Globals.gd" est maintenant disponible partout.
extends Node2D
var jump_force = 0.0
func _process(delta):
position.y += (Globals.gravity - jump_force) * delta # var global de gravitee
position.y = min(500.0, position.y)
func _input(event):
if Input.is_action_just_pressed("jump"):
jump_force = 400
Menu Pricipal
Scene MenuPrincipal.tscn
C'est sympa si notre joueur est accueillis par un menu.
Creez, une nouvelle scène "Menu", de type Control cette fois.
Vu que c'est un morceau d'interface.
Ajoutez comme enfant un Bouton et placer le au centre.
Vous pouvez mettre du texte via l'inspecteur avec le button selectionné.
Sauvegardez votre scène sous "../Scenes/Menu/Menu.tscn".
Voilà ce que vous devez avoir :
Si vous voulez lancer votre jeu sur cette scene, il va faloir aller dans :
[projet]-[Parametre du porjet]-[General]-[Application]-[Run]
Et changer le paramètre "main scene" pour que ce soit votre menu.
Connecter un signal
Il faudrait que lrosque que le bouton soit cliquer, que nous changions de scene.
Pour ça il faut déjà etre capable d'écoute le signal du bouton lors du click.
Commencons par créer un script pour notre Menu : "Menu.gd".
Un fois avec un script, retournons sur le viewport et selectionnons le button.
Vous pouvez voir à droite, dans l'onglet "Node", une liste de signaux liés au button.
Cliquez sur "pressed" :
L'éditeur va nous demander de connecter ce signal à une fonction dans un script.
ça tombe bien on a un script pour notre menu, on va donc clicker sur [connecter] :
On a maintenant la fontion func _on_Button_pressed() dans le script Menu.gd, qui
sera appelée quand on clique sur le bouton "jouer".
Passer d'une scene à une autre
On a une méthode dans qi est appelée quand on clique sur jouer, maintenant la question est de savoir quoi metttre
dedant.
Pour changer la scene courante en godot c'est très simple on utilise :
get_tree().change_scene("chemins/vers/scene.tscn")
Mettez à jour le script "Menu.gd" de façon a changer de scène :
# Menu.gd
extends Control
func _on_Button_pressed():
get_tree().change_scene("res://Scenes/MainScene/MainScene.tscn")
Le jeu se lance maintenant quand on clique sur "jouer"
Obstacles
Scene Obstacle.tscn
Vous commencez à etre chaud en Godot. C'est bien.
Il est temps d'ajouter des obstacles à notre flappy bird.
Creez une Scene Obstacle, mais cette fois, pas de type Node2D, non.
Cliquez sur :
Et cherchez "StaticBody2D", puis creer votre scène.
Sauvegardez la, dans ../Scene/Obstacle/Obstacle.tscn
(Vous allez avoir un petit warning mais pas d'inquiétude nous allons nous en occuper plus tard.)
Comme pour le joueur, il nous faut un peu de visuel, ajouter 2 sprite pour faire les tuyaux du haut et du bas.
Faites resembler votre scene à quelquechose comme ça :
Instancier un obstacle
De retour dans MainScene, on va ajouter un seul obstacle pour le moment.
Comme pour le joueur, ajoutez la scene "Obstacle" en tant qu'enfant de MainScene
avec le bouton :
placez le en dehors de l'écran, de cette façon :
Deplacer un obstacle
l'obstacle doit maintenant aller de la droite vers la gauche. Car c'est "plus simple" ici de faire bouger les obstacles vers le joueur et non l'inverse.
On va avoir besoin d'un script pour nos obstacle.
Dans la scène Obstacle, ajouter un script "Obstacle.gd".
On va se dire ici, que nos obstacle ont une vitesse, on ajoutera donc un attribut var speed = 300.0
pour le representer.
Et dans _process(delta) on veut changer la position horizontale le l'obstacle dans le temps.
On va mettre potion.x -= speed * delta pour faire ceci.
Instancier DES obstacles
Un obstacle, c'est sympa, mais le but est d'en éviter plusieurs, sinon le jeu ne sera pas un hit.
Dans votre MainScene, remplacez l'obstacle solitaire par un Node2D, que vous appellerez "SpawnObstacle".
Creerz maintenant un script pour MainScene. Dans celui ci nous allons récupperer le point de spawn,
et instancier des obstacles toutes les 2 secondes.
Déjà, nous avons besoin d'un attrubut timer var timer pour savoir combient de temps s'est écoulé depuis
le spawn d'un obstacle. Et d'un délais spawn_rate = 2.0 pour savoir quand ajouter un obstacle.
extends Node2D
var timer = 0.0
var spawn_rate = 2.0
func _process(delta):
timer += delta # on suit le temps qui passe
Bien, si notre timer a dépassé le delai, on creer un obstacle, et on l'ajoute au noeud SpawnObstacle.
Ce qui donne :
extends Node2D
var timer = 0.0
var spawn_rate = 2.0
func _process(delta):
if timer > spawn_rate:
var obstacle = load("res://Scenes/Obstacle/Obstacle.tscn").instance()
$SpawnObstacle.add_child(obstacle)
timer = 0.0 # n'oubliez pas de remetre a zero le timer
timer += delta
timer += delta
load("res://Scenes/Obstacle/Obstacle.tscn").instance() creer une instance de notre obstacle. par contre il existe en "limbo" et n'existe pas encore dans la scene du jeu.
$SpawnObstacle permet d'acceder au noeud SpawnObstacle
et .add_child(obstacle) Ajoute l'obstacle en tant qu'enfant du noeud SpawnObstacle.
Vous avez maintenant une multitude d'obstacles, gg.
Varier la hauteur des obstacles
Il faut maintenant que quand un obstacle apparaisse, qu'il se décale sur la verticale,
pour avoir une hauteur diférente des autre.
Il exsite la fonction _ready() que vous avez du voir passer.
Elle est appelée quand un noeud est ajouté dans une scène. Et permet d'ajoute des comportement à ce moment.
On va utiliser un RandomNumberGenerator mais nous allons en creer qu'un seul. Car un par obstacle est un peu lourd.
Allez dans le script "Globals.gd" pour en creer un.
Pour en creer un c'est simple, je ne sais pas comment faire...
On ne peut pas tout savoir... mais ça tombe bien Godot à une documentation accessible depuis
l'éditeur.
Dans l'éditeur de script cliquez sur :
et tappez : "RandomNumberGenerator".
Vous verrez, dans la description de la classe un exemple, ne vous genez pas et copiez alègrement les lignes de l'exemple.
De retour dans "Obstacle.gd" mettez à jour la fonction _ready() :
extends StaticBody2D
var speed = 300
func _ready():
position.y += Globals.rng.randf_range(-150.0, 150.0)
Vos obstacles on maintenant une hauteur aléatoire.
Detecter une colision
HitBox du joueur
Il faut maintenant définir une hitbox sur notre joueur, sinon on ne peut
pas detecter les obstacles, et il n'y a pas vraiment de jeu.
Pour ça, on va ajouter au joueur, un noeud enfant de type : Area2D.
Vous allez voir un warning, car notre Area2D n'a pas de forme, il faut donc lui en ajouter une.
Renomez Area2D en HitBox, ce sera plus parlant.
Et ajoutez à votre HitBox un noeud enfant de type CollisionShape2D.
Le warning de la HitBox disparait, super. Mais un autre apparait sur CollisionShape2D.
Car il n'y a toujour pas de forme.
Cliquez sur CollisionShape2D et regadez l'inspecteur à droite.
Cliquez sur la case de Shape et creer une nouvelle forme. (la notre sera ronde car l'oiseau et de forme ronde.).
Qu'importe la forme que vous avez choisie, le but est maintenat de la faire corespondre au visuel du joueur.
Re cliquez sur le menu de l'option shape, où cette fois les option liée à votre forme apparaissent.
HitBox d'un obstacle
Vous vous souvenez du warning sur notre scene Obstacle ?
Il est temps de s'en occuper.
Le problème encore une fois, et que notre obstacle n'a pas de forme.
Creer 2 noeuds enfants d'Obstacle de type CollisionShape2D, et comme pour
le joueur faites en sorte que les formes recouvrent le visuel de l'obstacle.
Vous devriez avoir quelquechose comme ça :
Signal entre le joueur et un obstacle
Maintenat, il est temps de detecter les colisions.
Un peu comme le button, on va connecter un signal de la HitBox du joueur (anciennement "Area2D").
Dans la scène Joueur, cliquez sur HitBox, et allez dans l'onglet Node/Signaux à droite.
On trouve ici le signal body_entered(body).
Connectez le au script du joueur en cliquant dessus.
Ajoutez print("ouille !") dans la fonction _on_Hitbox_body_entered(body),
pour que le joueur souffre au contact d'un obstacle.
Lancer votre jeu, et vous verrez dans terminal de sortie safficher "ouille !" a chaque
obstacle rencontrer.
Game Over
Scene GameOver.tscn
Comme pour le menu, creer une nouvelle scene de type Control et
Sauvegardez la sous ../Scenes/GameOver/GameOver.tscn
Ajoutez un noeud de type Label où vous vous moquerez du joueur vennat de perdre.
Ajoutez 2 bouttons un pour rejouer et l'autre pour quitter
Appelez les : ButtonRejouer et ButtonQuit
Votre menu ressemble à ça normalement :
Je vous propose de varier et de connecter nos bouton sans l'éditeur.
Creez un script GameOver.gd
Bouton rejouer et quitter
Pour ecouter nos buttons, on va les connecter avec la fonction connect
lorque le menu est chargé ( dans la fonction _ready() ).
Le script GameOver.gd va ressembler à ceci :
extends Control
func _ready():
$ButtonRejouer.connect("pressed", self, "_on_btn_rejouer_pressed")
$ButtonQuit.connect("pressed", self, "_on_btn_quit_pressed")
func _on_btn_rejouer_pressed():
get_tree().change_scene("res://Scenes/MainScene/MainScene.tscn")
func _on_btn_quit_pressed():
get_tree().quit()
Il reste juste à mettre à jour le script Joueur.gd avec la ligne : get_tree().change_scene("res://Scenes/GameOver/GameOver.tscn")
Pour afficher le game over lorsque le joueur touche un obstacle.