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_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 :
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 +=
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.