Skip to main content

Complete 2D player movement, Beginner to Pro in Godot

 Have you ever wondered why these games feel so good to play?

If your answer is visuals then, No the key component is their controls. The character movement feels natural, and they move exactly how you want. A good character movement will always make the player feel that they are in total control of the character. 

So, you are wondering how to achieve this? 

Well, there are some tricks that professional developers use to make their control better. And today we are goanna see what they are and how you can do it in Godot.

VIDEO TUTORIAL



COMPLETE CODE:

extends KinematicBody2D

var velocity : Vector2

export var max_speed : int = 1000
export var gravity : float = 55
export var jump_force : int = 1600
export var acceleration : int = 50
export var jump_buffer_time : int  = 15
export var cayote_time : int = 15

var jump_buffer_counter : int = 0
var cayote_counter : int = 0

func _physics_process(_delta):
	
	if is_on_floor():
		cayote_counter = cayote_time

	if not is_on_floor():
		if cayote_counter > 0:
			cayote_counter -= 1
		
		velocity.y += gravity
		if velocity.y > 2000:
			velocity.y = 2000

	if Input.is_action_pressed("ui_right"):
		velocity.x += acceleration 
		$Sprite.flip_h = false

	elif Input.is_action_pressed("ui_left"):
		velocity.x -= acceleration
		$Sprite.flip_h = true

	else:
		velocity.x = lerp(velocity.x,0,0.2)
	
	velocity.x = clamp(velocity.x, -max_speed, max_speed)
	
	if Input.is_action_just_pressed("ui_select"):
		jump_buffer_counter = jump_buffer_time
	
	if jump_buffer_counter > 0:
		jump_buffer_counter -= 1
	
	if jump_buffer_counter > 0 and cayote_counter > 0:
		velocity.y = -jump_force
		jump_buffer_counter = 0
		cayote_counter = 0
	
	if Input.is_action_just_released("ui_select"):
		if velocity.y < 0:
			velocity.y += 400
	
	velocity = move_and_slide(velocity, Vector2.UP)


Explanation:

Now the first trick is Using a capsule shape for player collision or at least use a circle collision shape at the foot. This little change will make your movement a lot better.


when I am just couple of pixels below the platform b
ut because of the circle shape collision. I am still able to slide up to it.

Basically, you don’t want to stop player from climbing when he is just 2 pixels below

This can make the player frustrated. And you don’t want that. The only downside of this is that you can also slip down from the platform, but we have a solution for this which we will discuss later.


BASIC MOVEMENT (LEFT, RIGHT AND JUMP)

extends KinematicBody2D

var velocity : Vector2

export var max_speed : int = 1000
export var gravity : float = 55
export var jump_force : int = 1600
	
func _physics_process(_delta):
	
	if not is_on_floor():
		velocity.y += gravity
		if velocity.y > 2000:
			velocity.y = 2000
	
	if Input.is_action_pressed("ui_right"):
		velocity.x = max_speed 
		$Sprite.flip_h = false
		
	elif Input.is_action_pressed("ui_left"):
		velocity.x = -max_speed
		$Sprite.flip_h = true
	else:
		velocity.x = 0

	if Input.is_action_pressed("ui_select"):
		if is_on_floor():
			velocity.y = -jump_force

	velocity = move_and_slide(velocity, Vector2.UP)

First of all, I use a kinematic body for the player which allow me to use the move_and_slide() function. Now first I have declared a velocity vector and some basic variable that we need. Now create the physics_process function and inside it
We check when we are not on the ground.
Then we will apply gravity in the Y component of the velocity, so that our character falls when not on the ground. But we also don’t want to increase the velocity to infinite, so we clamp it in 2000. 
Then we check when the player has pressed the “ui_right” key. Which is nothing but the right arrow key So, when we detect the input, we want to move our character to the right. That's why we set X component of velocity to our max speed, and I am setting the flip of the sprite, so it looks at the direction of movement.
Similarly, when we press the left arrow key. we will set X component of the velocity to negative max speed and at last set the flip of the flip of the sprite.
Now if we press none of the buttons, we want the player to stop, thus we set the velocity.x to 0. Now for jumping, if we press the “ui_select” which is by the way your spacebar
We first make sure we are on the ground and then Set the velocity.y to negative jump_force which is the upward direction. And finally, we will put this velocity in the move and slide function Along with Velocity.UP to tell which direction is upward. And with that done your player can move left, right and jump.

Add Acceleration and Deceleration

For this create a new variable acceleration.
And when we are pressing the right arrow key. We will add a small acceleration value instead of setting it to full speed. Similarly on pressing the left arrow key We will subtract the acceleration value
	if Input.is_action_pressed("ui_right"):
		velocity.x += acceleration 
		$Sprite.flip_h = false
		
	elif Input.is_action_pressed("ui_left"):
		velocity.x -= acceleration
		$Sprite.flip_h = true

And here instead of stopping the character suddenly we will use a lerp function Which stand for linear interpolation, this will take three values. First one is the starting value for this we will use whatever our current value is. The second one is the final value. For this I will write 0. And the third one decides how fast you want to complete it. For this demo I will use 0.2

But remember the higher the value the faster it will complete. And finally, when we are adding acceleration, we don’t want our velocity to go beyond our maximum speed. That’s why at last we use the clamp function to set our velocity within the limits.

	else:
		velocity.x = lerp(velocity.x,0,0.2)
	
	velocity.x = clamp(velocity.x, -max_speed, max_speed)    

variable jump height

This allows players to do short jumps by releasing the jump button early and still do long jumps by holding the jump button longer. This is done by applying additional downforce when we release the jump button.
To do this we just need to add these 3 lines. First, we look when the player has released the jump button. After that we check if the velocity.y < 0 Which means we are still going upwards. 
So now we add some downward force in our velocity, for this demo 400 is fine. And with that done we can now do short jumps by releasing the jump button early.

  if Input.is_action_just_released("ui_select"):
	if velocity.y < 0:
		velocity.y += 500

Jump buffer

next most important thing that you should definitely add to your game is “jump buffer” With jump buffer you give your player some extra margin in which if they press the jump button just before hitting the ground, they can still perform the jump. This because if the player presses jump button and your character doesn’t jump, 
your game will feel unresponsive, and the player will quit So, for jump buffer we first need to create two variables One is buffer time and other is buffer counter
Now if you don’t know, the “physics process” function runs 60 times in 1 sec.
So, setting buffer time to 15 means ¼ of a sec. Now when the player presses jump button, we want to set the counter to buffer_time. Then outside of this block we check if the counter > 0 Then we reduce the counter by 1.
And again, outside the if statement we check if the counter > 0 and we are on floor Then we do the jump. And finally set counter to 0.
for example, let’s suppose we are falling 
And as soon as we press the jump button, we basically start the counter.
And now for the next 15 frames the value of counter is going to be greater than 1
And if we hit the ground within this duration, we will do the jump. Otherwise, our timer will run out.

export var jump_buffer_time : int  = 15

var jump_buffer_counter : int = 0

if Input.is_action_just_pressed("ui_select"):
	jump_buffer_counter = jump_buffer_time

if jump_buffer_counter > 0:
	jump_buffer_counter -= 1

if jump_buffer_counter > 0 and is_on_floor():
	velocity.y = -jump_force
	jump_buffer_counter = 0

coyote time

As I said in the starting of the video you should use a circle collision shape in the bottom half of the player. As it helps you to get to the platform if you are just a little bit away, but it has a downside that you can slip through the corner as well.
In most platformers players usually jump from the edge. 
And if instead of jumping they fall, then again, your game feels unresponsive. 
So, the solution to this is adding coyote time to your game. Similar to jump buffer, coyote time also give the player some extra time to perform the jump after they leave the ground.

To create this, we first need to create two variables one is coyote time and other is coyote counter. 
Now when we are on the ground, we want to set the coyote counter to coyote time. and as soon as we are not in the ground, we will start reducing the counter by 1. And at last, down when we are jumping, instead of checking for the ground 
We check if the coyote counter is > 0. And also, when we do a jump, we want to set counter to 0.

And that’s it when we are on the ground the counter is always going to be full
And if we fall off the platform the counter starts to reduce. And if we press the jump button before it reaches 0. We can do the jump.
export var cayote_time : int = 15
var cayote_counter : int = 0

if is_on_floor():
	cayote_counter = cayote_time

	if not is_on_floor():
		if cayote_counter > 0:
			cayote_counter -= 1

	if cayote_counter > 0 and jump_buffer_counter > 0:
		velocity.y = -jump_force
		cayote_counter = 0
		jump_buffer_counter = 0

So, you can definitely feel the difference after applying all the improvements.
And subscribe my channel for more amazing videos.

Comments

Popular posts from this blog

Background Loading in Godot | DICODE

Video Tutorial       

Improve your jumping! | Type of jumps in Godot

Wall jump, Wall climbing, Wall sliding and double jump are some of the most common mechanics for a plat-former game. And on top of that they are super easy to create in Godot. So in this Godot tutorial, I have tried to implement all these things in the simplest way possible. I would highly recommend you to watch the 2-D movement video first, because I am adding wall jumps and other stuff in that same script. So, watch it at 1.5x speed! to get an overview. VIDEO TUTORIAL TEXT TUTORIAL Double Jump For this, create a variable jump_counter. This will keep track of how many jumps we have done, while we're in the air. So by default it will be 0. var jump_counter : int = 0 <------- var jump_buffer_counter : int = 0 var cayote_counter : int = 0 Now if you remember, in the previous video, when we are pressing the jump button. We are setting the jump_buffer_counter. So, when we are not on the ground, we will check if buffer_counter > 0 This will be true, only when we have pressed th