Creating heavy character movement (Beginner tutorial) - Acceleration and deceleration

This is a short tutorial! On Roblox, there’s been a long-standing issue of adding acceleration and deceleration to your character movement to make it feel “heavier.” It’s a key part of more realistic games. However, so far with walkspeed-based methods, deceleration hasn’t always been an obvious solution. So, find out how to add it yourself!

Experience level: Beginner
Time required: 5 mins

Note: Script line numbers might be innacurate to ± 10 lines! Refer to the screenshots to make sure your code is in the right place.

Video example

Video example here!

Download the .rbxl example here:
HeavyMovementDemo.rbxl (184.2 KB)


Forking the PlayerModule

  1. Playtest your game in Roblox Studio, and look for Players > [your username] > PlayerScripts > PlayerModule. Copy the “PlayerModule” ModuleScript.
    image
    PlayerModule at the bottom of the screenshot

  2. Stop the playtest and paste the “PlayerModule” into StarterPlayer > StarterPlayerScripts
    image


Modifying the PlayerModule

  1. Expand “PlayerModule” and open the “ControlModule” ModuleScript.

  2. Navigate to around line 105 (Use Ctrl + G or Cmd + G to quickly jump there). Past the following snippet of code:

-- Custom accel/decel variables
self.speedRatio = 0
self.retainedMoveDirection = Vector3.new()

image

  1. Navigate to around line 530. Paste this snippet of code:
-- Accelerate when player is trying to move
if moveVector.Magnitude > 0.1 then
	-- accelTime to reach full speed in seconds
	local accelTime = 0.5
	self.speedRatio = math.min(self.speedRatio + dt * (1 / accelTime), 1)
	
	-- apply player input relevant moveVector
	self.retainedMoveDirection = moveVector
else
	-- Decelerate when they stopped
	-- decelTime to reach full stop in seconds
	local decelTime = 0.12
	self.speedRatio = math.max(self.speedRatio - dt * (1 / decelTime), 0)
	
	-- "freeze" the retainedMoveDirection, because moveVector is now a zero vector
end
  1. Replace the line self.moveFunction(Players.LocalPlayer, moveVector, false) with the following:
local customMoveVector = self.retainedMoveDirection * self.speedRatio
self.moveFunction(Players.LocalPlayer, customMoveVector, false)

  1. Playtest, and you now should be accelerating and decelerating when you move!

(Advanced) Easing the acceleration

This is entirely optional, but will make movement feel a lot better for players. Basically, it applies the constant acceleration from 0-100% to an EasingStyle, to “arc” the acceleration. For example, Quad (Out) looks like this: (x-axis is speedRatio and y-axis is easedSpeedRatio)
image

  1. Define TweenService outside of the ControlModule.OnRenderStepped function.

  2. Paste the following snippet, replacing the self.moveFunction(...) and the previous code you pasted.

-- Applies speedRatio (0-1) to an EasingStyle to control the "feel" of the acceleration/deceleration
local easedSpeedRatio = TweenService:GetValue(self.speedRatio, Enum.EasingStyle.Quart, Enum.EasingDirection.Out)
local customMoveVector = self.retainedMoveDirection * easedSpeedRatio
self.moveFunction(Players.LocalPlayer, customMoveVector, false)

  1. Playtest, and movement should feel noticeably smoother! You can change the EasingStyle and EasingDirection however you’d like. For faster initial acceleration, use EasingStyle.Circular. For a “tripping” feel, use EasingStyle.Back. Refer to this page for visuals on each style and direction.
Was this tutorial easy to follow? (0- difficult, unclear and 5- easy, intuitive)
  • 1
  • 2
  • 3
  • 4
  • 5

0 voters

12 Likes

Can you post a .rbxl of the modifications?

Thanks

Just added it to the topic, hope it helps. (It’s at the top of the post)

Are you able to show a preview of what it looks like? Currently scrolling through this on mobile (and I’m not the only one) so can’t test it out immediately.

Just added it! The acceleration time is exaggerated to be clearly visible on the video. In reality, you’d make it subtler.

2 Likes

Seems really easy to do, nice tutorial.

1 Like

Can this be applied to NPC’s too?

This only works for Players as they control the Humanoid. For NPCs, this would need to be scripted in alongside the NPC script you’re using.

1 Like

Does the .rbxl file already have things set up?

It seems to move like normal to me…

I just downloaded it to test, and yes it’s working. It’s pretty exaggerated in the example too, but if you want to test it and be 100% sure, open StarterPlayerScripts > PlayerModule > ControlModule and change line 536 to:

local accelTime = 2

This increases the time taken to reach full speed to 2 seconds.