Need help with improving this Movement Code

Hey! I’ve made this movement system focused on Bhoping, but i’m having a lot of trouble with optimization. I can see a bit why this causes frame drops, but i can’t find i way to optimize it.

Any help on improving the code would be great. Even if it’s not optimization.
OBS: I sadly couldn’t comment all of the code. But for a better read i commented what every function is.

This is the code ( LocalScript at StarterCharacterScripts ):

-- local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")
local TS = game:GetService("TweenService")
local Library = require(workspace.SukytaPersonalModule) -- My Personal Library.
local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded
local Camera = workspace.CurrentCamera
local Hum = Character:FindFirstChildOfClass("Humanoid")
local RootPart = Character:WaitForChild("HumanoidRootPart")

local State = "N/A"
local Is_Running = false

local Settings = {
	-- Others.
	MaxSpeed = 150, -- Caps out speed.
	
	-- Walk.
	WalkNtoMaxTime = 1.8, -- How much seconds it takes for the player to go from WalkStartSpeed to WalkSpeed / RunSpeed.
	WalkStartSpeed = 6, -- Starting walkspeed.
	WalkSpeed = 15, -- The walkspeed.
	
	-- Run.
	RunEnabled = true, -- Determines if player can run.
	RunKey = Enum.KeyCode.LeftControl, -- Key pressed to run.
	RunSpeed = 35, -- The WalkSpeed when running.
	
	-- Slide.
	SlideEnabled = false, -- Determines if player can slide.
	SlideKey = Enum.KeyCode.C, -- Key pressed to slide.
	
	-- Bhop.
	BhopEnabled = false, -- Determines if player can bhop or not.
	AutoBhopEnabled = true, -- Determines if the Player can just hold down the spacebar to bhop. Recommended for bhoping easier.
	CapNoStrafeBhop = true, -- Determines if bhopping without strafing will have a speed limit.
	BhopMaxSpeed = 60, -- Caps out the speed of bhoping without strafing.
	BhopSpeedIncrement = 0, -- Brute base acceleration for bhop. Recommended: 0 - 1.
	BhopAcceleration = 7.5, -- Increases the acceleration of the bhop. Acceleration increases with bhop. Recommended: 5 - 25
	BhopHorizontalConvertPercentage = 5, -- Determines how much of Horizontal speed will be converted to vertical speed when landing. Max: 100
	
	-- Air.
	AirEnabled = false, -- Determines if air is enabled or not. Staying at air won't affect your speed. --> Strafe will continue working even if this is off.
	AirAcceleration = 2.5, -- Determines how fast you accelerate at air. Recommended: 1 - 3.5. !!! Affects bhoping heavily !!!
	
	-- Strafe.
	StrafeEnabled = false, -- Determines if strafe will be enabled or not. !!! If strafe is not enabled, deactivate CapNoStrafeBhop !!!
	StrafeAcceleration = 15, -- Increases base strafe acceleration. Recommended: 7.5 - 20.
	StrafeStrength = 15, -- Increases overall strafe speed increase. Recommended: 5 - 20.
	StrafeSensitivity = 2.1, -- Increases how much you need to move your camera for strafe to work. Recommended: 2 - 3.
	
	-- Camera.
	CameraFirstPerson = false, -- Determines if camera will be locked at first person. !!! Turning this off will turn strafe off ( cus strafe only works with first person ) !!! Imagine being bad at scripting.
	CameraSwayEnabled = false, -- Determines if camera will have a sway when moved. If not at first person this may bug your camera.
	CameraSwayStrength = 2.4, -- Determines how strong sway is. Recommended: 2 - 4.
	SpeedAffectsFov = true, -- Determines if speed affects camera Fov.
	CameraBaseFov = 70, -- Normal camera Fov. Recommended: 70 - 80.
	CameraMaxFov = 120, -- Caps out the Camera Fov. Recommended: 110 - 120.
	CameraOffset = Vector3.new(0, 0.5, -1), -- Determines the camera offset. --> Won't affect camera if first person is not enabled.
	CameraFovSensitivity = 6 -- Changes how much Fov will be affected by speed. --> Minimum: 0 | Maximum: 10 | Recommended: 4 - 6.
}

---------------------------------- Putting invalid values or values out of the min-max range will cause errors!
---------------------------------- Having really low BhopAcceleration may cause bhop to be useless!

Camera.FieldOfView = Settings.CameraBaseFov
if Settings.CameraFirstPerson == true then Player.CameraMode = Enum.CameraMode.LockFirstPerson else Player.CameraMode = Enum.CameraMode.Classic end
if Settings.CameraFirstPerson == true then Hum.CameraOffset = Settings.CameraOffset end

local Tween = TS:Create(Hum, TweenInfo.new(Settings.WalkNtoMaxTime, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {WalkSpeed = Settings.WalkSpeed})
local BaseWalkSpeed = Settings.WalkSpeed
local NextSpeed = 0
local AirSpeed = 0
local Chain = 0 -- Bhop Chain --> Can be used to grant points or just as a stat.

local Turn = 0
local BhopResetTime = if Settings.AutoBhopEnabled == true then 0.02 else 0.085


-------------------------- Helping Functions --------------------------


local function Flatten(vec: Vector3): Vector2
	return Vector2.new(vec.X, vec.Z).Unit
end
local LastDirection = Flatten(Camera.CFrame.LookVector)

local Lerp = function(a, b, t)
	return a + (b - a) * t
end;


-------------------------- Movement Code --------------------------


Hum.StateChanged:Connect(function(_, NewState: Enum.HumanoidStateType) -- Resetting bhop after landing.
	if NewState == Enum.HumanoidStateType.Landed then
		local function A()
			if Hum.FloorMaterial ~= Enum.Material.Air then
				task.wait(0.1)
				if Hum.FloorMaterial ~= Enum.Material.Air then
					task.wait(BhopResetTime)

					if Hum.FloorMaterial ~= Enum.Material.Air then
						local NewTween = TS:Create(Hum, TweenInfo.new(0.1, Enum.EasingStyle.Quint, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = BaseWalkSpeed})
						local NewTween2 = TS:Create(Camera, TweenInfo.new(0.75, Enum.EasingStyle.Quart, Enum.EasingDirection.Out, 0, false, 0), {FieldOfView = Settings.CameraBaseFov})
						NewTween:Play()
						NewTween2:Play()
						task.wait(0.11)
						Hum.WalkSpeed = BaseWalkSpeed
						Chain = 0
						State = "N/A"
					end
				end
				return
			end
		end

		coroutine.wrap(A)()
	end
end)



local function AutoBhop(IsJumping) -- Auto Bhop.
	if Settings.BhopEnabled == false then return end
	if Hum.FloorMaterial == Enum.Material.Air then return end
	if IsJumping then
		local Success = pcall(function()
			State = "Bhoping"
			if Hum.WalkSpeed < Settings.MaxSpeed then
				if AirSpeed > 0 then AirSpeed -= (20 / 100) * AirSpeed end

				if Hum.WalkSpeed < Settings.BhopMaxSpeed + AirSpeed or Settings.CapNoStrafeBhop == false then
					NextSpeed = ((1.6 - (1.6 / (100 - Settings.BhopAcceleration))) * (Hum.WalkSpeed / 1.5)) - math.log(Hum.WalkSpeed, 4.5)
					if Is_Running == true then NextSpeed *= 1.1 end
					NextSpeed += Settings.BhopSpeedIncrement
					NextSpeed += AirSpeed
				elseif Hum.WalkSpeed > Settings.BhopMaxSpeed + AirSpeed and Settings.CapNoStrafeBhop == true then
					NextSpeed = Settings.BhopMaxSpeed + AirSpeed
				end

				if NextSpeed > Settings.BhopMaxSpeed + AirSpeed then NextSpeed = Settings.BhopMaxSpeed + AirSpeed end
				if NextSpeed > Settings.MaxSpeed then NextSpeed = Settings.MaxSpeed - (1 + AirSpeed + 1) end
				
				if RootPart.Velocity.Y < -15 then
					NextSpeed += (Settings.BhopHorizontalConvertPercentage / 100) * -RootPart.Velocity.Y -- Converter.
					task.wait()
					RootPart.Velocity.Y = 1
				end
				
				Chain += 1
				
				local BhopTween = TS:Create(Hum, TweenInfo.new(0.15, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = NextSpeed})
				BhopTween:Play()
				if Settings.SpeedAffectsFov == true then
					local NewTween = TS:Create(Camera, TweenInfo.new(0.1, Enum.EasingStyle.Quint, Enum.EasingDirection.Out, 0, false, 0), {FieldOfView = Settings.CameraBaseFov + Hum.WalkSpeed / (10 - Settings.CameraFovSensitivity)})
					NewTween:Play()
				end
			else
				Hum.WalkSpeed = Settings.MaxSpeed
			end
		end)
	end
end

local function ManualBhop(Input, Processed) -- Manual Bhop
	if Settings.BhopEnabled == false then return end
	if Hum.FloorMaterial == Enum.Material.Air then return end
	if Processed == true then return end
	if Input.UserInputType ~= Enum.UserInputType.Keyboard then return end
	if Input.KeyCode ~= Enum.KeyCode.Space then return end
	if Settings.BhopEnabled == false then return end
	
	local Success = pcall(function()
		State = "Bhoping"
		if Hum.WalkSpeed < Settings.MaxSpeed then
			if AirSpeed > 0 then AirSpeed -= (20 / 100) * AirSpeed end

			if Hum.WalkSpeed < Settings.BhopMaxSpeed + AirSpeed or Settings.CapNoStrafeBhop == false then
				NextSpeed = ((1.6 - (1.6 / (100 - Settings.BhopAcceleration))) * (Hum.WalkSpeed / 1.5)) - math.log(Hum.WalkSpeed, 4.5)
				if Is_Running == true then NextSpeed *= 1.1 end
				NextSpeed += Settings.BhopSpeedIncrement
				NextSpeed += AirSpeed
			elseif Hum.WalkSpeed > Settings.BhopMaxSpeed + AirSpeed and Settings.CapNoStrafeBhop == true then
				NextSpeed = Settings.BhopMaxSpeed + AirSpeed
			end

			if NextSpeed > Settings.BhopMaxSpeed + AirSpeed then NextSpeed = Settings.BhopMaxSpeed + AirSpeed end
			if NextSpeed > Settings.MaxSpeed then NextSpeed = Settings.MaxSpeed - (1 + AirSpeed + 1) end

			if RootPart.Velocity.Y < -15 then
				NextSpeed += (Settings.BhopHorizontalConvertPercentage / 100) * -RootPart.Velocity.Y -- Converter.
				task.wait()
				RootPart.Velocity.Y = 1
			end

			local BhopTween = TS:Create(Hum, TweenInfo.new(0.1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = NextSpeed})
			BhopTween:Play()
			if Settings.SpeedAffectsFov == true then
				local NewTween = TS:Create(Camera, TweenInfo.new(0.1, Enum.EasingStyle.Quint, Enum.EasingDirection.Out, 0, false, 0), {FieldOfView = Settings.CameraBaseFov + Hum.WalkSpeed / (10 - Settings.CameraFovSensitivity)})
				NewTween:Play()
			end
		else
			Hum.WalkSpeed = Settings.MaxSpeed
		end
	end)
end

local function Strafe() -- Strafe.
	if Settings.StrafeEnabled == true then return end
	if Hum.FloorMaterial ~= Enum.Material.Air then return end
	
	--[[
	
		Strafe gives speed based on how much player moved his camera.
	
	]]
	
	local Direction = Flatten(Camera.CFrame.LookVector)
	local Angle = math.asin(LastDirection:Cross(Direction))
	if Angle ~= 0 and Library.NumbersMagnitude(Angle, 0) > (Settings.StrafeSensitivity / 50) and Library.NumbersMagnitude(Angle, 0) < Settings.StrafeSensitivity * 2.5 then
		local NewAirSpeed = ((Settings.StrafeAcceleration / (2 - (Settings.StrafeStrength / 100))) / math.log(Hum.WalkSpeed, 11)) + Library.TurnPositive(Angle)
		NewAirSpeed = (Settings.StrafeStrength / 100) * NewAirSpeed
		if Hum.WalkSpeed + NewAirSpeed > Settings.MaxSpeed then NewAirSpeed = 0 end
		AirSpeed += NewAirSpeed
		Hum.WalkSpeed += NewAirSpeed
	elseif Library.NumbersMagnitude(Angle, 0) > 5 then
		local NewAirSpeed = -(((Settings.StrafeAcceleration / 15) / math.log(Hum.WalkSpeed, 13)) - Library.TurnPositive(Angle)) - Settings.StrafeStrength
		AirSpeed += NewAirSpeed
		Hum.WalkSpeed += NewAirSpeed
	end
	LastDirection = Direction
end

local function Sway(DT)
	local MouseDelta = UIS:GetMouseDelta()

	Turn = Lerp(Turn, math.clamp(MouseDelta.X, -Settings.CameraSwayStrength, Settings.CameraSwayStrength), (Settings.CameraSwayStrength * DT))

	Camera.CFrame = Camera.CFrame * CFrame.Angles(0, 0, math.rad(Turn))
end

if Settings.StrafeEnabled == true then
	Hum:GetPropertyChangedSignal("MoveDirection"):Connect(function()
		if State ~= "Bhoping" and Hum.FloorMaterial ~= Enum.Material.Air then return end

		if math.round(Hum.MoveDirection.X) == -1 then
			local NewAirSpeed = ((Settings.AirAcceleration / 1.5) / math.log(Hum.WalkSpeed, 11))
			if Hum.WalkSpeed + NewAirSpeed > Settings.MaxSpeed then NewAirSpeed = Settings.MaxSpeed - Hum.WalkSpeed end
			AirSpeed += NewAirSpeed
			Hum.WalkSpeed += NewAirSpeed
		elseif math.round(Hum.MoveDirection.X) == 1 then
			local NewAirSpeed = ((Settings.AirAcceleration / 1.5) / math.log(Hum.WalkSpeed, 11))
			if Hum.WalkSpeed + NewAirSpeed > Settings.MaxSpeed then NewAirSpeed = Settings.MaxSpeed - Hum.WalkSpeed end
			AirSpeed += NewAirSpeed
			Hum.WalkSpeed += NewAirSpeed
		elseif Hum.MoveDirection == Vector3.new(0, 0, 0) and AirSpeed > 0 then
			local NewAirSpeed = (7.5 / 100) * AirSpeed
			AirSpeed -= NewAirSpeed
			Hum.WalkSpeed -= NewAirSpeed
		end
	end)
end

if Settings.AirEnabled == true then
	Hum:GetPropertyChangedSignal("FloorMaterial"):Connect(function() -- Air Acceleration.
		if Hum.FloorMaterial == Enum.Material.Air then
			local LastChain = Chain
			task.wait(0.5)
			if Hum.FloorMaterial ~= Enum.Material.Air then return end
			repeat
				local NewAirSpeed = (Settings.AirAcceleration / math.log(Hum.WalkSpeed, 8)) * 1.25
				if Hum.WalkSpeed + NewAirSpeed > Settings.MaxSpeed then NewAirSpeed = Settings.MaxSpeed - Hum.WalkSpeed end
				AirSpeed += NewAirSpeed
				Hum.WalkSpeed += NewAirSpeed

				RS.Heartbeat:Wait()
			until Hum.FloorMaterial ~= Enum.Material.Air
			task.wait(0.15)
			if Hum.FloorMaterial ~= Enum.Material.Air and Chain	<= LastChain then AirSpeed = 0 return end
			repeat wait(0.1) until State ~= "Bhoping"
			AirSpeed = 0
		end
	end)
end

Hum:GetPropertyChangedSignal("MoveDirection"):Connect(function() -- Walk Acceleration.
	if State == "Bhoping" or Hum.FloorMaterial == Enum.Material.Air then return end
	if Hum.MoveDirection == Vector3.new(0,0,0) then
		Hum.WalkSpeed = Settings.WalkStartSpeed
		if Tween then
			Tween:Cancel()
		end
		Tween = nil
	elseif not Tween then
		Tween = TS:Create(Hum, TweenInfo.new(Settings.WalkNtoMaxTime, Enum.EasingStyle.Sine, Enum.EasingDirection.In), {WalkSpeed = BaseWalkSpeed})
		Tween:Play()
	end
end)

if Settings.RunEnabled == true or Settings.SlideEnabled == true then
	UIS.InputBegan:Connect(function(Input, Processed) -- Run
		if Processed == true then return end
		if Input.UserInputType ~= Enum.UserInputType.Keyboard then return end

		if Input.KeyCode == Settings.RunKey and Settings.RunEnabled == true then
			Is_Running = not Is_Running
			task.wait()
			if Is_Running == false then
				BaseWalkSpeed = Settings.WalkSpeed
				task.wait()
				if State ~= "Bhoping" and Hum.FloorMaterial ~= Enum.Material.Air then -- Sees if player is bhoping
					local WalkTween = TS:Create(Hum, TweenInfo.new(1.2, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = Settings.WalkSpeed})
					WalkTween:Play()
				end
			else
				BaseWalkSpeed = Settings.RunSpeed
				task.wait()
				if State ~= "Bhoping" and Hum.FloorMaterial ~= Enum.Material.Air then
					local RunTween = TS:Create(Hum, TweenInfo.new(1.2, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = Settings.RunSpeed})
					RunTween:Play()
				end
			end

			wait(1.5)
		elseif Input.KeyCode == Settings.SlideKey and Settings.SlideEnabled == true then
			print("Slide!") -- I did not make it yet.
		end
	end)
end


-------------------------- Connections --------------------------


if Settings.BhopEnabled == true then
	local BhopConnection = if Settings.AutoBhopEnabled == true then Hum.Jumping:Connect(AutoBhop) else UIS.InputBegan:Connect(ManualBhop)
end
	
if Settings.StrafeEnabled == true and Settings.CameraFirstPerson == true then
	RS:BindToRenderStep("UpdateCamera", Enum.RenderPriority.Camera.Value + 1, Strafe)
end

if Settings.CameraSwayEnabled == true then
	RS:BindToRenderStep("SwayCamera", Enum.RenderPriority.Camera.Value + 2, Sway)
end

Also, most equations in this code, such as the equation for adding speed after bhoping, are basically random, meaning i came up with them by just putting some random things here and there until i found a decent result. If you have a better way in mind, i would be grateful if you could say it.

The “Library” used in the code is a Personal Module i made in the mean time, all the functions used in this code are really simple functions, but if you need the code from it, i will reply with it.

2 Likes

Hey! Even though no one replied yet, i did some changes to the code!

  1. Changed a bit how Strafe Works:
    - Strafe speed equation changed to depend on how much you moved the camera.
    - Fixed player being able to gain and maintain speed when mindlessly moving mouse.
    - Nerfed Strafe Speed Increase heavily.

  2. Fixed a lot of equation errors around the code.

  3. Fixed problem with AirSpeed being maintained even after landing.

  4. Hopefully optimized more the code, causing less frame drops.

Here is it:

local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")
local TS = game:GetService("TweenService")
local Library = require(workspace.SukytaPersonalModule) -- My Personal Library.
local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded
local Camera = workspace.CurrentCamera
local Hum = Character:FindFirstChildOfClass("Humanoid")
local RootPart = Character:WaitForChild("HumanoidRootPart")

local State = "N/A"
local Is_Running = false

local Settings = {
	-- Others.
	MaxSpeed = 150, -- Caps out speed.
	RunEnableOrDisableCooldown = 1.5, -- Cooldown for the run key.
	
	-- Walk.
	WalkNtoMaxTime = 1.8, -- How much seconds it takes for the player to go from WalkStartSpeed to WalkSpeed / RunSpeed.
	WalkStartSpeed = 6, -- Starting walkspeed.
	WalkSpeed = 15, -- The walkspeed.
	
	-- Run.
	RunEnabled = true, -- Determines if player can run.
	RunKey = Enum.KeyCode.LeftControl, -- Key pressed to run.
	RunSpeed = 35, -- The WalkSpeed when running.
	
	-- Slide.
	SlideEnabled = false, -- Determines if player can slide.
	SlideKey = Enum.KeyCode.C, -- Key pressed to slide.
	
	-- Bhop.
	BhopEnabled = true, -- Determines if player can bhop or not.
	AutoBhopEnabled = true, -- Determines if the Player can just hold down the spacebar to bhop. Recommended for bhoping easier.
	-- Manual Bhop Jump window: 0.23 seconds.
	CapNoStrafeBhop = true, -- Determines if bhopping without strafing will have a speed limit.
	BhopMaxSpeed = 60, -- Caps out the speed of bhoping without strafing.
	BhopSpeedIncrement = 0, -- Brute base acceleration for bhop. Recommended: 0 - 1.
	BhopAcceleration = 5, -- Increases the acceleration of the bhop. Acceleration increases with bhop. Recommended: 5 - 15.
	BhopHorizontalConvertPercentage = 5, -- Determines how much of Horizontal speed will be converted to vertical speed when landing. Max: 100
	
	-- Air.
	AirEnabled = true, -- Determines if air is enabled or not. Staying at air won't affect your speed. --> Strafe will continue working even if this is off.
	AirAcceleration = 2, -- Determines how fast you accelerate at air. Recommended: 1 - 3. !!! Affects bhoping heavily !!!
	
	-- Strafe.
	StrafeEnabled = true, -- Determines if strafe will be enabled or not. !!! If strafe is not enabled, deactivate CapNoStrafeBhop !!!
	StrafeAcceleration = 1.25, -- Increases base strafe acceleration. Recommended: 1.25 - 1.75.
	StrafeStrength = 2, -- Increases overall strafe speed increase. Recommended: 1 - 2.
	StrafeSensitivity = 10, -- Increases how much you need to move your camera for strafe to work. Recommended: 7.5 - 15.
	
	-- Mouse
	HideMouseIfFirstPerson = true, -- If CameraFirstPerson = true: hides mouse.
	
	-- Camera.
	CameraFirstPerson = true, -- Determines if camera will be locked at first person. !!! Turning this off will turn strafe off ( cus strafe only works with first person ) !!! "I'm bad at scripting sorry" -- Sukyta.
	CameraSwayEnabled = true, -- Determines if camera will have a sway when moved. If not at first person this may bug your camera.
	CameraSwayStrength = 2.4, -- Determines how strong sway is. Recommended: 2 - 4.
	SpeedAffectsFov = false, -- Determines if speed affects camera Fov.
	CameraBaseFov = 70, -- Normal camera Fov. Recommended: 70 - 80.
	CameraMaxFov = 120, -- Caps out the Camera Fov. Recommended: 110 - 120.
	CameraOffset = Vector3.new(0, 0.5, -1), -- Determines the camera offset. --> Won't affect camera if first person is not enabled.
	CameraFovSensitivity = 6 -- Changes how much Fov will be affected by speed. --> Minimum: 0 | Maximum: 10 | Recommended: 4 - 6.
}

---------------------------------- Putting invalid values or values out of the min-max range will cause errors!
---------------------------------- Having really low BhopAcceleration may cause bhop to be useless!

Camera.FieldOfView = Settings.CameraBaseFov
if Settings.CameraFirstPerson == true then Player.CameraMode = Enum.CameraMode.LockFirstPerson if Settings.HideMouseIfFirstPerson == true then UIS.MouseIconEnabled = false end else Player.CameraMode = Enum.CameraMode.Classic end
if Settings.CameraFirstPerson == true then Hum.CameraOffset = Settings.CameraOffset end

local Tween = TS:Create(Hum, TweenInfo.new(Settings.WalkNtoMaxTime, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {WalkSpeed = Settings.WalkSpeed})
local BaseWalkSpeed = Settings.WalkSpeed
local NextSpeed = 0
local AirSpeed = 0
local Chain = 0 -- Bhop Chain --> Can be used to grant points or just as a stat.

local StrafeConnection = nil

local Turn = 0
local BhopResetTime = if Settings.AutoBhopEnabled == true then 0.03 else 0.12


-------------------------- Helping Functions --------------------------


local function Flatten(vec: Vector3): Vector2
	return Vector2.new(vec.X, vec.Z).Unit
end
local LastDirection = Flatten(Camera.CFrame.LookVector)

local Lerp = function(a, b, t)
	return a + (b - a) * t
end;


-------------------------- Movement Code --------------------------


Hum.StateChanged:Connect(function(_, NewState: Enum.HumanoidStateType) -- Resetting bhop after landing.
	if NewState == Enum.HumanoidStateType.Landed then
		local function A()
			if Hum.FloorMaterial ~= Enum.Material.Air then
				task.wait(BhopResetTime + 0.11)
				if Hum.FloorMaterial ~= Enum.Material.Air then
					local NewTween = TS:Create(Hum, TweenInfo.new(0.1, Enum.EasingStyle.Quint, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = BaseWalkSpeed})
					local NewTween2 = TS:Create(Camera, TweenInfo.new(0.6, Enum.EasingStyle.Quart, Enum.EasingDirection.Out, 0, false, 0), {FieldOfView = Settings.CameraBaseFov})
					NewTween:Play()
					NewTween2:Play()
					task.wait(0.11)
					Hum.WalkSpeed = BaseWalkSpeed
					AirSpeed = 0
					Chain = 0
					State = "N/A"
				end
				return
			end
		end
		
		coroutine.wrap(A)()
	end
end)



local function AutoBhop(IsJumping) -- Auto Bhop.
	if Settings.BhopEnabled == false then return end
	if Hum.FloorMaterial == Enum.Material.Air then return end
	if IsJumping then
		local Success = pcall(function()
			State = "Bhoping"
			if Hum.WalkSpeed < Settings.MaxSpeed then
				AirSpeed -= (15 / 100) * AirSpeed

				if Hum.WalkSpeed < Settings.BhopMaxSpeed + AirSpeed or Settings.CapNoStrafeBhop == false then
					NextSpeed = ((1.6 - (1.6 / (100 - Settings.BhopAcceleration))) * (Hum.WalkSpeed / 1.5)) - math.log(Hum.WalkSpeed, 4.5)
					if Is_Running == true then NextSpeed *= 1.1 end
					NextSpeed += Settings.BhopSpeedIncrement
					NextSpeed += AirSpeed
				elseif Hum.WalkSpeed > Settings.BhopMaxSpeed + AirSpeed and Settings.CapNoStrafeBhop == true then
					NextSpeed = Settings.BhopMaxSpeed + AirSpeed
				end

				if NextSpeed > Settings.BhopMaxSpeed + AirSpeed then NextSpeed = Settings.BhopMaxSpeed + AirSpeed end
				if NextSpeed > Settings.MaxSpeed then NextSpeed = Settings.MaxSpeed - (1 + AirSpeed + 1) end
				
				if RootPart.Velocity.Y < -15 then
					NextSpeed += (Settings.BhopHorizontalConvertPercentage / 100) * -RootPart.Velocity.Y -- Converter.
					task.wait()
					RootPart.Velocity.Y = 1
				end
				
				Chain += 1
				
				local BhopTween = TS:Create(Hum, TweenInfo.new(0.05, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = NextSpeed})
				BhopTween:Play()
			else
				Hum.WalkSpeed = Settings.MaxSpeed
			end
		end)
	end
end

local function ManualBhop(Input, Processed) -- Manual Bhop
	if Settings.BhopEnabled == false then return end
	if Hum.FloorMaterial == Enum.Material.Air then return end
	if Processed == true then return end
	if Input.UserInputType ~= Enum.UserInputType.Keyboard then return end
	if Input.KeyCode ~= Enum.KeyCode.Space then return end
	if Settings.BhopEnabled == false then return end
	
	local Success = pcall(function()
		State = "Bhoping"
		if Hum.WalkSpeed < Settings.MaxSpeed then
			if AirSpeed > 0 then AirSpeed -= (20 / 100) * AirSpeed end

			if Hum.WalkSpeed < Settings.BhopMaxSpeed + AirSpeed or Settings.CapNoStrafeBhop == false then
				NextSpeed = ((1.6 - (1.6 / (100 - Settings.BhopAcceleration))) * (Hum.WalkSpeed / 1.5)) - math.log(Hum.WalkSpeed, 4.5)
				if Is_Running == true then NextSpeed *= 1.1 end
				NextSpeed += Settings.BhopSpeedIncrement
				NextSpeed += AirSpeed
			elseif Hum.WalkSpeed > Settings.BhopMaxSpeed + AirSpeed and Settings.CapNoStrafeBhop == true then
				NextSpeed = Settings.BhopMaxSpeed + AirSpeed
			end

			if NextSpeed > Settings.BhopMaxSpeed + AirSpeed then NextSpeed = Settings.BhopMaxSpeed + AirSpeed end
			if NextSpeed > Settings.MaxSpeed then NextSpeed = Settings.MaxSpeed - (1 + AirSpeed + 1) end

			if RootPart.Velocity.Y < -15 then
				NextSpeed += (Settings.BhopHorizontalConvertPercentage / 100) * -RootPart.Velocity.Y -- Converter.
				task.wait()
				RootPart.Velocity.Y = 1
			end

			local BhopTween = TS:Create(Hum, TweenInfo.new(0.05, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = NextSpeed})
			BhopTween:Play()
		else
			Hum.WalkSpeed = Settings.MaxSpeed
		end
	end)
end

local function Strafe() -- Strafe.
	if Settings.StrafeEnabled == false then StrafeConnection:Disconnect() return end
	if Hum.FloorMaterial ~= Enum.Material.Air then return end
	
	--[[
	
		Strafe gives speed based on how much player moved his camera.
	
	]]
	
	local Direction = Flatten(Camera.CFrame.LookVector)
	local Angle = math.asin(LastDirection:Cross(Direction))
	
	if Angle ~= 0 and Library.NumbersMagnitude(Angle, 0) > 0.01 * Settings.StrafeSensitivity and Library.NumbersMagnitude(Angle, 0) < 0.055 * Settings.StrafeSensitivity then
		local NewAirSpeed = ((Library.TurnPositive(Angle * 4) + Settings.StrafeStrength) * Settings.StrafeAcceleration)
		NewAirSpeed /= math.log(Hum.WalkSpeed, 3)
		if Hum.WalkSpeed + NewAirSpeed > Settings.MaxSpeed then NewAirSpeed = 0 end
		AirSpeed += NewAirSpeed
		Hum.WalkSpeed += NewAirSpeed
	elseif Library.NumbersMagnitude(Angle, 0) > 0.055 * Settings.StrafeSensitivity then
		local NewAirSpeed = Library.TurnPositive(Angle * 18) + Settings.StrafeStrength
		AirSpeed -= NewAirSpeed
		Hum.WalkSpeed -= NewAirSpeed
	end
	LastDirection = Direction
end

local function Sway(DT)
	local MouseDelta = UIS:GetMouseDelta()

	Turn = Lerp(Turn, math.clamp(MouseDelta.X, -Settings.CameraSwayStrength, Settings.CameraSwayStrength), (Settings.CameraSwayStrength * DT))

	Camera.CFrame = Camera.CFrame * CFrame.Angles(0, 0, math.rad(Turn))
end

if Settings.StrafeEnabled == true then
	Hum:GetPropertyChangedSignal("MoveDirection"):Connect(function()
		if State ~= "Bhoping" and Hum.FloorMaterial ~= Enum.Material.Air then return end

		if math.round(Hum.MoveDirection.X) == -1 then
			local NewAirSpeed = ((Settings.AirAcceleration / 1.9) / math.log(Hum.WalkSpeed, 5))
			if Hum.WalkSpeed + NewAirSpeed > Settings.MaxSpeed then NewAirSpeed = Settings.MaxSpeed - Hum.WalkSpeed end
			AirSpeed += NewAirSpeed
			Hum.WalkSpeed += NewAirSpeed
		elseif math.round(Hum.MoveDirection.X) == 1 then
			local NewAirSpeed = ((Settings.AirAcceleration / 1.9) / math.log(Hum.WalkSpeed, 5))
			if Hum.WalkSpeed + NewAirSpeed > Settings.MaxSpeed then NewAirSpeed = Settings.MaxSpeed - Hum.WalkSpeed end
			AirSpeed += NewAirSpeed
			Hum.WalkSpeed += NewAirSpeed
		elseif Hum.MoveDirection == Vector3.new(0, 0, 0) and AirSpeed > 0 then
			local NewAirSpeed = (3.5 / 100) * AirSpeed
			AirSpeed -= NewAirSpeed
			Hum.WalkSpeed -= NewAirSpeed
		end
	end)
end

if Settings.AirEnabled == true then
	Hum:GetPropertyChangedSignal("FloorMaterial"):Connect(function() -- Air Acceleration.
		if Hum.FloorMaterial == Enum.Material.Air then
			local LastChain = Chain
			task.wait(0.5)
			if Hum.FloorMaterial ~= Enum.Material.Air then return end
			repeat
				local NewAirSpeed = (Settings.AirAcceleration / math.log(Hum.WalkSpeed, 8)) * 1.25
				if Hum.WalkSpeed + NewAirSpeed > Settings.MaxSpeed then NewAirSpeed = Settings.MaxSpeed - Hum.WalkSpeed end
				AirSpeed += NewAirSpeed
				Hum.WalkSpeed += NewAirSpeed

				RS.Heartbeat:Wait()
			until Hum.FloorMaterial ~= Enum.Material.Air
			task.wait(0.15)
			if Hum.FloorMaterial ~= Enum.Material.Air and Chain	<= LastChain then AirSpeed = 0 return end
			repeat wait(0.1) until State ~= "Bhoping"
			AirSpeed = 0
		end
	end)
end

Hum:GetPropertyChangedSignal("MoveDirection"):Connect(function() -- Walk Acceleration.
	if State == "Bhoping" or Hum.FloorMaterial == Enum.Material.Air then return end
	if Hum.MoveDirection == Vector3.new(0,0,0) then
		Hum.WalkSpeed = Settings.WalkStartSpeed
		if Tween then
			Tween:Cancel()
		end
		Tween = nil
	elseif not Tween then
		Tween = TS:Create(Hum, TweenInfo.new(Settings.WalkNtoMaxTime, Enum.EasingStyle.Sine, Enum.EasingDirection.In), {WalkSpeed = BaseWalkSpeed})
		Tween:Play()
	end
end)

if Settings.SpeedAffectsFov == true then
	local LastHS = 0
	Hum:GetPropertyChangedSignal("WalkSpeed"):Connect(function() -- Camera
		if Library.NumbersMagnitude(LastHS, Hum.WalkSpeed) > 4 then
			local NewTween = TS:Create(Camera, TweenInfo.new(0.03, Enum.EasingStyle.Sine, Enum.EasingDirection.Out, 0, false, 0), {FieldOfView = Settings.CameraBaseFov + Hum.WalkSpeed / (10 - Settings.CameraFovSensitivity)})
			NewTween:Play()
		end
		
		LastHS = Hum.WalkSpeed
	end)
end

if Settings.RunEnabled == true or Settings.SlideEnabled == true then
	UIS.InputBegan:Connect(function(Input, Processed) -- Run
		if Processed == true then return end
		if Input.UserInputType ~= Enum.UserInputType.Keyboard then return end

		if Input.KeyCode == Settings.RunKey and Settings.RunEnabled == true then
			Is_Running = not Is_Running
			task.wait()
			if Is_Running == false then
				BaseWalkSpeed = Settings.WalkSpeed
				task.wait()
				if State ~= "Bhoping" and Hum.FloorMaterial ~= Enum.Material.Air then -- Sees if player is bhoping
					local WalkTween = TS:Create(Hum, TweenInfo.new(1.2, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = Settings.WalkSpeed})
					WalkTween:Play()
				end
			else
				BaseWalkSpeed = Settings.RunSpeed
				task.wait()
				if State ~= "Bhoping" and Hum.FloorMaterial ~= Enum.Material.Air then
					local RunTween = TS:Create(Hum, TweenInfo.new(1.2, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out, 0, false, 0), {WalkSpeed = Settings.RunSpeed})
					RunTween:Play()
				end
			end

			wait(Settings.RunEnableOrDisableCooldown)
		elseif Input.KeyCode == Settings.SlideKey and Settings.SlideEnabled == true then
			print("Slide!") -- I did not make it yet.
		end
	end)
end


-------------------------- Connections --------------------------


if Settings.BhopEnabled == true then
	local BhopConnection = if Settings.AutoBhopEnabled == true then Hum.Jumping:Connect(AutoBhop) else UIS.InputBegan:Connect(ManualBhop)
end
	
if Settings.StrafeEnabled == true and Settings.CameraFirstPerson == true then
	StrafeConnection = RS:BindToRenderStep("UpdateCamera", Enum.RenderPriority.Camera.Value + 1, Strafe)
end

if Settings.CameraSwayEnabled == true then
	RS:BindToRenderStep("SwayCamera", Enum.RenderPriority.Camera.Value + 2, Sway)
end

If you somehow want to use this movement code, go ahead, i don’t mind.

As i’m dumb, i completely forgot about the library used.
Here is the NumbersMagnitude and TurnPositive code from it, just add it at the start of the code:

local function TurnPositive(Number)
	return math.sqrt(math.pow(Number, 2))
end

local function NumbersMagnitude(N1, N2)
	if N1 > N2 then
		return N1 - N2
	else
		return N2 - N1
	end
end
2 Likes

Hello Sukyta,

You have started heading in the right direction but your method of applying air acceleration is flawed since you can just shake your mouse and gain speed without actually performing a strafe mechanic. I have been working on my own Source Engine Movement in Roblox for about a year and I have a few tips I can share with you.

Olezen/UnitySourceMovement: Source engine-like movement in Unity, based on Fragsurf by cr4yz (Jake E.). (github.com) - This is going to be a godsend for you. It was the base script for me to understand how the strafing physics work, and I copied a lot of it over for my script. You will have to convert C# → LuaU in order to do this but it really isn’t that hard since the languages aren’t too different syntactically.

You will need to create your own collision as well, which for me is what took the most time.
(2) Collide And Slide - Actually Decent Character Collision From Scratch - YouTube This is the video you will need.

Apply both of these in concept and there you have a beautifully crafted movement script. Feel free to ask me any questions that you have.

3 Likes

Hello Beters! Thank you for the reply.

The way i implemented Air Acceleration was rather intentional. Because the code is inspired on how Nico’s Nextbots BHOP works and not on the BHOP from Quake Engine / CS:GO.

But i’m still going to check it out and test things to get an better final result.

3 Likes
  1. This appears to be a constant tween, so only create it once at the top of the script and re-use it:
Tween = TS:Create(Hum, TweenInfo.new(Settings.WalkNtoMaxTime, Enum.EasingStyle.Sine, Enum.EasingDirection.In), {WalkSpeed = BaseWalkSpeed})
  1. Vector3.zero is several times faster:
Vector3.new(0, 0, 0)

Vector3.zero
  1. task.wait runs at 60FPS, but wait runs at 30FPS
wait(...)

task.wait(...)
  1. Some of your code uses frequent signals like Hum:GetPropertyChangedSignal("MoveDirection") and Hum:GetPropertyChangedSignal("FloorMaterial"); consider adding debounces if applicable.

if math.round(Hum.MoveDirection.X) == -1 then
	-- logic...
elseif math.round(Hum.MoveDirection.X) == 1 then
	-- same logic...

if Hum.MoveDirection.X ~= 0 then
  1. You may also want to consider optimising the Strafe function since it runs every frame and does complex maths.
  2. Your custom functions can be optimised:
local function TurnPositive(Number)
	return math.sqrt(math.pow(Number, 2))
end

math.abs(Number)
local function NumbersMagnitude(N1, N2)
	if N1 > N2 then
		return N1 - N2
	else
		return N2 - N1
	end
end

math.abs(N1 - N2)
2 Likes