# GMLCreating Smooth Character Movement

R

#### Rukiri

##### Guest
GM Version: GMS1.4, GMS2 *May work with older versions
Target Platform: ALL
Download: https://github.com/Rukiri/ZenRPG * This will be constantly updated, but the main code below should stay the same

Summary:
In this tutorial you'll learn how to create smooth movement, this tutorial is specific to player movement but can be translated into NPC movement, or Cell-based movement "Math for that type of movement is not provided"

Code:

Supporting Scripts:

Code:
``````var y1 = argument0;
var y2 = argument1;
var x1 = argument2;
var x2 = argument3;
var degree;

degree = arctan2(argument0 - argument1, argument2 - argument3) * (180 / pi);

return degree;``````
Code:
``````var radians;
var degrees = argument0;

radians = 3.14 * degrees / 180;

Player Object:
Create Event:
Code:
``````move_speed = 200;
enable_diagonal_movement = true;``````
Step Event:
Code:
``````// Input Setup

var key_right, key_left, key_down, key_up;

key_right = keyboard_check(vk_right);
key_left = keyboard_check(vk_left);
key_down = keyboard_check(vk_down);
key_up = keyboard_check(vk_up);

// Object Position and Direction
var move_direction, dir, xpos, ypos, hspd, vspd;
var delta = delta_time / 1000000;
hspd = move_speed;
vspd = move_speed;
xpos = key_right - key_left ;
ypos = key_down - key_up;

dir = RadianToDegree(key_up, key_down, key_right, key_left);

// Check if moving
var moving
if key_left || key_right || key_down || key_up {
moving = true;
} else {
moving = false;
}

// Move the object
var move_xpos, move_ypos;
move_xpos = abs(xpos);
move_ypos = abs(ypos);

if moving == true {
if enable_diagonal_movement == true {
if move_xpos {
x += hspd * cos(move_direction) * delta;
}
if move_ypos {
y += vspd * -sin(move_direction) * delta;
}
} else {
// Disable Diagonal Movement
if move_xpos + move_ypos == 1 {
x += hspd * cos(move_direction) * delta;
y += vspd * -sin(move_direction) * delta;
}
}
}

// Keep Object within the map boundries
x=clamp(x,0,room_width-sprite_width);
y=clamp(y,0,room_height-sprite_height);``````
Tutorial:

So let me explain a few things as to why I'm doing this way over another way and why I prefer using math over built in functions. So the formula I'm using to move the player is using sin and cos, these are trigonometric functions which are essential lengthdir_x or y, and in my experience, they don't work as well you were to write the code yourself. The same can be said for converting Radians by math vs the built-in function radtodeg.

So, with that out of the way let's explain the code!
In our create event we see 2 variables:

Code:
``````move_speed = 200;
enable_diagonal_movement = true;``````
The first variable is our movement speed variable, I found 200 to be a decent speed. The reason it needs to be 200 is because speed is controlled by delta time, so if I were to remove 200 it would move 200 frames in whatever direction per second and that's not good.. 2 is good, but everyone has different hardware and we need to incorporate delta time because of this so it doesn't feel like a drag if, for example, you have 100 enemy objects on the screen. For you, that's probably nothing, but another person is struggling to run the game. This is why we need to use delta time whenever we need to properly time something.

The second variable is if you don't want diagonal movement in your game, setting it to false will only allow the object to move in 4 directions which is common by default in RPG Maker.

The step event is pretty basic, we start by creating a few variables for our input which is nice so we don't have to call
Code:
``keyboard_check(key)``
every time we need to call some input. The next section will set up our horizontal speed and vertical speed as well as our direction. The reason why it's important to split up horizontal and vertical it's so you can control them independently.

The next step we need to get our directions in radians, we could use point_direction but from my understanding point_direction get the angle in degrees which is not something we want.. it's important to always use radians > degrees it's a rule in game development I won't break. You are welcome to use point direction, which is called by
Code:
``var move_direction = point_direction(0, 0, xpos, ypos);``
but for this tutorial, I will be sticking with radians.

Now we can move on to moving our object or player.

We first need to check if we're moving, this can be done a few ways and one way to do it is by doing the following.
Code:
``````var moving;
if xpos && ypos {
moving = true;
} else {
moving = false;
}``````
The way I check to see if we're moving is to check if any input variable is pressed.
Code:
``````var moving
if key_left || key_right || key_down || key_up {
moving = true;
} else {
moving = false;
}``````
It works the same as the above method but I find it's easier to read, so use which way works for you

The next step is to check if the input is 1 or -1, and 0 if nothing is pressed we do that by inputting the following code.
Code:
``````var move_xpos, move_ypos;
move_xpos = abs(xpos);
move_ypos = abs(ypos);``````
Now that most of our code is written we just check if we want to move diagonally and to check if the object is moving. So, it should look like the following.
Code:
``````var move_xpos, move_ypos;
move_xpos = abs(xpos);
move_ypos = abs(ypos);

if moving == true {
if enable_diagonal_movement == true {
if move_xpos {
x += hspd * cos(move_direction) * delta;
}
if move_ypos {
y += vspd * -sin(move_direction) * delta;
}
} else {
// Disable Diagonal Movement
if move_xpos + move_ypos == 1 {
x += hspd * cos(move_direction) * delta;
y += vspd * -sin(move_direction) * delta;
}
}
}``````
So that's about it, we checked if we can move and check if we can move diagonally now let me explain what's going on inside the movement portion of this code.

Code:
``x += hspd * cos(move_direction) * delta;``
Because it was important that we kept our project using radians over degrees we use cos, if you don't want to use radians you can use dcos the results should be the same. So we're taking our horizontal speed than we multiply that by cos(move_direction) and then multiply by delta time. And it's the same for our vertical axis but we use the sin function, we add a minus sign in front of sin since game maker interpolates things...

And that's it, we use the same code for our 4-direction method but we just check if our inputs are 1, so this will limit our movement to 4 directions.

https://en.wikipedia.org/wiki/Trigonometric_functions

If you have any questions don't hesitate to ask

Last edited by a moderator:

#### Juju

##### Member
dsin() and dcos() are in-built functions and use degrees as units. Also your RadiansToDegrees() script is a bit confusing as a name, and point_direction() does the same job. If you do want to swap between radians and degrees then GM also has functions for that too: radtodeg() / degtorad()

R

#### Rukiri

##### Guest
dsin() and dcos() are in-built functions and use degrees as units. Also your RadiansToDegrees() script is a bit confusing as a name, and point_direction() does the same job. If you do want to swap between radians and degrees then GM also has functions for that too: radtodeg() / degtorad()
There is nothing wrong with using built in functions as long as *YOU KNOW THE MATH BEHIND THEM! I'm the type of person who would rather know/learn the math behind the function than just use a function because it works.

I generally write my own functions if I know the math, I was aware of built in functions that basically do the same job

#### ChessMasterRiley

##### Member
There is nothing wrong with using built in functions as long as *YOU KNOW THE MATH BEHIND THEM! I'm the type of person who would rather know/learn the math behind the function than just use a function because it works.

I generally write my own functions if I know the math, I was aware of built in functions that basically do the same job
I like the idea of learning the concept before implementing your solution, but I would guess the included functions are significantly faster due to being precompiled with the engine. I could be wrong, so it may be worth testing since this is a script that is called every single step.