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 :
    PLAYERSTATE_PREV = PLAYERSTATE
    PLAYERSTATE = PLAYERSTATE_NEXTLorsque 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 :
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 :
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 :
    if btn_jump.check() == 1 and jumping == 1:
        set_axis_velocity(Vector2(0,-jumpforce))
        jumping += 1Lors 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.





