DEV Community

Carlos Armando Marcano Vargas
Carlos Armando Marcano Vargas

Posted on • Originally published at carlosmv.hashnode.dev on

Building a 2D Platformer Game with Godot. Part 2

In the previous article, we learned how to install Godot, how to create a Godot project, and how to create a scene and nodes.

In this article, we are going to learn how to add animations to the player node.

Adding Animations

We go to the player scene.

We click on the "script" symbol.

This window will show up.

Now, let's take a look at the code of this script:

extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

func _physics_process(delta):

    # Add the gravity.
    if not is_on_floor():
        velocity.y += gravity * delta

    # Handle Jump.
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Get the input direction and handle the movement/deceleration.
    # As good practice, you should replace UI actions with custom gameplay actions.
    var direction = Input.get_axis("ui_left", "ui_right")
    if direction:
        velocity.x = direction * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)

    move_and_slide()

Enter fullscreen mode Exit fullscreen mode

This code extends the CharacterBody2D class, which has collision shapes and handles gravity automatically.

SPEED and JUMP_VELOCITY are constants for the character's speed and jump velocity.

The physicsprocess() function is called every physics frame. gravity gets the project gravity setting to match the RigidBody nodes. velocity.y += gravity * delta adds gravity to the velocity of each frame. if Input.is_action_just_pressed("ui_accept") checks if the jump key is pressed. velocity.y = JUMP_VELOCITY sets the jump velocity when jumping. direction = Input.get_axis gets the left/right input and stores the direction (-1 for left, 1 for right).

velocity.x = direction * SPEED moves the character left or right based on the direction.

move_and_slide() applies the velocity and handles collisions.

Now, the first animation that we are going to add is the Idle animation.

We add the following line of code: $AnimatedSprite2D.animation = "Idle" to the _physics_process() function.

Let's change the value of the constant SPEED to 100 instead of 300. If we keep the SPEED's value to 300, it will move too fast and the player will fall.

func _physics_process(delta):

    # Add the gravity.
    if not is_on_floor():
        velocity.y += gravity * delta
        $AnimatedSprite2D.animation = "Jump"

    # Handle Jump.
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Get the input direction and handle the movement/deceleration.
    # As good practice, you should replace UI actions with custom gameplay actions.
    var direction = Input.get_axis("ui_left", "ui_right")
    if direction:
        velocity.x = direction * SPEED
        $AnimatedSprite2D.animation = "Run"
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        $AnimatedSprite2D.animation = "Idle"

    move_and_slide()

Enter fullscreen mode Exit fullscreen mode

The line of code we just added plays the Idle animation when the player is not moving.

We play the scene, and we can see the player in the Idle animation.

Let's add the "Jump" and "Run" animations.

We need to change a little bit the script's code.

func _physics_process(delta):

    var direction = Input.get_axis("ui_left", "ui_right")
    if direction:
        velocity.x = direction * SPEED
        $AnimatedSprite2D.animation = "Run"
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        _animated_sprite.play("Idle")

    # Add the gravity.
    if not is_on_floor():
        velocity.y += gravity * delta
        $AnimatedSprite2D.animation = "Jump"

    # Handle Jump.
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Get the input direction and handle the movement/deceleration.
    # As good practice, you should replace UI actions with custom gameplay actions.

    move_and_slide()

Enter fullscreen mode Exit fullscreen mode

Now, if we play the scene again, all the animations should be playing, if we move the player to right and left, the "Run" animations should play. If we press the space bar key on our keyboard, the "Jump" animation should play, if we don't move the player, the "Idle" animation should play.

So far, this is the complete script we have.

extends CharacterBody2D

const SPEED = 100.0
const JUMP_VELOCITY = -400.0

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
@onready var _animated_sprite = $AnimatedSprite2D

func _physics_process(delta):

    var direction = Input.get_axis("ui_left", "ui_right")
    if direction:
        velocity.x = direction * SPEED
        $AnimatedSprite2D.animation = "Run"
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        _animated_sprite.play("Idle")

    # Add the gravity.
    if not is_on_floor():
        velocity.y += gravity * delta
        $AnimatedSprite2D.animation = "Jump"

    # Handle Jump.
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Get the input direction and handle the movement/deceleration.
    # As good practice, you should replace UI actions with custom gameplay actions.

    move_and_slide()

Enter fullscreen mode Exit fullscreen mode

On top of the physics_process() function we declare a new variable, _animated_sprite, and we assign it $AnimatedSprite2D as value.

Now, instead of writing $AnimatedSprite2D to load a sprite. We can just write animate_sprite and use their methods.

@onready var _animated_sprite = $AnimatedSprite2D

func _physics_process(delta):

    var direction = Input.get_axis("ui_left", "ui_right")

    if direction:
        velocity.x = direction * SPEED
        apply_friction(delta)
        _animated_sprite.play("Run")

    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        _animated_sprite.play("Idle")

    # Add the gravity.
    if not is_on_floor():
        velocity.y += gravity * delta
        _animated_sprite.play("Jump")

    # Handle Jump.
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Get the input direction and handle the movement/deceleration.
    # As good practice, you should replace UI actions with custom gameplay actions.

    move_and_slide()

Enter fullscreen mode Exit fullscreen mode

Let's create a new function, to add friction to the game.

func apply_friction(delta):
    velocity.x = move_toward(velocity.x, 0, FRICTION * delta)

Enter fullscreen mode Exit fullscreen mode

We have to create a new constant, FRICTION on top of the physics_process() function.

extends CharacterBody2D

const SPEED = 100.0
const JUMP_VELOCITY = -400.0
const FRICION = 400

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
@onready var _animated_sprite = $AnimatedSprite2D

func _physics_process(delta):

    var direction = Input.get_axis("ui_left", "ui_right")

    if direction:

        velocity.x = direction * SPEED
        apply_friction(delta)
        $AnimatedSprite2D.animation = "Run"
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        _animated_sprite.play("Idle")

    # Add the gravity.
    if not is_on_floor():
        velocity.y += gravity * delta
        $AnimatedSprite2D.animation = "Jump"

    # Handle Jump.
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Get the input direction and handle the movement/deceleration.
    # As good practice, you should replace UI actions with custom gameplay actions.

    move_and_slide()

func apply_friction(delta):
    velocity.x = move_toward(velocity.x, 0, FRICION * delta)

Enter fullscreen mode Exit fullscreen mode

An issue that we are having with the animations so far, is that the player doesn't flip to the left side when it moves to the left.

Let's change that, by using set_flip_h() function, to flip the sprite horizontally.

func _physics_process(delta):

    var direction = Input.get_axis("ui_left", "ui_right")

    if direction:
        velocity.x = direction * SPEED
        apply_friction(delta)
        _animated_sprite.play("Run")

        # Check for flip horizontal based on direction
        if direction > 0 :
            _animated_sprite.set_flip_h(false)
        else:
            _animated_sprite.set_flip_h(true)

....

Enter fullscreen mode Exit fullscreen mode

Now, the script flips the sprite horizontally based on the direction. If moving to the right, the flip is false, but if the player is moving to the left, the flip is true.

Camera 2D

One of the inconveniences that I have found building this game, is that every time I play the "World" scene, the game's camera seems too far away from the player.

It is difficult to appreciate the character's details and the assets of the game.

To solve this issue we are going to create another child node for the Player node. And select "Camera2D".

In the scene panel(blue rectangle in the image below), we select Camera2D. Then we go to change the Camera2D settings(red rectangle in the image below).

Now, we have to change Zoom's parameters.

If we change the X and Y parameters to 3.

This is how the game looks when we play it.

Adjust the zoom of the camera to your preferences.

Conclusion

In this article, we learn how to add the idle, run and jump animation to the player node. And how to adjust the zoom of the camera.

I want to shout out to Ansimuz for allowing us to use its assets for free. Coding Quest, HeartBeast and BornCG for creating very good content about Godot, their content motivated me to create this tutorial. Their videos were a great help for me to learn Godot and Game development.

Thank you for taking the time to read this article.

If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through Twitter, or LinkedIn.

Resources

Godot 2D Sprite Animation documentation

Godot Camera2D documentation

Video - Pixel Platformer Tutorial / Code Along P3 (Animation and Character Skins) - Godot Engine

Video - Build Your Own Platformer with this FREE Godot 4.0 Crash Course! Part 2

Top comments (0)