IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

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 ni 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.