You need to make a 2D platform-style character.
New developers are often surprised at how complex a platform character can be to program. Godot provides some built-in tools to assist, but there are as many solutions as there are games. In this tutorial, we won’t be going in-depth with features like double-jumps, crouching, wall-jumps, or animation. Here we’ll discuss the fundamentals of platformer movement. See the rest of the recipes for other solutions.
While it’s possible to use
RigidBody2D to make a platform character, we’ll be focusing on
CharacterBody2D. Kinematic bodies are well-suited for platformers, where you are less interested in realistic physics than in responsive, arcade feel.
Start with a
CharacterBody2D node, and add a
CollisionShape2D to it.
Attach the following script to the root node of the character. Note that we’re using input actions we’ve defined in the InputMap:
"jump". See InputActions.
extends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 func _physics_process(delta): # Add gravity every frame velocity.y += gravity * delta # Input affects x axis only velocity.x = Input.get_axis("walk_left", "walk_right") * speed move_and_slide() # Only allow jumping when on the ground if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = jump_speed
The values used for
jump_speed depend greatly on the size of your player sprite. The player’s texture in this example is
108x208 pixels. If your sprite is smaller, you’ll want to use smaller values. We also want high values so that everything feels fast and responsive. A low gravity results in a floaty-feeling game while a high value means you’re quickly back on the ground and ready to jump again.
Note that we’re checking
is_on_floor() after using
move_and_slide() function sets the value of this method, so it’s important not to check it before, or you’ll be getting the value from the previous frame.
Friction and acceleration
The above code is a great start, and you can use it as the foundation for a wide variety of platform controllers. One problem it has, though, is the instantaneous movement. For a more natural feel, it’s better if the character has to accelerate up to its max speed and that it coasts to a stop when there is no input.
One way to add this behavior is to use linear interpolation (“lerp”). When moving, we will lerp between the current speed and the max speed and while stopping we’ll lerp between the current speed and
0. Adjusting the lerp amount will give us a variety of movement styles.
For an overview of linear interpolation, see Gamedev Math: Interpolation.
extends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 @export_range(0.0, 1.0) var friction = 0.1 @export_range(0.0 , 1.0) var acceleration = 0.25 func _physics_process(delta): velocity.y += gravity * delta var dir = Input.get_axis("walk_left", "walk_right") if dir != 0: velocity.x = lerp(velocity.x, dir * speed, acceleration) else: velocity.x = lerp(velocity.x, 0.0, friction) move_and_slide() if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = jump_speed
Try changing the values for
acceleration to see how they affect the game’s feel. An ice level, for example, could use very low values, making it harder to maneuver.
This code gives you a starting point for building your own platformer controller. For more advanced platforming features such as wall jumps, see the other recipes in this section.
Download This Project
Download the project code here: https://github.com/godotrecipes/2d_platform_basic