Quick Note: Implementing Running for Character in Godot

Forewords

Before beginning, I'd like to announce there's a new top-down action RPG tutorial here by HeartBeast. It's the most up-to-date tutorial for Godot in Youtube right now, which is version 3.2. As of I'm writing this, he's already 11 episodes in. Go and show your love.

By the way, I'm trying out C# in Godot, but the tutorial series above uses GDScript, so mine is going to be a bit different. I also need to state that I've never coded in C# before, what I had in mind before I started C# was that C# was kinda like Java, which helped me understand it a lot but, if you see any quirky code, like violation of style guides or common practices in C#, let me know or simply ignore them.

This post also follows the logic of HeartBeast's tutorial, so I strongly recommend you check it out, at least 4 episodes in.

Movement 101

Firstly, we need to understand that there are three parts for a smooth movement:

  • Acceleration is how fast a player starts moving.
  • Friction is how fast a player stops moving.
  • Of course when a player accelerates, you need to define a peek value, which is the limit of how fast it can accelerate. This is called maximum speed.

When you imagine it in a graph, it starts increasing as soon as the player receives an input and it starts decreasing back to zero when it stops receiving the input. See below:

graph about the speed of a player

(I know this is not the most beautiful graph you've ever seen but bear with me please.)

Knowing this, we can assume a player's code (assuming the player is a KinematicBody2D) is going to be as such:

using Godot;
using System;

public class Player : KinematicBody2D
{
    private const int ACCELERATION = 500;
    private const int MAX_SPEED = 60;
    private const int FRICTION = 500;

    private Vector2 velocity;

    public override void _PhysicsProcess(float delta)  // compute next frame
    {
        // let's create a vector for user input, defining which coordinates the vector is targeting to
        var input_vector = Vector2.Zero;  // equivalent to `new Vector2()`, I use it for shorthand sometimes
        input_vector.x = Input.GetActionStrength("ui_right") - Input.GetActionStrength("ui_left");  // assign *if it is left or right* to input vector
        input_vector.y = Input.GetActionStrength("ui_down") - Input.GetActionStrength("ui_up");  // assign *if it is up or down* to input vector
        input_vector = input_vector.Normalized();  // see heartbeast for this, he explains better

        if (input_vector == Vector2.Zero)  // if we do not receive any input
        {
            velocity = velocity.MoveToward(Vector2.Zero, FRICTION * delta);
        }
        else  // if we receive any input
        {
            velocity = velocity.MoveToward(input_vector * MAX_SPEED, ACCELERATION * delta);
        }

        velocity = MoveAndSlide(velocity);  // move to target and new assign target coordinates to velocity
    }
}

This is a basic movement which has a constant speed. You can play with ACCELERATION, MAX_SPEED and FRICTION constants to change the feel of your movement.

How to Run

Let's assume we'd like to bump our speed when we, say, press SHIFT key. In order to do that, we first need to define an input. You can do that by clicking "Project > Project Settings" menu...

project settings menu entry

...and going into "Input Map" tab.

input map tab

As you can see, the ones such as ui_up and ui_down in the code are defined here along with the keys they target. So, as in the image, we need to add a new action and mapping it to a key by first defining a name for our new action and clicking the "Add" button.

Then we need to add a key for our new accelerate action...

adding a key

...and define a key for it.

defining a key

When you do that, you can now check if accelerate action is received by writing a code like below in C#:

bool is_accelerating = Input.IsActionPressed("accelerate");

Now we need to refactor our code a little bit. I wanted to do this with as little change to the code as possible. Our target is ACCELERATION, MAX_SPEED and FRICTION constants.

The problem is these are constants, which are not meant to be mutated on the runtime. However, we'd like to change the values of these depending on if we receive accelerate action or not. So, we change these to static.

private static int ACCELERATION = 500;
private static int MAX_SPEED = 60;
private static int FRICTION = 500;

There is also a very nice feature in C#. Just like Kotlin (for the sake of giving an example from JVM-based languages), we can write getters (and also setters) in property line. So, my code changes to:

private static int ACCELERATION
{
    get
    {
        var value = 500;
        if (Input.IsActionPressed("accelerate"))
        {
            value = 700;
        }
        return value;
    }
}
private static int MAX_SPEED
{
    get
    {
        var value = 60;
        if (Input.IsActionPressed("accelerate"))
        {
            value = 90;
        }
        return value;
    }
}
private static int FRICTION
{
    get
    {
        var value = 500;
        if (Input.IsActionPressed("accelerate"))
        {
            value = 700;
        }
        return value;
    }
}

We define default values with value variable and change it if we receive accelerate action. The code above results in the table below:

NormalWhile accelerate
ACCELERATION500700
MAX_SPEED6090
FRICTION500700

You can, again, change the values as your needs. Some values feel like you are controlling a rock while others feel like the ground is ice.

Final Words

As I've said in the beginning, be sure to check HeartBeast's Godot RPG series. It's the hottest thing in Godot tutorials right now. See you in the next post.

No Comments Yet