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