How to limit the players speed without using walkspeed?

I don’t want to do it by changing the walkspeed. I want to do it by not changing the walkspeed but I also don’t want the player to stop and then start because that would be very noticable.

Alright. I highly do not recommend it but there is a way to limit the velocity of a player is by using something called a VectorForce and some math. The reason being is because humanoid exerts a force on the HRP so you just have to negate that force.

You may have heard of a PID controller before. Basically it is a way to use math functions to achieve a goal e.g. rotation speed of a wheel. You can use it to make sure the character does not travel faster than a specified velocity

I do NOT recommend that as there are much better ways.

The second way you can do it is by limiting the MoveDirection to something less.
The third way you can do it by using a BodyVelocity to move the character instead.

Both the second and third option require you to create an entirely different system.

What exactly do you want to use it for and why do you not want to use WalkSpeed?

I disagree and this thread is getting silly.

I printed out the WalkSpeed and the velocity every physics frame while moving and changing WalkSpeed.

The first number (16, 8, 32) is the WalkSpeed. The second number (e.g. 13.86172…) is the HumanoidRootPart’s Velocity.Magnitude.

image

In other words, it maybe takes two frames to change velocity?

Here's the code if you want to recreate this
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local root = character:WaitForChild("HumanoidRootPart")
local default = humanoid.WalkSpeed

local speeds = {
	default / 2,
	default,
	default * 2
}

for i = 1, #speeds do
	local b = Instance.new("TextButton")
	b.Text = "Set speed to " .. speeds[i]
	b.Size = UDim2.fromOffset(200, 30)
	b.Position = UDim2.fromOffset(100, 100 + (i - 1) * 40)
	b.MouseButton1Click:Connect(function()
		
		humanoid.WalkSpeed = speeds[i]
		
		warn("==== CHANGED SPEED TO " .. speeds[i] .. " ====")
	end)
	b.Parent = script.Parent
end

game:GetService("RunService").Heartbeat:Connect(function()
	print(humanoid.WalkSpeed, root.Velocity.Magnitude)
end)

game:GetService("UserInputService").InputBegan:Connect(function(input, gp)
	if not gp and input.KeyCode == Enum.KeyCode.W then
		warn("==== HOLDING W ====")
	end
end)

game:GetService("UserInputService").InputEnded:Connect(function(input, gp)
	if not gp and input.KeyCode == Enum.KeyCode.W then
		warn("==== RELEASED W ====")
	end
end)

If you really, absolutely, positively, need your speed to change on that exact frame, you can do this:

if humanoid.FloorMaterial then
  local speed = root.Velocity.Magnitude
  if speed > 0 then
    local currentSpeedPercent = speed / humanoid.WalkSpeed
    root.Velocity = root.Velocity.Unit * currentSpeedPercent * NEW_SPEED
  end
end

humanoid.WalkSpeed = NEW_SPEED
Which I tested as well...
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local root = character:WaitForChild("HumanoidRootPart")
local default = humanoid.WalkSpeed

local speeds = {
	default / 2,
	default,
	default * 2
}

for i = 1, #speeds do
	local b = Instance.new("TextButton")
	b.Text = "Set speed to " .. speeds[i]
	b.Size = UDim2.fromOffset(200, 30)
	b.Position = UDim2.fromOffset(100, 100 + (i - 1) * 40)
	b.MouseButton1Click:Connect(function()
		
		if humanoid.FloorMaterial then
			local speed = root.Velocity.Magnitude
			if speed > 0 then
				local currentSpeedPercent = speed / humanoid.WalkSpeed
				root.Velocity = root.Velocity.Unit * currentSpeedPercent * speeds[i]  
			end
		end
		
		humanoid.WalkSpeed = speeds[i]
		
		warn("==== CHANGED SPEED TO " .. speeds[i] .. " ====")
	end)
	b.Parent = script.Parent
end

game:GetService("RunService").Heartbeat:Connect(function()
	print(humanoid.WalkSpeed, root.Velocity.Magnitude)
end)

game:GetService("UserInputService").InputBegan:Connect(function(input, gp)
	if not gp and input.KeyCode == Enum.KeyCode.W then
		warn("==== HOLDING W ====")
	end
end)

game:GetService("UserInputService").InputEnded:Connect(function(input, gp)
	if not gp and input.KeyCode == Enum.KeyCode.W then
		warn("==== RELEASED W ====")
	end
end)

…And the results:

image

There. Velocity changes matching WalkSpeed changes on the exact frame that you set them. If that doesn’t answer your question:

Then I don’t know what will.

Edit: By the way, you can’t really use BodyVelocity for this. Humanoid walking is not based on physics, exactly.

if I set NEW_SPEED to 0 then the player still moves that is the problem that I had earlier, but printing out root.Velocity gives 0, 0, 0 even though NEW_SPEED = 0 but the player is still moving. I don’t know why this is happening.

Apply Impulse is reliable but is not as smoothe as I would like and you cannot control it like you you can with WalkSpeed.

I don’t think it’s silly it’s just I don’t think I clarified what I want properly.

I want to be able to control the speed that the player moves without using walkspeed, I want to be able to set the max speed I want (studs per second) and I want it to be somewhat exact. its like walkspeed but only not walkspeed, using some other thing like a bodymover or velocity.

I ran a test and with NEW_SPEED = 0 the player is still going around 12stud/second?

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local root = character:WaitForChild("HumanoidRootPart")
local default = humanoid.WalkSpeed


local OldPos = root.Position
local Old = tick()
local NEW_SPEED = 0

while wait() do

	if humanoid.FloorMaterial then
		local speed = root.Velocity.Magnitude
			local currentSpeedPercent = speed / humanoid.WalkSpeed
			root.Velocity = root.Velocity.Unit * currentSpeedPercent * NEW_SPEED
	end
	if tick() - Old >= 1 then
		print((root.Position - OldPos).Magnitude)
		OldPos = root.Position
		Old = tick()
	end
end

image

That shows that I was actually going 12 - 12.5 studs per second while my velocity showed zero (root.Velocity). So I cannot control the studs per second I would like to go.

You forgot the

humanoid.WalkSpeed = NEW_SPEED

line in your test. That’s an important part :slight_smile:

I re-clarified what I want and that I don’t want to use WalkSpeed, "
How to limit the players speed without using walkspeed?"

I mean… You could do this, but it seems dumb. But since you insist:

Every frame, check how far the player went on the XZ axis. If they went further than the allowed number of studs, move them back along that vector to correct for the XZ movement.

Only do this in certain states (e.g. don’t affect ragdoll).

Proof of concept:

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local root = character:WaitForChild("HumanoidRootPart")
local humanoid = character:WaitForChild("Humanoid")

local maxSpeed = 8

local last = root.Position

-- set of states to limit speed during
local limitedSpeedState = {
	[Enum.HumanoidStateType.Climbing] = true,
	[Enum.HumanoidStateType.RunningNoPhysics] = true,
	[Enum.HumanoidStateType.Running] = true,
	[Enum.HumanoidStateType.Jumping] = true,
	[Enum.HumanoidStateType.Landed] = true,
	[Enum.HumanoidStateType.Freefall] = true,
	--[Enum.HumanoidStateType.Swimming] = true,
}

game:GetService("RunService").Stepped:Connect(function(t, dt)
	local now = root.Position
	local state = humanoid:GetState()
	if limitedSpeedState[state] then
		local vec = (now - last) * Vector3.new(1, 0, 1)
		local vel = vec.Magnitude / dt
		
		if vel > maxSpeed then
			local new = last + vec.Unit * maxSpeed * dt
			local pos = Vector3.new(new.X, root.Position.Y, new.Z)
			root.CFrame = root.CFrame + (pos - root.Position) 
			print(string.format("was %.1f\tnow %.1f", vel, (root.Position - last).Magnitude / dt))
		end
	end
	last = root.Position
end)
1 Like

I can’t use teleporting. I am doing on the client and I have download an anti cheat module that is on the server and the anti tp will pick this up.Why is this not possible with just using Velocity and applyimpulse or something?

It works well but is there any possibility instead of doing tp it uses velocity instead because this is exactly what I want just not using tp as tp can be unreliable when falling and etc.

Because Humanoids don’t really use physics for most of the time. And when they do, it’s weird half-physics that’s deep in the engine.

Not that I’m aware of. I gave it a shot, but its buggy, because Humanoids really hate physics:

My somewhat failed attempt
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local root = character:WaitForChild("HumanoidRootPart")
local humanoid = character:WaitForChild("Humanoid")

local maxSpeed = 1

-- set of states to limit speed during
local limitedSpeedState = {
	[Enum.HumanoidStateType.Climbing] = true,
	[Enum.HumanoidStateType.RunningNoPhysics] = true,
	[Enum.HumanoidStateType.Running] = true,
	[Enum.HumanoidStateType.Jumping] = true,
	[Enum.HumanoidStateType.Landed] = true,
	[Enum.HumanoidStateType.Freefall] = true,
	--[Enum.HumanoidStateType.Swimming] = true,
}

local last = root.Position
game:GetService("RunService").Stepped:Connect(function(t, dt)
	local now = root.Position
	
	if limitedSpeedState[humanoid:GetState()] then
		local vec = root.AssemblyLinearVelocity * Vector3.new(1, 0, 1)
		local vel = vec.Magnitude
		
		if vel > maxSpeed then
			local new = vec.Unit * maxSpeed
			local newVel = Vector3.new(new.X, root.AssemblyLinearVelocity.Y, new.Z)
			root.AssemblyLinearVelocity = newVel 
		end
	end
	
	last = now
end)

-- try to force it to keep in a physics mode? (this doesn't work really)
while true do
	local old, new = humanoid.StateChanged:Wait()
	if new == Enum.HumanoidStateType.RunningNoPhysics then
		 humanoid:ChangeState(Enum.HumanoidStateType.Running)
	end
end

That’s the limit of my knowledge. Good luck!

1 Like

I got it work but only in one direction , making the humanoid walk half as fast as they normally would

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local root = character:WaitForChild("HumanoidRootPart")
local default = humanoid.WalkSpeed


while wait() do
	character.PrimaryPart.Velocity = character.PrimaryPart.CFrame.LookVector * (character.PrimaryPart.Velocity.Unit * 8)
end

Do you know how I can replicate this in all directions? I can control the speed and such my normalizing the velocity put I do not know how to make it wokr in all directions? WOuld you be able to help me fix this issue ???

why are you even trying to do this? I’m not sure why you’re trying to avoid walkspeed in the first place.

I want to implement some code into my game to inhibit the wlakspeed updating real time and not rely on humanoid because the humanoid can be vunerable to exploitation so instead i want to use an alternative method to determine the speed of a player instead but I want to have control on the exact studs per second using another method

I have decided to use your method but could you please explain it. I don’t understand certain parts of your code and I would love to know how you arrived at this method/way.

Here’s the important bits. Let me know if there’s more detail you want.

-- Before physics updates on every frame
game:GetService("RunService").Stepped:Connect(function(t, dt)

  local now = root.Position
  local state = humanoid:GetState()

  if limitedSpeedState[state] then

    -- now - last is the offset from the last frame.
    -- Multiply by (1, 0, 1) so that we ignore the Y axis
    local vec = (now - last) * Vector3.new(1, 0, 1)

    -- Divide by the time the last frame took so we get the speed in studs/second
    local vel = vec.Magnitude / dt
    
    if vel > maxSpeed then

      -- We recalculate where the player should be, if they were only going maxSpeed
      -- instead of vel.
      -- To do this: start at `last`, move in the same direction that they did (`vec.Unit`),
      -- and only move `maxSpeed * dt`, which is how far they would've moved that frame
      local new = last + vec.Unit * maxSpeed * dt

      -- Use the X and Z from that calculated position, but the player's real
      -- Y position.
      local pos = Vector3.new(new.X, root.Position.Y, new.Z)

      -- Use the same rotation that the player had, but change the position
      root.CFrame = root.CFrame + (pos - root.Position) 

      -- Print it out.
      print(string.format("was %.1f\tnow %.1f", vel, (root.Position - last).Magnitude / dt))
    end
  end
  
  last = root.Position
end)

Is it possible to do this using setprimaryPartCFrame? I try to do it and it teleports me to negative -3376475954667…