From 752c15cce1317f2ea156ca368645f2239ea595bf Mon Sep 17 00:00:00 2001 From: Ruslan Ignatov Date: Sun, 28 Jun 2026 09:54:27 +0300 Subject: [PATCH] Added wall jumps --- game/entities/player/player.gd | 71 ++++++++++++++++++++------------ game/entities/player/player.tscn | 18 +++++--- game/levels/test_level.tscn | 4 ++ project.godot | 15 +++++++ 4 files changed, 76 insertions(+), 32 deletions(-) diff --git a/game/entities/player/player.gd b/game/entities/player/player.gd index 233c2a0..6f18341 100644 --- a/game/entities/player/player.gd +++ b/game/entities/player/player.gd @@ -9,7 +9,8 @@ extends CharacterBody2D @export_group("Jump", "jump") @export_range(0.0, 1000.0) var jump_max_fall_speed := 1000 -@export_range(0.0, 1000.0) var jump_velocity := 500.0 +@export_range(0.0, 1000.0) var jump_floor_velocity := 500.0 +@export var jump_wall_velocity := Vector2(500, 500) @export_group("Gravity factors", "gravity_factor") @export_range(0.0, 2.0) var gravity_factor_jump := 1.0 @@ -25,15 +26,18 @@ extends CharacterBody2D var _slow_down_tween: Tween -var _wall_normal: = 0.0 +var _last_wall_normal: = 0.0 @onready var state_machine : StateMachine = $StateMachine @onready var jump_buffer_timer : Timer = $JumpBufferTimer -@onready var coyote_time_timer : Timer = $CoyoteTimeTimer + +@onready var floor_coyote_time_timer : Timer = $FloorCoyoteTimeTimer +@onready var wall_coyote_time_timer : Timer = $WallCoyoteTimeTimer + +@onready var wall_attach_timer : Timer = $WallAttachTimer @onready var attack_cooldown_timer : Timer = $AttackCooldownTimer -@onready var slide_delay_timer : Timer = $SlideDelayTimer @onready var _max_speed := movement_max_speed_normal @@ -44,35 +48,50 @@ func _ready() -> void: func _physics_process(delta: float) -> void: if is_on_floor(): - coyote_time_timer.start() + floor_coyote_time_timer.start() + _last_wall_normal = 0.0 if is_on_ceiling_only() and velocity.y < 0.0: velocity.y = 0.0 - if not is_on_floor(): - if is_on_wall(): - _wall_normal = signf(velocity.x) - print("On wall! ", velocity.x, " ", Engine.get_frames_drawn()) - else: - var gravity_factor := gravity_factor_jump if velocity.y < 0.0 else gravity_factor_fall - - if velocity.y < 0.0 and not Input.is_action_pressed("jump"): - velocity.y *= gravity_factor_passive_jump - - velocity += get_gravity() * gravity_factor * delta - velocity.y = clampf(velocity.y, -jump_max_fall_speed, jump_max_fall_speed) - - + var input_direction := Input.get_axis("move_left", "move_right") if Input.is_action_just_pressed("jump"): jump_buffer_timer.start() + + if not is_on_floor(): + var gravity_factor := gravity_factor_jump if velocity.y < 0.0 else gravity_factor_fall - if not coyote_time_timer.is_stopped() and not jump_buffer_timer.is_stopped(): - velocity.y = -jump_velocity - jump_buffer_timer.stop() - coyote_time_timer.stop() + if is_on_wall_only() and not is_zero_approx(input_direction): + var wall_normal := get_wall_normal().x + if (wall_normal * input_direction) < 0.0: + wall_coyote_time_timer.start() + if sign(wall_normal) != sign(_last_wall_normal): + wall_attach_timer.start() + _last_wall_normal = wall_normal + + if not Input.is_action_pressed("move_down") and velocity.y > 0.0: + gravity_factor = gravity_factor_slide if wall_attach_timer.is_stopped() else 0.0 - var direction := Input.get_axis("move_left", "move_right") - if direction: - velocity.x = move_toward(velocity.x, direction * _max_speed, movement_acceleration * delta) + if velocity.y < 0.0 and not Input.is_action_pressed("jump"): + velocity.y *= gravity_factor_passive_jump + + velocity += get_gravity() * gravity_factor * delta + velocity.y = clampf(velocity.y, -jump_max_fall_speed, jump_max_fall_speed) + + if not jump_buffer_timer.is_stopped(): + if not floor_coyote_time_timer.is_stopped(): + velocity.y = -jump_floor_velocity + jump_buffer_timer.stop() + floor_coyote_time_timer.stop() + elif not wall_coyote_time_timer.is_stopped(): + velocity = -jump_wall_velocity + velocity.x *= -_last_wall_normal + jump_buffer_timer.stop() + wall_coyote_time_timer.stop() + + + if input_direction: + var input_velocity := input_direction * _max_speed + velocity.x = move_toward(velocity.x, input_velocity, movement_acceleration * delta) else: velocity.x = move_toward(velocity.x, 0, movement_acceleration * delta) diff --git a/game/entities/player/player.tscn b/game/entities/player/player.tscn index 2b1924c..3b3da71 100644 --- a/game/entities/player/player.tscn +++ b/game/entities/player/player.tscn @@ -22,18 +22,24 @@ player = NodePath("../..") [node name="JumpBufferTimer" type="Timer" parent="." unique_id=1845152635] process_callback = 0 +wait_time = 0.1 +one_shot = true + +[node name="FloorCoyoteTimeTimer" type="Timer" parent="." unique_id=867376979] +process_callback = 0 wait_time = 0.05 one_shot = true -[node name="CoyoteTimeTimer" type="Timer" parent="." unique_id=867376979] +[node name="WallCoyoteTimeTimer" type="Timer" parent="." unique_id=548389084] process_callback = 0 -wait_time = 0.05 +wait_time = 0.1 +one_shot = true + +[node name="WallAttachTimer" type="Timer" parent="." unique_id=1472311359] +process_callback = 0 +wait_time = 0.5 one_shot = true [node name="AttackCooldownTimer" type="Timer" parent="." unique_id=1208896947] process_callback = 0 wait_time = 0.75 - -[node name="SlideDelayTimer" type="Timer" parent="." unique_id=1746851787] -process_callback = 0 -wait_time = 0.75 diff --git a/game/levels/test_level.tscn b/game/levels/test_level.tscn index 62c8791..b387372 100644 --- a/game/levels/test_level.tscn +++ b/game/levels/test_level.tscn @@ -25,6 +25,10 @@ shape = SubResource("RectangleShape2D_bhunq") position = Vector2(0, -1100) shape = SubResource("RectangleShape2D_bhunq") +[node name="CollisionShape2D4" type="CollisionShape2D" parent="StaticBody2D" index="3" unique_id=558475574] +position = Vector2(200, -1000) +shape = SubResource("RectangleShape2D_bhunq") + [node name="Menus" parent="." index="2" unique_id=1464185162] offset_left = -320.0 offset_top = -210.0 diff --git a/project.godot b/project.godot index ed41b00..fae7eb8 100644 --- a/project.godot +++ b/project.godot @@ -35,11 +35,26 @@ gdscript/warnings/untyped_declaration=1 window/size/viewport_width=640 window/size/viewport_height=360 window/size/resizable=false +window/size/maximize_disabled=true window/stretch/mode="viewport" window/stretch/scale_mode="integer" [input] +ui_accept={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_cancel={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null) +] +} ui_left={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)