Logo Makeyourgame
Accueil Les formations Créer un jeu Les ateliers Les tutoriels Blog
Logo Makeyourgame
Les formations Les financements Les exercices La certification Unity
Logo Makeyourgame
Mes objectifs Les jeux Les ressources Le glossaire
Logo Makeyourgame
Les tutos Les tutos Youtube

Roll The Ball 3D avec Godot Engine (partie II) : Godot Engine

pictureProfil_cevantime
Ecrit par cevantime
Catégorie Godot Engine
Mis à jour le 12/09/2020
Roll The Ball 3D avec Godot Engine (partie II)
Dans le cours précédent, nous avions mis en place les différents éléments de notre scène (sol, murs, balle et collectibles). Dans ce cours, nous nous consacrerons principalement au scripting des différents éléments :
  • Contrôle de la balle
  • Contrôle de la caméra
  • Disparition des collectibles et mise à jour du score.
15 min Débutant
Dans le [course=142]cours précédent[/course], nous avions mis en place les différents éléments de notre scène (sol, murs, balle et collectibles). Dans ce cours, nous nous consacrerons principalement au scripting des différents éléments :
  • Contrôle de la balle
  • Contrôle de la caméra
  • Disparition des collectibles et mise à jour du score.

Contrôle de la balle

Nous allons voir dans cette partie comment se passe le contrôle d'un RigidBody avec Godot. D'une manière générale, la récupération des entrées utilisateurs (nos inputs) se fait à chaque frame (60 fois par seconde pour un FPS de 60). Godot fournit un certain nombre de fonctions qui seront appelées sur chacun des noeuds à chaque nouvelle frame. Ces fonctions sont :
  • _integrate_forces(state) : à utiliser pour le contrôle des RigidBody. Godot peut-être amené à lancer plusieurs fil d'exécutions pour optimiser les calculs liés au moteur physique. Ceci explique que cette fonction soit la seule fonction sûre pour le contrôle des éléments dépendant directement du moteur physique.
  • _physics_process(delta) : à utiliser pour le contrôle des KinematicBody. Le taux de rafraichissement dépend du FPS, lequel peut-être amené à varier. Néanmoins, les Kinematics ont besoin d'inscrire leur logique de déplacement sur une période indépendante du FPS.
  • _process(delta) : Certaines opérations ont besoin d'être effectuées aussi souvent que possible (à chaque frame). Dans ce cas, c'est cette fonction qui doit-être utilisée.
Notre balle étant un RigidBody, La fonction à utiliser est _integrate_forces(state) . Ajoutons un script à notre balle (Ball.gd) et récupérons les inputs utilisateurs.
extends RigidBody


func _integrate_forces(state):
   var input_h = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
   var input_v = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
Ici, nous soustrayons deux Input.get_action_strength ce qui a à peu près le même effet que le Input.GetAxis de Unity (les avantages de cette méthode sont exposés ici). Maintenant que nous avons récupéré nos inputs utilisateurs, nous pouvons bouger notre balle. Attention toutefois, car il n'est pas recommandé de changer directement la position de notre balle puisque il s'agit d'un RigidBody. Pour contrôler notre balle, nous devrons lui appliquer des forces, des torques , des impulsions ou encore modifier se vélocité. Ceci vaut aussi pour la 2D. Dans le cas présent, nous allons simplement appliquer une force au centre de la balle, laquelle sera proportionnelle aux inputs de notre utilisateur. Le code résultant est très simple :
extends RigidBody


func _integrate_forces(state):
   var input_h = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
   var input_v = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
   var force = Vector3(input_h, 0, input_v)
   add_central_force(force.normalized())
Nous créons un vecteur force à partir de nos inputs (avec uniquement une composante x et une composante z) et nous appliquons cette force au centre de la balle. Remarquez que nous normalisons ce vecteur force avant de l'appliquer à notre balle ( .normalized() ) afin de garantir que notre vecteur sera toujours de longueur 1. Dernier ajustement, nous allons faire en sorte de pouvoir régler la vitesse de notre balle directement depuis l'éditeur en exportant une variable speed dans notre script et en la multipliant à notre force.
extends RigidBody

export(float,1, 20) var speed = 5

func _integrate_forces(state):
   var input_h = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
   var input_v = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
   var force = Vector3(input_h, 0, input_v)
   add_central_force(force.normalized() * speed) # NE PAS OUBLIER DE MULTIPLIER PAR SPEED !!
Ce sera tout pour notre balle !

Ajout de la caméra

Si vous lancez votre scène level à ce stade, vous vous retrouverez devant une belle fenêtre grise sans nulle trace de votre Level. Ceci est dû au fait que votre scène 3D doit impérativement avoir une caméra parmi ses enfants. Ajoutons-la à notre level et déplaçons-la un peu de façon à la dissocier de notre balle ! [keynotion]Une astuce pratique pour contrôler le point de vue de notre caméra consiste à sélectionner la caméra dans notre hiérarchie, puis à diviser notre vue en deux en cliquant sur https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806153332-5f4f4f6d511c4.png puis Two viewports, et enfin à cocher la case https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806153621-5f4f4f74d4be9.png dans le viewport où vous souhaitez voir le point de vue de votre caméra ![/keynotion] Nous obtenons quelque chose comme ça : https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806153910-5f4f4e6f88337.png Maintenant, quand vous lancez la scène et que vous contrôlez votre balle avec les flèches directionnelles du clavier (ou encore la croix de votre manette !), vous voyez bien votre balle se déplacer ! https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/peek060820201544-5f4f4e7ea08fe.gif
Si vous attendez trop longtemps, votre balle se mettra en sleep mode et ignorera les forces que nous lui appliquons. Pour éviter cela, pous pouvez décocher la propritété Can Sleep de votre Ball.
Pour éviter que la balle sorte du champs de vision de la caméra, nous allons faire en sorte que la caméra suive le mouvement de la balle. Souvent, nous pouvons faire cela en plaçant notre caméra comme enfant de notre balle. Cette approche fonctionne très bien dans le cas où nous contrôlons un élément qui "reste bien droit" tout au long du jeu (un FPS  ou un TPS, par exemple). Malheureusement, cela ne fonctionne pas dans le cas de notre balle puisque celle-ci roule. Voyez plutôt : https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/peek060820201550-5f4f4e99cab6c.gif Dans le tutoriel Unity, le développeur choisit de résoudre ce problème grâce à un script : il récupère la différence de position entre la caméra et la balle et fait en sorte que cet écart soit maintenu tout au long du jeu. On pourrait très bien résoudre la problème de la manière en Godot mais je préfère vous montrer une autre méthode, probablement meilleure. La méthode présentée ci-dessous a l'avantage de se passer de script et de n'exploiter que les noeuds Godot. [keynotion]Lorsque cela est possible, essayez toujours de résoudre vos problèmes en utilisant les noeuds à votre disposition plutôt qu'en passant par des scripts[/keynotion] Nous allons ajouter un Spatial à notre Level (appelons le CameraRef) et placer notre caméra comme enfant de notre CameraRef. Ensuite, nous allons utiliser un noeud bien utile, appelé RemoteTransfom, lequel a la capacité de transmettre sa transform ou une partie de sa transform à un autre noeud. Ajoutez un RemoteTransform à votre Ball puis assigner sa propriété Remote Path au noeud CameraRef. Dépliez ensuite https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806160516-5f4f5053d0f99.png et décochez Rotation et Scale de façon à ce que notre RemoteTransform transmette uniquement sa position au noeud CameraRef. https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/peek060820201611-5f4f4eaf77348.gif Et voilà le travail, notre caméra suit notre balle sans que nous ayons eu à scripter quoi que ce soit ! https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/peek060820201608-5f4f4ea67fd87.gif

Faire disparaître les collectibles

Nous allons maintenant voir comment faire disparaître les collectibles au passage de la balle. Pour ce faire, rendons-nous dans notre scène Pick Up. Ajoutons un script à notre Pick Up (Pick Up.gd) et connectons le signal body_entered de notre noeud Pick Up avec notre script : https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/peek060820201619-5f4f4eb4f3aa7.gif Ainsi, la fonction _on_Pick_Up_body_entered(body) sera exécutée lorsque un body pénètrera dans la zone occupée par le collectible en question. Pour l'instant, nous nous contenterons de supprimer notre collectible de la scène si cela se produit :
extends Area

func _on_Pick_Up_body_entered(body):
   queue_free()
Si vous lancez votre scène maintenant, vous constaterez que la balle "ramasse" bien les collectibles! Rapide, non ?

Ajout du score

Ajout de l'UI

Comme en 2D, l'ajout d'une UI passe par l'ajout d'un CanvasLayer et de noeud héritant de Control. Ajoutez donc un CanvasLayer à votre UI et renommez-le UI. En enfant de ce CanvasLayer, nous allons mettre un Label qui contiendra notre score. Vous pouvez placer ce Label en haut à gauche de votre écran et mettre comme texte par défaut Score : 0 .

Mise à jour du score

Nous allons maintenant connecter notre UI (et donc le score qu'elle affiche) avec le ramassage de notre Pick Up. Pour cela, nous allons faire en sorte que les Pick Up émette un signal lorsqu'ils sont capturés, lequel sera transmis à notre UI. Commençons par créer un signal pour la capture au sein du script Pick Up.gd. Appelons le captured et émettons-le lorsque notre élément est capturé par notre balle.
extends Area

signal captured

func _on_Pick_Up_body_entered(body):
   queue_free()
   emit_signal("captured")
Connectons maintenant ce signal avec notre UI au sein de notre Level. Pour cela, nous devons connectez tous les Pick Up à notre UI au démarrage de notre scène Level. Nous allons devoir passer par un script pour faire cela. Attachons un script à Level (Level.gd) et commençons y coder notre fonction _ready() (laquelle s'exécute au début de la vie de notre scène, au moment où tous noeuds sont en place).
extends Spatial


func _ready():
   pass
Il existe plusieurs manière de récupérer tous les Pick Up de notre scène depuis ce Script. Une façon de faire assez propre est d'assigner un groupe (équivalent des Tags en Unity) à nos Pick Up et de récupérer tous les éléments de ce groupe. Assignons donc le groupe "Pick Up" à notre scène Pick Up. Vous devrez pour cela retourner dans la scène Pick Up, cliquez sur l'onglet https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806172739-5f4f4ebb4f294.png puis sur https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806172837-5f4f4ebf9c4b9.png . Ajoutez ensuite le groupe Pick Up. https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/peek060820201730-5f4f4ec3eb1f6.gif Récupérons maintenant tous les Pick Up dans la méthode _ready() de Level.gd puis connectons leur signal captured avec ce script.
extends Spatial


func _ready():
   var pickups = get_tree().get_nodes_in_group("Pick Up")
   
   for pickup in pickups:
      pickup.connect("captured", self, "_on_Pick_Up_captured")
      
      
func _on_Pick_Up_captured():
   pass
Ainsi notre, fonction _on_Pick_Up_captured() s'exécutera pour chaque Pick Up capturé ! Nous pouvons maintenant ajouter un variable score à notre script, l'incrémenter à chaque fois que cette fonction est appelée et mettre à jour notre UI !
extends Spatial

var score = 0

func _ready():
   var pickups = get_tree().get_nodes_in_group("Pick Up")
   
   for pickup in pickups:
      pickup.connect("captured", self, "_on_Pick_Up_captured")
      
      
func _on_Pick_Up_captured():
   score += 1
   $UI/LabelScore.text = "Score : %s" % score

Finitions

Notre jeu est terminé dans les grandes lignes. Nous allons tout de même apporter deux améliorations. Afficher un panneau de fin lorsque tous les Pick Up ont été ramassés et ajouter une  lumière afin de rendre notre scène moins terne.

Ajout du panneau de fin de jeu

Dans notre UI, nous allons ajouter un PanelContainer contenant un label affichant simplement le texte "You win !!!". Modifiez également le https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806174825-5f4f4ec82b584.png du PanelContainer pour l'afficher au centre. https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806185133-5f4f4ecce06f4.png Ce panel doit être masqué par défaut. Renommez-le EndContainer. https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806185235-5f4f4ed12ac23.png Maintenant, nous allons faire en sorte que ce panel soit affiché lorsque nous ramassons le dernier collectible. Pour cela, nous calculons le score maximal au démarrage de notre scène principale et nous le comparons au score actuel à chaque fois qu'un Pick Up est capturé.
extends Spatial

var score = 0
var score_max

func _ready():
   var pickups = get_tree().get_nodes_in_group("Pick Up")
   
   for pickup in pickups:
      pickup.connect("captured", self, "_on_Pick_Up_captured")
      
   score_max = pickups.size()
      
func _on_Pick_Up_captured():
   score += 1
   $UI/LabelScore.text = "Score : %s" % score
   
   if score == score_max:
      $UI/EndContainer.show()
Toutes les mécaniques du jeu sont en place. Enjoy !

Ajouter une lumière

Pour l'instant, notre scène n'est éclairée que par la lumière ambiante. Afin de rendre le tout un peu plus joli, nous allons ajouter une lumière directionnelle. Ajoutez un noeud de type DirectionnalLight à votre scène et orientez-la de la manière dont vous voudrez pour obtenir le rendu qui vous satisfait ! Voici les valeurs de la transform de ma lumière : https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806191219-5f4f4ed53d978.png Vous pouvez également activer les ombres : https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806191541-5f4f4ed9d880f.png

Bonus : Régler l'anticrénelage

Vous l'avez peut-être remarqué : les bords des éléments sont peu crénelés (effet "marche d'escalier";) : https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806191829-5f4f4ee1b5660.png Vous pouvez allez dans Project/Project Settings/Rendering/Quality et activer l'anticrénelage MSAA. Plus la valeur est élevé, plus l'anti-crénelage est important. Essayez x8 , par exemple : https://makeyourgame.fun/upload/users/cevantime/RollTheBall2/capturedu20200806192102-5f4f4ee696668.png Cette simple opération rend notre jeu beaucoup agréable à regarder, non ?

Conclusion

Ceci conclut ce chapitre et ce cours initiatique sur la 3D avc Godot. Si vous souhaitez voir d'autres contenus sur le thème de la 3D avec Godot, n'hésitez pas à nous contacter !

Vues: 2489

Licence:



Validation du Tutoriel

Veuillez vous connecter ou créer un compte pour pouvoir valider ce tutoriel et ainsi gagner de l'expérience (XP) !

Vous aimerez aussi...

Blog et Tutoriels

Créer les colliders directement dans Blender 3D.

Créer les colliders directement dans Blender 3D.

par GuiRig

Pourquoi créer un collider dans Blender 3D plutôt que Unity ?

Le modeleur 3D unity reste très très limité. On ne pourra créer que des formes primitives ( cube, sphère etc )

Et puisque nous modélisons nos mesh dans Blender, autant en profiter pour superposer nos colliders. Cela s'appelle des meshColliders. Rapide et efficace :-)

applause logo0