In this post I’ll talk about how you can use the \(sin(x) \) function to give some smoother feeling acceleration and deceleration to your player in Gamemaker Studio 2. I’ll mainly focus on horizontal movement which you may use in a sidescrolling game, but of course it may be extended to a top down game.

I’ll use the following variables:
\(xspeed \) – this is the current horizontal speed of the player
\(maxxspeed \) – this is the maximum allowed horizontal speed of the player
\(acceleration \) – an arbitrary value to control the rate of acceleration
\(hinput \) – horizontal input, a value between -1 and 1

One standard method for movement is to use acceleration and deceleration at a constant rate, for example to speed up:

\(xspeed+=hinput\times acceleration;\)

And for slowing down:

\(xspeed-=hinput\times acceleration;\)

Linear Acceleration

I wanted to try something different and get acceleration working at a non-constant rate. In the case of Other Minds, I wanted it such that the higher your horizontal speed is, the faster the rate of acceleration. I can use a sine wave over the period \(x=[0,\frac{\pi}{2}]\) for this. (Note that \(sin(x) \) uses radians, \(\frac{\pi}{2}\) radians = 90 degrees. \(dsin(x) \) is the equivalent function that uses degrees).

So using this principle, I started with:

\(var\: dx=hinput\times sin(xspeed); \\
xspeed+=dx;\)

Where \(dx\) is the change in \(xspeed\). This has a few problems; firstly I want to limit \(sin(x) \) to the range \(x=\left[0,\frac{\pi}{2}\right]\). Secondly, when \(xspeed=0 \), \(dx=0 \) so the player will never actually accelerate at all.

We will introduce a couple of new variables – \(acceleration \) and \(maxxspeed \). We will also limit the range of \(sin(x)\), as discussed. Note that we also use the absolute value of \(xspeed\) so \(dx\) is always positive for positive hinput (and negative for negative hinput). The above formula now becomes:

\(var\: dx=hinput\times acceleration\times \left(1+sin\left(\frac{\mid xspeed\mid}{maxxspeed}\times \frac{\pi}{2}\right)\right);\\
xspeed+=dx;\)

When \(xspeed=0\), \(sin\left(\left(\frac{\mid xspeed\mid}{maxxspeed}\right)\times \frac{\pi}{2}\right)=0\), so we have added \(hinput\times acceleration\) to \(dx\), ensuring we have a minimum acceleration.

Finally we just need a few extra lines to make sure we don’t exceed the max speed. We would also like to actually move the player.

\(xspeed=clamp(xspeed,-maxxspeed,maxxspeed);\\
x+=xspeed;\)

Now the only problem left is that the player never slows down.
To reduce the speed we use the following:

\(var \:dx=-2\times sign(xspeed)\times acceleration\times \left(1+sin\left(\frac{\mid xspeed\mid}{maxxspeed}\times\frac{\pi}{2}\right)\right);\\ xspeed+=dx;\)

The rate at which we decelerate is multiplied by 2 otherwise we get an ‘ice physics’ sort of feel. This is arbitrary and may be adjusted this to your liking.

Smooth acceleration and deceleration

This all forms a great basis to start playing around. I use a modified version of this in Other Minds – I wanted something I would be able to tweak and adapt to use with many different characters. This is as follows:

\(var\: dx=\frac{hinput}{2\times accelerationreductionfactor} \times \left(sin \left( \frac{\mid xspeed \mid}{maxxspeed} \times \frac{\pi}{2} \right) + acceleration\right) \)

Note the addition of \(accelerationreductionfactor\) which I can use to further fine tune my motion. I multiply the \(sin(x) \) function by a constant of \(\frac{1}{2 \times accelerationreductionfactor} \), whereas before it was multiplied by \(acceleration\). Again, this value may be modified to your liking.

Deceleration is as follows:

\(var \: dx=\frac{1.5}{accelerationreductionfactor}\times sin\left(\frac{\pi \times \mid xspeed\mid}{2\times maxxspeed} \right); \\ dx=sign(xspeed)\times max(dx,1.5 \times acceleration); \\ xspeed-=dx \)

I chose this for a very rapid deceleration to keep the controls feeling tight and responsive, there is nothing worse than hitting an enemy or falling off an edge because of slippery controls.

A chart showing a comparison between the smooth-style acceleration outlined throughout this post and the linear-style acceleration outlined at the beginning of the post
Values used: acceleration=0.05, maxxspeed=4, accelerationreductionfactor=1.5

Code blocks:

Create event:

xspeed=0;
maxxspeed=4;
acceleration=0.05;
accelerationreductionfactor=1.5;

Step event:

var hinput=keyboard_check(vk_right)-keyboard_check(vk_left);
var dx=0.5*(hinput/accelerationreductionfactor)*(sin(pi*0.5*abs(xspeed)/maxxspeed)+acceleration);
if (hinput==0)
     {
          dx=-1.5*sign(xspeed)*max(acceleration,(1/accelerationreductionfactor)*sin(pi*0.5*abs(xspeed)/maxxspeed));
     }
xspeed=clamp(xspeed,-maxxspeed,maxxspeed);

//This sets xspeed to 0 if we are moving sufficiently slowly, may be necessary if you're using non-integer hinput values, for example using analog sticks as input
if (abs(xspeed)<acceleration*0.5 and hinput==0)
     {
          xspeed=0;
     }
x+=xspeed;

Any code is free to use for however you see fit. Credit not needed but is appreciated. Feel free to send me your projects if you do make use of it.

Talk about this post and suggest new blog topics in my Community Discord!

If you like my work, consider supporting me on Ko-fi!