Movement System

Hello. I am currently making a movement system for a game. Idk what the game is gonna be yet, but I have what I think to be a decent movement system implemented. Granted, I am biased since I made it and know how everything works and the best ways to go fast and not hit walls and such. The black square is a trampoline. It’s there so u can gain speed quickly.

The 2 UI elements are for speed; the top, smaller one is your horizontal speed and the bottom, bigger one is your total speed (horizontal and vertical).

Also, if you are a scripter, please give me feedback on my script (variable/function names, optimizations, features). The script is located under StarterCharacterScripts and is called “Movement”. Note that this is a very ugly and poorly written script. I do not feel like making a script pretty if I’m not sure I’m going to implement all of the features currently present in it.

There is also some background music because I got bored play testing and wanted something to listen to.

Place file:
movement showcase.rbxl (94.2 KB)

Movement code if u don’t wanna download the place:

local TS = game:GetService("TweenService")

local char: Model = script.Parent
local humanoid: Humanoid = char.Humanoid
local hrp: Part = char:FindFirstChild("HumanoidRootPart")
local torso: Part = char:FindFirstChild("Torso") or char:FindFirstChild("UpperTorso")

--speeds
--set to 0 for no walking and only sprinting (not recommended, instead set it to 16 or so for better movement)
local DEFAULT_SPEED = game:GetService("StarterPlayer").CharacterWalkSpeed
local SPRINT_SPEED = 50
local MAX_SPEED = 200 --math.huge for infinity/no max speed

local BODY_VELOCITY_TIME = 0.3 --amount of time the BodyVelocity impacts the player after they jump for

local BUNNY_HOP_SPEED_MULTIPLIER = 1.2 --recommended: 1.2
local FREEFALL_SPEED_MULTIPLIER = 1.5

--amount of time, in seconds, that a player can be on the ground for and still gain speed if they jump again
local BUNNY_HOP_GRACE_PERIOD = 0.2

local SPRINT_TIME = 2 --amount of time, in seconds, that the SPRINT_SPEED takes to reach from DEFAULT_SPEED

local SLOW_DOWN_TIME = 0.6

local WALL_BOUNCE_SPEED_MULTIPLIER = 1

local speedTweenPlaying = false
local slowTweenPlaying = false

local bunnyHopping = true
local running = false

local timeSinceLanded = 0
local timeSinceJump = math.huge
local timeAirborne = math.huge
local justEnteredFreefall = false

local freefallTweenComplete = true
local freefallTween: Tween = nil

local gui = game:GetService("Players").LocalPlayer.PlayerGui:WaitForChild("ScreenGui")
local totalSpeedLabel = gui:WaitForChild("Total Speed")
local horizontalSpeedLabel = gui:WaitForChild("Horizontal Speed")

local function roundNum(num: number): string
	local s = tostring(num)
	local dot = s:find("%.")
	return s:sub(1, dot and dot+1 or #s)
end

local function isPlayerJumping()
	return humanoid:GetState() == Enum.HumanoidStateType.Jumping
end
local function isPlayerRunning()
	return humanoid.MoveDirection.Magnitude > 0
end
local function slowDown()
	TS:Create(humanoid, TweenInfo.new(SLOW_DOWN_TIME, Enum.EasingStyle.Quart, Enum.EasingDirection.In), {["WalkSpeed"] = 
		running and SPRINT_SPEED or DEFAULT_SPEED}):Play()
end

humanoid.StateChanged:Connect(function(old: Enum.HumanoidStateType, new: Enum.HumanoidStateType) 
	if new == Enum.HumanoidStateType.Freefall then
		justEnteredFreefall = true
	end
	if new == Enum.HumanoidStateType.Jumping then
		timeSinceJump = os.clock()
	end
end)
--wall bouncing
local wallDebounce = false
torso.Touched:Connect(function(hit: BasePart)
	--print((hrp.AssemblyLinearVelocity * Vector3.new(1,0,1)).Magnitude)
	local horizontalMomentum = (hrp.AssemblyLinearVelocity * Vector3.new(1,0,1)).Magnitude
	if not wallDebounce then
		if (bunnyHopping or os.clock() - timeAirborne > 0.5) and (humanoid:GetState() == Enum.HumanoidStateType.Freefall or 
			humanoid:GetState() == Enum.HumanoidStateType.Freefall or isPlayerJumping()) and 
			horizontalMomentum > SPRINT_SPEED * 0.75 then
			wallDebounce = true

			local params = RaycastParams.new()
			params.IgnoreWater = true
			params.CollisionGroup = "Player"

			local ray = workspace:Raycast(torso.Position, torso.CFrame.LookVector * 3, params)

			if ray and ray.Instance then
				print(horizontalMomentum)
				
				local lv = Instance.new("LinearVelocity")
				lv.ForceLimitsEnabled = false
				lv.RelativeTo = Enum.ActuatorRelativeTo.World
				lv.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
				lv.LineDirection = ray.Normal
				lv.LineVelocity = horizontalMomentum * WALL_BOUNCE_SPEED_MULTIPLIER
				lv.Attachment0 = hrp:FindFirstChild("MovementAttachment") or hrp:FindFirstChildWhichIsA("Attachment")
				lv.Parent = hrp

				game:GetService("Debris"):AddItem(lv, 0.2)
			end
			task.wait(1)
			wallDebounce = false
		end
	end
end)

local isBhopTweenPlaying = false
while task.wait() do
	--update text (can't use `GetPropertyChangedSignal` because it doesn't fire for physics-related properties)
	local currentSpeed = hrp.AssemblyLinearVelocity
	local horizontalSpeed = currentSpeed * Vector3.new(1,0,1)
	totalSpeedLabel.Text = roundNum(currentSpeed.Magnitude)
	horizontalSpeedLabel.Text = roundNum(horizontalSpeed.Magnitude)
	
	--sprinting
	local relativeSpeedUpTime = SPRINT_TIME*(SPRINT_SPEED-humanoid.WalkSpeed)/(SPRINT_SPEED-DEFAULT_SPEED)
	if isPlayerRunning() and not running then
		TS:Create(humanoid, TweenInfo.new(relativeSpeedUpTime, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {["WalkSpeed"] = SPRINT_SPEED}):Play()
		running = true
	end
	if running and not isPlayerRunning() then
		TS:Create(humanoid, TweenInfo.new(SLOW_DOWN_TIME, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {["WalkSpeed"] = DEFAULT_SPEED}):Play()
		running = false
	end
	
	--bunny hopping
	if humanoid:GetState() == Enum.HumanoidStateType.Landed then
		timeSinceLanded = os.clock()
		
		if os.clock() - timeAirborne > 0.5 then
			slowDown()
		end
		timeAirborne = math.huge
		if freefallTween then freefallTween:Cancel() end
	end
	if justEnteredFreefall then
		if humanoid:GetState() == Enum.HumanoidStateType.Freefall and os.clock() - timeSinceJump > 0.5 then
			justEnteredFreefall = false
			timeAirborne = os.clock()
		end
	end

	if os.clock() - timeAirborne > 0.5 and freefallTweenComplete then
		freefallTweenComplete = false

		freefallTween = TS:Create(humanoid, TweenInfo.new(0.5, Enum.EasingStyle.Linear, Enum.EasingDirection.In), {["WalkSpeed"] = 
			math.clamp(humanoid.WalkSpeed * FREEFALL_SPEED_MULTIPLIER, SPRINT_SPEED, MAX_SPEED)})
		freefallTween:Play()
		freefallTween.Completed:Connect(function()
			freefallTweenComplete = true
		end)
	end
	
	if isPlayerJumping() and running and humanoid.WalkSpeed >= SPRINT_SPEED - 0.5 and 
		horizontalSpeed.Magnitude >= SPRINT_SPEED - 0.5 then
		if os.clock() - timeSinceLanded < BUNNY_HOP_GRACE_PERIOD then
			TS:Create(humanoid, TweenInfo.new(0.6, Enum.EasingStyle.Quart, Enum.EasingDirection.In), {["WalkSpeed"] = 
				math.clamp(humanoid.WalkSpeed * BUNNY_HOP_SPEED_MULTIPLIER, SPRINT_SPEED, MAX_SPEED)}):Play()
			
			local lv = Instance.new("LinearVelocity")
			lv.ForceLimitsEnabled = true
			lv.ForceLimitMode = Enum.ForceLimitMode.PerAxis
			lv.MaxAxesForce = Vector3.new(1,0,1) * 2e3
			lv.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector
			lv.VectorVelocity = humanoid.MoveDirection * humanoid.WalkSpeed
			lv.Attachment0 = hrp:FindFirstChild("MovementAttachment") or hrp:FindFirstChildWhichIsA("Attachment")
			lv.Parent = hrp
			
			game:GetService("Debris"):AddItem(lv, BODY_VELOCITY_TIME)
			
			bunnyHopping = true
		end
		continue
	else
		if bunnyHopping then
			if humanoid:GetState() == Enum.HumanoidStateType.Freefall then continue end
			if os.clock() - timeSinceLanded > BUNNY_HOP_GRACE_PERIOD or currentSpeed.Magnitude <= SPRINT_SPEED - 0.5 then	
				slowDown()
				bunnyHopping = false
			end
		end
	end
end
1 Like