Building a 2D Platformer Game with Godot. Part 2
Tags: godot, 2d, game, development
Published: 2023-10-01 00:14:10

---
title: Building a 2D Platformer Game with Godot. Part 2
published: true
date: 2023-09-25 16:16:08 UTC
tags: #godot #2d #game #development
canonical_url: https://carlosmv.hashnode.dev/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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688493079086/fe0ac724-adb3-485a-94df-9ff642f81e6b.png)

We click on the "script" symbol.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688495779545/e139dcfe-d83e-4e24-9778-80be3935868f.png)

This window will show up.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688495849068/811fb217-e012-4d96-b20b-7252253167fe.png)

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()

```

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`](http://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()

```

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688498880530/9f93eb52-8763-46fd-bc51-596528139f6a.png)

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()

```

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()

```

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()

```

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)

```

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)

```

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)

....

```

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688749606434/838ed8ec-5802-44d0-937a-425f11bb3816.png)

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".

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688749400960/88e47e2f-4935-48b5-ad8e-5da277d2e7be.png)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688749437024/46ca1668-75ef-4db5-a1f4-590dbbd68a51.png)

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).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688749994323/09e2dcfa-1a24-4b29-aced-ddd09d719273.png)

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688750154742/db1a8115-9552-4407-9512-a2488dfbe603.png)

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](https://ansimuz.itch.io/) for allowing us to use its assets for free. [Coding Quest](https://www.youtube.com/@CodingQuests), [HeartBeast](https://www.youtube.com/@uheartbeast) and [BornCG](https://www.youtube.com/@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**](https://twitter.com/Carlos_marcv), or [**LinkedIn**](https://www.linkedin.com/in/carlos-marcano-a2135a134/).

### Resources

[Godot 2D Sprite Animation documentation](https://docs.godotengine.org/en/stable/tutorials/2d/2d_sprite_animation.html)

[Godot Camera2D documentation](https://docs.godotengine.org/en/stable/classes/class_camera2d.html)

[Video - Pixel Platformer Tutorial / Code Along P3 (Animation and Character Skins) - Godot Engine](https://www.youtube.com/watch?v=fevBckU1p6o&list=PL9FzW-m48fn16W1Sz5bhTd1ArQQv4f-Cm&index=3)

[Video - Build Your Own Platformer with this FREE Godot 4.0 Crash Course! Part 2](https://www.youtube.com/watch?v=RqCm8mLZp24&t=1093s)