Si le joueur appuyer sur la touche de saut un peu avant de toucher le sol, alors le personnage sautera au moment d'avoir touché le sol. ça permet au joueur de ne pas avoir totalement besoin d'être super précis sur la commande de saut pour pourtant faire bouger le personnage. Le mécanisme utilise un RayCast sous les pieds de la princesse pour savoir où se trouve le sol. Il restera à tuner cette variable dans le futur.
263 lines
8.7 KiB
GDScript
263 lines
8.7 KiB
GDScript
extends CharacterBody2D
|
|
|
|
## The player listens for input actions appended with this suffix.[br]
|
|
## Used to separate controls for multiple players in splitscreen.
|
|
@export var action_suffix := ""
|
|
|
|
var gravity: int = ProjectSettings.get("physics/2d/default_gravity")
|
|
@onready var wall_detect_left := $wall_detect_left as RayCast2D
|
|
@onready var wall_detect_right := $wall_detect_right as RayCast2D
|
|
@onready var ground_far_detect := $ground_far_detect as RayCast2D
|
|
@onready var animation := $AnimatedSprite2D as AnimatedSprite2D
|
|
@onready var camera := $Camera2D as Camera2D
|
|
|
|
|
|
@export var WALKING_SPEED = 400
|
|
@export var FALLING_SPEED = 400
|
|
@export var JUMPING_SPEED = 400
|
|
@export var X_SPEED_TABLE = [0, 0.1, 0.15, 0.2, 0.3, 0.6, 0.9, 1]
|
|
@export var X_SPEED_DECEL = [0, 0.1, 0.6, 1]
|
|
@export var FALL_SPEED_TABLE = [0, 0.1, 0.15, 0.2, 0.3, 0.6, 0.9, 1]
|
|
@export var JUMP_SPEED_TABLE = [0, 0.1, 0.2, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
|
|
@export var KICK_SPEED_TABLE = [0, 0.2, 0.4, 0.6, 1, 1.6, 2.4, 3]
|
|
|
|
# Nombre d'incrément à rajouter lorsque la touche de saut est maintenue
|
|
@export var JUMPING_COUNTER_REFILL = 2
|
|
# Tous les combien d'incréments rajouter un refill sur le compteur
|
|
@export var JUMPING_KEY_COUNTER_THRESHOLD = 3
|
|
|
|
# Nombre de frames coyote durant lesquelle le joueur peut encore sauter
|
|
# sans encore être au sol
|
|
@export var COYOTE_LENGTH := 20
|
|
|
|
# Direction dans laquelle est positioné le personnage.
|
|
var direction : int = 1
|
|
|
|
# Variables détats relatives à la marche.
|
|
var walking : bool = false
|
|
var walking_step: int = -1
|
|
@export var WALK_INCR_GROUND : int = 1
|
|
@export var WALK_INCR_AIR : int = 3
|
|
var walk_incr_reserve : int = 0
|
|
var init_decel: bool = true
|
|
var init_direction_change = true
|
|
|
|
# variables d'état relatives au saut
|
|
var jumping : bool = false
|
|
var need_jump : bool = false
|
|
var jumping_step : int = -1
|
|
var jump_key_counter : int = 0
|
|
|
|
var falling_step : int = -1
|
|
|
|
var pressing_wall = false
|
|
var grab_wall = false
|
|
|
|
|
|
const PLATFORM_LAYER = 1 << (5 - 1) # collision layer 5
|
|
const PICS_BLOCK_LAYER = 1 << (6 - 1) # collision layer 6
|
|
var layer_of_collision = null
|
|
|
|
func walk(direction:int) -> float:
|
|
# Fait marcher le personnage.
|
|
|
|
# Si le personnage est dans l'air, il aura une friction plus faible lors de
|
|
# la décélération.
|
|
var threshold = WALK_INCR_AIR
|
|
if is_on_floor():
|
|
threshold = WALK_INCR_GROUND
|
|
|
|
if is_on_wall():
|
|
walking_step = min(walking_step, 1)
|
|
|
|
# Un changement de direction implique de perdre la vélocité dans la direction
|
|
# précédente avant de repartir dans la direction que l'on veut.
|
|
# ça ne change rien pour le nombre de frames nécéssaires pour accélérer dans
|
|
# tous les cas. Mais le feeling est meilleur.
|
|
var direction_change: bool = (direction > 0 and velocity.x < 0) or (direction < 0 and velocity.x > 0)
|
|
if direction_change:
|
|
if init_direction_change:
|
|
init_direction_change = false
|
|
walking_step = -1
|
|
else:
|
|
init_direction_change = true
|
|
|
|
# Si le joueur décide de marcher alors, le compteur de pas doit s'incrémenter
|
|
# Si le joueur ne veut plus marcher, alors, le compteur de pas décrémente
|
|
# La vitese choisie est le numéro d'étape dans le tableau correspondant
|
|
# X_SPEED_TABLE pour l'accélération
|
|
# X_SPEED_DECEL pour la décélération
|
|
var table = X_SPEED_TABLE
|
|
if walking:
|
|
walking_step = min(walking_step+1, X_SPEED_TABLE.size() -1)
|
|
init_decel = true
|
|
else:
|
|
# Lors de la première frame de la décélération, initialiser la valeur
|
|
# du compteur d'incrément en haut du tableau de décélération
|
|
table = X_SPEED_DECEL
|
|
if init_decel:
|
|
walking_step = min(walking_step, X_SPEED_DECEL.size() - 1)
|
|
init_decel = false
|
|
|
|
# Si le compteur d'incrément est supérieur ou égal à zéro, c'est qu'il
|
|
# faut bouger, donc il est nécéssaire en premier temps de récupérer la vitesse
|
|
# à l'indice courant. Puis si le joueur ne veut plus accélérer, alors, appliquer
|
|
# la décélération
|
|
if walking_step >= 0:
|
|
var speed = table[walking_step] * WALKING_SPEED
|
|
if not walking:
|
|
walk_incr_reserve += 1
|
|
# appliquer le nombre de frames nécéssaire pour décrémenter, dépend
|
|
# de où est le personnage (air vs sol)
|
|
if walk_incr_reserve >= threshold:
|
|
walk_incr_reserve = 0
|
|
walking_step-=1
|
|
if direction_change and walking:
|
|
# dans le cas du changement de direction,
|
|
return speed * direction + velocity.x
|
|
else:
|
|
return speed * direction
|
|
return velocity.x
|
|
|
|
|
|
func fall() -> float:
|
|
if jumping:
|
|
falling_step = -1
|
|
return velocity.y
|
|
if is_on_floor_only():
|
|
if get_floor_normal()[0] < 0: # pente à gauche
|
|
if direction >= 0:# on va à droite, désactive la gravité
|
|
falling_step = -1
|
|
return velocity.y
|
|
else: # on va à gauche, gravité à fond
|
|
falling_step = FALL_SPEED_TABLE.size()-1
|
|
else: # pente à droite
|
|
if direction > 0:# on va à droite, active la gravité à fond
|
|
falling_step = FALL_SPEED_TABLE.size()-1
|
|
else: # on va à gauche, désactive la gravité
|
|
falling_step = -1
|
|
return velocity.y
|
|
else:
|
|
if grab_wall:
|
|
falling_step = max(falling_step-1, 1)
|
|
else:
|
|
falling_step = min(falling_step+1, FALL_SPEED_TABLE.size()-1)
|
|
return FALL_SPEED_TABLE[falling_step] * FALLING_SPEED
|
|
|
|
|
|
func jump() -> float:
|
|
if not jumping:
|
|
return velocity.y
|
|
|
|
if not is_on_ceiling() and jump_key_counter > 0 and jump_key_counter % JUMPING_KEY_COUNTER_THRESHOLD == 0:
|
|
jumping_step = min(
|
|
jumping_step + JUMPING_COUNTER_REFILL,
|
|
JUMP_SPEED_TABLE.size() -1
|
|
)
|
|
|
|
if jumping_step > 0:
|
|
jumping_step -= 1
|
|
return JUMP_SPEED_TABLE[jumping_step] * JUMPING_SPEED * -1
|
|
else:
|
|
end_jump()
|
|
return velocity.y
|
|
|
|
func end_jump():
|
|
jumping = false
|
|
jumping_step = -1
|
|
jump_key_counter = 0
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
pressing_wall = wall_detect_left.is_colliding() or wall_detect_right.is_colliding()
|
|
grab_wall = pressing_wall and Input.is_action_pressed("grab" + action_suffix)
|
|
|
|
if Input.is_action_just_pressed("jump" + action_suffix):
|
|
if ground_far_detect.is_colliding() or is_on_floor():
|
|
need_jump=true
|
|
|
|
if need_jump and is_on_floor():
|
|
need_jump = false
|
|
if not jumping:
|
|
jumping = true
|
|
jumping_step = JUMP_SPEED_TABLE.size()-1
|
|
jump_key_counter = 0
|
|
|
|
if Input.is_action_pressed("jump" + action_suffix):
|
|
if jumping:
|
|
jump_key_counter += 1
|
|
else:
|
|
jump_key_counter = 0
|
|
|
|
velocity.y = jump()
|
|
velocity.y = fall()
|
|
|
|
walking = (
|
|
Input.is_action_pressed("move_left" + action_suffix) or
|
|
Input.is_action_pressed("move_right" + action_suffix)
|
|
)
|
|
|
|
var axis = Input.get_axis(
|
|
"move_left" + action_suffix,
|
|
"move_right" + action_suffix
|
|
)
|
|
|
|
|
|
if not is_zero_approx(axis):
|
|
if axis < 0:
|
|
direction = -1
|
|
else:
|
|
direction = 1
|
|
|
|
|
|
velocity.x = walk(direction)
|
|
|
|
if not is_zero_approx(velocity.x):
|
|
if velocity.x > 0.0:
|
|
animation.scale.x = 1.0
|
|
else:
|
|
animation.scale.x = -1.0
|
|
|
|
if grab_wall:
|
|
if wall_detect_left.is_colliding():
|
|
animation.scale.x = 1
|
|
elif wall_detect_right.is_colliding():
|
|
animation.scale.x = -1
|
|
|
|
#floor_stop_on_slope = not platform_detector.is_colliding()
|
|
var collision = move_and_slide()
|
|
if collision:
|
|
var collider = get_last_slide_collision().get_collider()
|
|
|
|
if collider is TileMap:
|
|
var tile_rid = get_last_slide_collision().get_collider_rid()
|
|
layer_of_collision = PhysicsServer2D.body_get_collision_layer(tile_rid)
|
|
if layer_of_collision == PICS_BLOCK_LAYER:
|
|
get_tree(). reload_current_scene()
|
|
|
|
var anim := get_new_animation()
|
|
if anim != animation.animation:
|
|
animation.animation = anim
|
|
animation.play()
|
|
|
|
|
|
func get_new_animation() -> String:
|
|
var animation_new: String
|
|
|
|
if is_on_floor():
|
|
if walking_step > 0:
|
|
animation_new = "walk"
|
|
else:
|
|
animation_new = "idle"
|
|
else:
|
|
if velocity.y > 0.0:
|
|
if grab_wall:
|
|
animation_new = "wall_stick"
|
|
else:
|
|
if walking_step > 0:
|
|
animation_new = "falling_diagonals"
|
|
else:
|
|
animation_new = "falling_straight"
|
|
else:
|
|
animation_new = "jumping"
|
|
return animation_new
|