Godot - Jeu de plateformes 2D

Machine à états

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 - Machine à états


III. Résumé

Dans cet épisode, nous implémentons une machine à états nous permettant ainsi de gérer les différents états que peut avoir le joueur (saut, marche…). Celle-ci permet de rendre le code générique et de faciliter l'ajout de nouveaux comportements tels que le double saut.

III-A. La machine à états

Nous allons faire en sorte d'intégrer une machine à états pour le joueur. Une machine à états (ou plus précisément une machine à états finis) définit un nombre d'états (si le joueur est au sol, si le joueur est en chute…). Le joueur ne peut avoir qu'un seul état à la fois. Le joueur va, suivant les actions de l'utilisateur, passer d'un état à un autre.

III-A-1. Implémentation

Pour cette implémentation de machine à états, nous utilisons trois variables :

  • état précédent ;
  • état actuel ;
  • état suivant.

Au début de la fonction _fixed_process() nous mettons à jour l'état :

 
Sélectionnez
    PLAYERSTATE_PREV = PLAYERSTATE
    PLAYERSTATE = PLAYERSTATE_NEXT

Lorsque nous devons changer d'état, il suffit de mettre l'état voulu dans la variable PLAYERSTATE_NEXT.

III-A-2. Code

Nous obtenons le code suivant, ayant le même comportement que dans le chapitre précédent, tout en intégrant la machine à états :

 
Sélectionnez
extends RigidBody2D

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

export var player_speed = 200
export var jumpforce = 2000
export var acceleration = 7
export var air_acceleration = 1
export var extra_gravity = 400

var PLAYERSTATE_PREV = ""
var PLAYERSTATE = ""
var PLAYERSTATE_NEXT = "ground"

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)
    rotate = get_node("rotate")
    
    # Initalization here
    set_fixed_process(true)
    set_applied_force(Vector2(0,extra_gravity))

func _fixed_process(delta):
    PLAYERSTATE_PREV = PLAYERSTATE
    PLAYERSTATE = PLAYERSTATE_NEXT
    
    if PLAYERSTATE == "ground":
        ground_state(delta)
    elif PLAYERSTATE == "air":
        air_state(delta)
        
        
func ground_state(delta):
    if btn_left.check() == 2:
        move(-player_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))
    else:
        PLAYERSTATE_NEXT = "air"
    

func air_state(delta):
    if btn_left.check() == 2:
        move(-player_speed, air_acceleration, delta)
    elif btn_right.check() == 2:
        move(player_speed, air_acceleration, delta)
    else:
        move(0, air_acceleration, delta)
    
    if is_on_ground():
        PLAYERSTATE_NEXT = "ground"

III-B. Mouvement gauche/droite

Nous souhaitons que le joueur tourne suivant la direction où il va.

Pour que la rotation soit visible, nous rajoutons un sprite. Pour effectuer la rotation, nous modifions la propriété « scale ».

L'implémentation est similaire à celle de la machine à états :

 
Sélectionnez
extends RigidBody2D

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

export var player_speed = 200
export var jumpforce = 2000
export var acceleration = 7
export var air_acceleration = 1
export var extra_gravity = 400

var PLAYERSTATE_PREV = ""
var PLAYERSTATE = ""
var PLAYERSTATE_NEXT = "ground"

var ORIENTATION_PREV = ""
var ORIENTATION = ""
var ORIENTATION_NEXT = "right"

var raycast_down = null

var current_speed = Vector2(0,0)

var rotate = null

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)
    rotate = get_node("rotate")
    
    # Initalization here
    set_fixed_process(true)
    set_applied_force(Vector2(0,extra_gravity))


func rotate_behavior():
    if ORIENTATION == "right" and ORIENTATION_NEXT == "left":
        rotate.set_scale(rotate.get_scale() * Vector2(-1,1))
    elif ORIENTATION == "left" and ORIENTATION_NEXT == "right":
        rotate.set_scale(rotate.get_scale() * Vector2(-1,1))
        


func _fixed_process(delta):
    
    PLAYERSTATE_PREV = PLAYERSTATE
    PLAYERSTATE = PLAYERSTATE_NEXT
    
    ORIENTATION_PREV = ORIENTATION
    ORIENTATION = ORIENTATION_NEXT
    

    if PLAYERSTATE == "ground":
        ground_state(delta)
    elif PLAYERSTATE == "air":
        air_state(delta)
        
        
func ground_state(delta):

    if btn_left.check() == 2:
        move(-player_speed, acceleration, delta)
        ORIENTATION_NEXT = "left"
    elif btn_right.check() == 2:
        move(player_speed, acceleration, delta)
        ORIENTATION_NEXT = "right"
    else:
        move(0, acceleration, delta)
    
    rotate_behavior()
    
    if is_on_ground():
        if btn_jump.check() == 1:
            set_axis_velocity(Vector2(0,-jumpforce))
    else:
        PLAYERSTATE_NEXT = "air"
    

func air_state(delta):
    

    if btn_left.check() == 2:
        move(-player_speed, air_acceleration, delta)
        ORIENTATION_NEXT = "left"
    elif btn_right.check() == 2:
        move(player_speed, air_acceleration, delta)
        ORIENTATION_NEXT = "right"
    else:
        move(0, air_acceleration, delta)
    
    rotate_behavior()
    if is_on_ground():
        PLAYERSTATE_NEXT = "ground"

III-C. Double saut

Pour le double saut, nous rajoutons un compteur de saut qui est incrémenté à chaque saut. Dans l'état en l'air du joueur, suivant la valeur du compteur, nous pouvons à nouveau sauter :

 
Sélectionnez
    if btn_jump.check() == 1 and jumping == 1:
        set_axis_velocity(Vector2(0,-jumpforce))
        jumping += 1

Lors du premier saut, dans l'état « au sol », le compteur est mis à 1.

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.