This game development tutorial follows my Mario-like platformer tutorials. This recipe covers adding ladder climbing to a platformer, scaling walls and making a climbable rope in a side scroller.
This is relatively a simple mechanic to add if you have done making a platformer player.
Assets & Project Link
All assets are given credit in a.txt
files in every folder. This is the project link.
Climbing System Breakdown
Our rope is just an Area2D
. We will use its signals body_entered
and body_exited
to detect if player has entered the vicinity of the rope. Whenever a player is nearby, the rope body_entered
signal will be triggered and rope is now in active state and ready to be grabbed. And when body_exited
signal is triggered, it means player is no longer touching the rope, so make the rope inactive again.
If player presses the climb
action, the script will loop over all the ropes and see which one is active. If it found an active rope (meaning the rope to which player is colliding), it will trigger the grab function on player.
The grab function will play the ‘scaling/climbing-animation’ in AnimationPlayer2D
node, it will also restrict all other movements of player (such as walk, run, jump, etc). And will only allow player to move up or down (and never left or right).
When player presses the un-climb
action (toggles the climb button again), player will go back to normal state and can now normally walk, run, jump and so on.
Make a Climbable Object (Rope)
We start by making a scene for rope, ladder, chain, vines (or basically anything that is climbable). Create a scene and call it Rope
or Climbable
or whatever you wish. Make sure that the root node is Area2D
and it has a collision shape node as a child of root. This is how the scene looks like:
Now attach a script and connect the signals body_entered
and body_exited
to the script functions. We will use these signals to make the rope active or inactive based on player’s vicinity.
Rope.gd:
extends Area2D
var is_player_touching_the_rope: bool = false
func _ready():
add_to_group("ropes")
func _on_body_entered(body):
if body in get_tree().get_nodes_in_group("players"):
is_player_touching_the_rope = true
func _on_body_exited(body):
if body in get_tree().get_nodes_in_group("players"):
is_player_touching_the_rope = false
The rope being active means that is_player_touching_the_rope
is true. Rope being inactive means that is_player_touching_the_rope
is false. So when player is touching the rope, only then player can actually call grab()
call on it, else it will ignore the rope.
Use the Rope System to Climb
Adding rope to the level
Before using it, make sure to instance the rope scene in the level. I instanced it and placed it on tree trunk, so the trunk becomes climbable. Like this:
I also have the same character that we made in the earlier tutorial and added climbing on top of its existing mechanics. You can also make your own player, it is totally your choice.
Making the player use the rope
In Player’s script, when player presses the climbing
action, trigger climbing mechanics:
Player.gd:
func _process(delta):
# ... REST OF THE CODE ...
# Trigger climbing mechanics
if Input.is_action_just_pressed("ui_end"): # I used END key here
# If already climbing, make it leave climbing (unclimb)
if is_climbing:
ungrab_rope()
return
# Else check for nearby rope and grab it (start climbing)
if not is_climbing:
grab_rope()
# ... REST OF THE CODE ...
# Grabs the rope object whose area are we overlapping with
func grab_rope():
for rope in get_tree().get_nodes_in_group("ropes"):
if rope.is_player_touching_the_rope:
is_climbing = true
position.x = rope.position.x
# Player no longer climbing
func ungrab_rope():
is_climbing = false
In above code, we have successfully toggled climb/unclimb. But it has no effect. This is because we did not use is_climbing
after making it true.
So in _process(delta)
function, we will check if player is climbing, then do all sort of climbing things such as playing the scaling/climb animation and moving up and down based on input. In Player.gd _process(delta)
function:
# !!! NOTE: ANIMATION IS TOTALLY OPTIONAL, AND YOU CAN REMOVE
# !!! ANIMATION PART IF YOU DONT HAVE AnimatedSprite2D NODE OR
# !!! CLIMB ANIMATION
func _process(delta):
# ... REST OF THE CODE ...
# If climbing, move up/down and play animation
if is_climbing:
velocity = Vector2(0, 0) # Reset velocity
$AnimatedSprite2D.animation = "climb"
if Input.is_action_pressed("ui_up"):
$AnimatedSprite2D.play("climb")
velocity.y -= 8000 * delta # Main part
elif Input.is_action_pressed("ui_down"):
$AnimatedSprite2D.play("climb")
velocity.y += 8000 * delta # Main part
else:
$AnimatedSprite2D.stop()
# ... REST OF THE CODE ...
Problems with this Ladder Scaling System
While scaling the ladder/rope, make sure that jumping, walking, running and other normal things are disabled. I used a hacky way of adding an if-statement to check if we are is_climbing
: then don’t jump. But a better designed game must have a system of state-machines that defines the constraints of when certain actions can or cannot be done.
But my hacky solution looks like this though (not recommended):
func _physics_process(delta):
var dir = Input.get_axis("ui_left", "ui_right")
if not is_climbing: # ***SEE THIS***
velocity.y += gravity * delta # Apply gravity
if dir != 0: # Movement
if is_on_floor():
velocity.x = lerp(velocity.x, dir * speed, acceleration)
else:
velocity.x = lerp(velocity.x, dir * speed * 1.4, acceleration)
if dir > 0:
$AnimatedSprite2D.flip_h = false
else:
$AnimatedSprite2D.flip_h = true
if not is_attacking:
$AnimatedSprite2D.play("run")
else:
velocity.x = lerp(velocity.x, 0.0, friction)
if not is_attacking:
$AnimatedSprite2D.play("idle")
# Double Jump
if not is_climbing: # ***SEE THIS***
if Input.is_action_just_pressed("ui_up") and (is_on_floor() or jumps_used < jumps_count - 1):
velocity.y = jump_speed
jumps_used += 1
is_jumping = true
# Animation and Jumping State
if not is_climbing: # ***SEE THIS***
if is_jumping:
if is_on_floor():
jumps_used = 0
is_jumping = false
if not is_attacking:
$AnimatedSprite2D.play("fall")
More Things
These related 2D platformer game tutorials discuss other common things needed for 2D platformer games.
Thank you for reading it <3