Godot - Jeu de plateformes 2D

Amélioration du déplacement

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Sommaire

Vous pouvez retrouver les autres épisodes de cette série dans le sommaire dédié.

II. Vidéo


Godot Engine - Jeu de plateformes 2D - Amélioration du déplacement


III. Résumé

Dans cet épisode, nous allons améliorer le déplacement du joueur, lui permettre de sauter et rajouter une caméra pour le suivre.

III-A. Amélioration du déplacement

Pour que le code soit plus facilement compréhensible, nous allons créer une fonction move() s'occupant du déplacement du personnage. Cette fonction prend trois paramètres :

  • la vitesse « speed » ;
  • l'accélération « acceleration » ;
  • le temps delta entre deux appels.

Le temps delta permet de connaître le temps réel écoulé entre deux appels des fonctions mettant à jour l'état du jeu. En l'utilisant, les déplacements des éléments de votre jeu auront toujours la même vitesse, quelle que soit la vitesse du PC. Pour plus de détails, suivez ce lien.

Le nouveau code résultant est le suivant :

 
Sélectionnez
extends RigidBody2D

export var player_speed = 200
export var acceleration = 5

var current_speed = Vector2(0,0)

func move(speed, acc, delta):
    current_speed.x = lerp(current_speed.x, speed, acc * delta)
    set_linear_velocity(Vector2(current_speed.x, get_linear_velocity().y))
    
func _ready():
    set_fixed_process(true)
    
func _fixed_process(delta):
    var btn_right = Input.is_action_pressed("btn_right")
    var btn_left = Input.is_action_pressed("btn_left")
    
    if btn_left:
        move(-payer_speed, acceleration, delta)
    elif btn_right:
        move(player_speed, acceleration, delta)
    else:
        move(0, acceleration, delta)

La fonction lerp() permet d'effectuer une interpolation linéaire entre deux valeurs. Ainsi, la vitesse du personnage ne passe pas de 0 à player_speed instantanément, mais suit une croissance adoucie.

Le reste du code n'est qu'une modification de ce que nous avons vu dans l'épisode précédent.

III-B. Ajouter une caméra

Godot propose un nœud (« Camera2D », car nous sommes dans un monde en 2D) pour intégrer une caméra. Il suffit de l'ajouter à la scène. En l'ajoutant comme enfant du joueur et en activant la propriété « Current » de la caméra, celle-ci va automatiquement suivre le joueur.

Vous pouvez ajuster la propriété « smoothing » pour rajouter un délai entre le joueur et la caméra.

III-C. Faire sauter le joueur

III-C-1. Idée

Pour savoir si le joueur est sur une plateforme et donc, qu'il a le droit de sauter, ou s'il est dans l'air et qu'il n'a donc pas le droit de sauter, nous utilisons un « lancer de rayon ». En simulant un rayon, nous pouvons déterminer si celui-ci rencontre un objet. Si nous lançons un court rayon vers le bas et qu'il rencontre un objet, nous pouvons considérer le joueur comme marchant sur une plateforme. S'il ne rencontre aucun objet, c'est qu'il n'y a que du vide sous le joueur et donc, qu'il ne peut pas sauter.

III-C-2. Implémentation

Dans Godot, un rayon est un nœud « RayCast2D ». Dans ses propriétés, nous devons l'activer « enabled » et augmenter sa taille afin qu'il sorte du joueur.

III-C-3. Script

III-C-3-a. Avoir accès à un nœud

Notre nœud « RayCast2D » est un enfant du « RigidBody2D » où le script est implanté. Nous devons donc récupérer un accès au nœud du lancer de rayon pour pouvoir utiliser ses fonctions. La fonction get_node() permet de récupérer un accès à un nœud spécifique en donnant le chemin menant au nœud.

III-C-3-b. Éviter de détecter la collision avec le joueur

Le rayon part du joueur et traverse une partie du joueur. La fonction is_colliding() va toujours retourner vrai, car le rayon va rencontrer au minimum un objet : le joueur. Pour que cela ne soit plus ainsi, il est possible d'ajouter une exception, indiquant que le rayon ne doit pas détecter la collision avec tel ou tel objet. La fonction est add_exception().

III-C-3-c. Code

 
Sélectionnez
extends RigidBody2D

export var player_speed = 200
export var jumpforce = 200
export var acceleration = 5
export var extra_gravity = 400

var raycast_down = null

var current_speed = Vector2(0,0)

func move(speed, acc, delta):
    current_speed.x = lerp(current_speed.x, speed, acc * delta)
    set_linear_velocity(Vector2(current_speed.x, get_linear_velocity().y))
    
func is_on_ground():
    if raycast_down.is_colliding():
        return true
    else:
        return false
    
func _ready():
    raycast_down = get_node("RayCast2D")
    raycast_down.add_exception(self)

    set_fixed_process(true)
    set_applied_force(Vector2(0, extra_gravity))
    
func _fixed_process(delta):
    var btn_right = Input.is_action_pressed("btn_right")
    var btn_left = Input.is_action_pressed("btn_left")
    var btn_jump = Input.is_action_pressed("btn_jump")
    
    if btn_left:
        move(-payer_speed, acceleration, delta)
    elif btn_right:
        move(player_speed, acceleration, delta)
    else:
        move(0, acceleration, delta)
        
    if is_on_ground():
        if btn_jump:
            set_axis_velocity(Vector2(0, -jumpforce))

III-C-3-d. Éviter que le joueur puisse appuyer continuellement sur le bouton de saut

Nous utilisons un fichier externe définissant une fonction, qui, au lieu de retourner simplement vrai ou faux suivant l'appui d'une touche, est capable de déterminer si l'appui vient d'être fait, ou s'il est relâché.

input_test.gd
Sélectionnez
### Classe pour la gestion des entrées. Retourne quatre états.
var input_name
var prev_state
var current_state
var input


var output_state
var state_old

### Récupère le nom de l'entrée et le stocke
func _init(var input_name):
    self.input_name = input_name
    
### Vérifie l'entrée et la compare aux états précédents
func check():
    input = Input.is_action_pressed(self.input_name)
    prev_state = current_state
    current_state = input
    
    state_old = output_state
    
    if not prev_state and not current_state:
        output_state = 0 ### Relâché
    if not prev_state and current_state:
        output_state = 1 ### Vient d'être pressé
    if prev_state and current_state:
        output_state = 2 ### Pressé
    if prev_state and not current_state:
        output_state = 3 ### Vient d'être relâché
    
    return output_state

Pour charger le script, vous devez utiliser la fonction preload().

Voici le code résultant de l'utilisation de input_state :

 
Sélectionnez
extends RigidBody2D

var input_states = preload("res://scripts/input_states.gd")

export var player_speed = 200
export var jumpforce = 200
export var acceleration = 5
export var extra_gravity = 400

var raycast_down = null

var current_speed = Vector2(0,0)

var btn_right = input_states.new("btn_right")
var btn_left = input_states.new("btn_left")
var btn_jump = input_states.new("btn_jump")

func move(speed, acc, delta):
    current_speed.x = lerp(current_speed.x, speed, acc * delta)
    set_linear_velocity(Vector2(current_speed.x, get_linear_velocity().y))
    
func is_on_ground():
    if raycast_down.is_colliding():
        return true
    else:
        return false
    
func _ready():
    raycast_down = get_node("RayCast2D")
    raycast_down.add_exception(self)

    set_fixed_process(true)
    set_applied_force(Vector2(0, extra_gravity))
    
func _fixed_process(delta):
    if btn_left.check() == 2:
        move(-payer_speed, acceleration, delta)
    elif btn_right.check() == 2:
        move(player_speed, acceleration, delta)
    else:
        move(0, acceleration, delta)
        
    if is_on_ground():
        if btn_jump.check() == 1:
            set_axis_velocity(Vector2(0, -jumpforce))

IV. Commenter

Vous pouvez commenter et donner vos avis dans la discussion associée sur le forum.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2015 Andreas Esau. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.