local Stamina = 100
local Sprinting = false
local function sprint(active)
if active then
Humanoid.WalkSpeed = Humanoid.WalkSpeed + 6
else
Humanoid.WalkSpeed = Humanoid.WalkSpeed - 6
end
Sprinting = active
end
UserInputService.InputBegan:Connect(function(input)
if input.KeyCode ~= Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.ButtonR2 then return end
sprint(true)
end)
UserInputService.InputEnded:Connect(function(input)
if input.KeyCode ~= Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.ButtonR2 then return end
sprint(false)
end)
RunService.RenderStepped:Connect(function()
if Sprinting then
if Stamina > 0 then
Stamina = Stamina - 1
wait(1)
else
sprint(false)
end
end
end)
Hereās what I have so far. The problems I am currently facing are
The stamina drains way too quickly. I have a wait(1), but that does literally nothing?? And once stamina == 0 and I set sprint to false, it continuously the lowers the players speed. Please note I cannot do
Humanoid.WalkSpeed = 22 -- or any specific number
I do ±6 as the speed has to be relative to the players speed, as there are different classes a player can have, each with their own speed
Maybe you need a stamina debounce so it doesnāt drain practically instantly? So something like:
if stamina > 0 and staminadebounce == false then
staminadebounce = true
stamina = stamina -1
wait(1)
staminadebounce = false
else
Sorry Iām on mobile so my formatting is bad
Edit: Also I think your issue with the walkspeed continuously decreasing is because nothing is preventing sprint(false) from continuously firing once stamina equals zero. So it continuously subtracts 6 walkspeed on the renderstepped. I think you need a debounce here as well.
Using RenderStepped runs a new instance of whatever code youāre running in there every frame, so the wait will not wait for it. Try reducing the value of stamina. Also I think Heartbeat works fine for this scenario as well.
local Stamina = 100
local Sprinting = false
local Exhausted = false
local speedDiff = 6
local function sprint(active) -- not the best example
if Exhausted then return end
if active then
Humanoid.WalkSpeed = Humanoid.WalkSpeed + speedDiff
else
Humanoid.WalkSpeed = Humanoid.WalkSpeed - speedDiff
end
if Stamina <= 0 then
Exhausted = true
end
Sprinting = active
end
UserInputService.InputBegan:Connect(function(input)
if input.KeyCode ~= Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.ButtonR2 then return end
sprint(true)
end)
UserInputService.InputEnded:Connect(function(input)
if input.KeyCode ~= Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.ButtonR2 then return end
sprint(false)
end)
RunService.RenderStepped:Connect(function()
if Sprinting and not Exhausted then
if Stamina > 0 then
Stamina = Stamina - 0.1
else
sprint(false)
end
end
end)
I have a couple of issues with your code, specifically this part:
RunService.RenderStepped:Connect(function()
if Sprinting then
if Stamina > 0 then
Stamina = Stamina - 1 wait(1)
else
sprint(false)
end
end
end)
To answer your immediate question, Iād like to point out that youāre creating a new thread every render frame. This means every render frame Stamina = Stamina - 1 is being executed. This, also, means that itās removing stamina every frame, then pausing that specific thread for 1 second. Iāll actually get to fixing your code after my next point:
Another problem arises when using RenderStepped: It can vary. It can vary a lot. One personās RenderStepped may fire, say, once every 1/25th of a second, while someone on their beast of a pc gets a solid 1/60th. This means that the lower the FPS, the less stamina they lose per second. Uh oh!
Oh man how do i fix this!?
Itās easy. RenderStepped passes an argument, dt, or deltaTime or āthe change in time.ā This value is equal to the amount of seconds that have passed since the previous render frame. Letās see this helpful little variable in action!
RunService.RenderStepped:Connect(function(dt)
if Sprinting then
if Stamina > 0 then
Stamina = Stamina - 1*dt
else
sprint(false)
end
end
end)
When we multiply our constant, (in this case 1), by our delta, (the change in time), we get the magic number to subtract! Meaning you will lose exactly 1 stamina per second, regardless of how often RenderStepped fires. Hallelujah!
Or just use Stepped so that you arenāt freezing frames to run code. RenderStepped realistically should only be used for render loops, like changing the CFrame of the camera.
Thereās no way to replenish it tho? Once it hits 0 I canāt get it to go back up
Heres my butchered attempt. Itās kinda close, only problem is once it hits 0, it does back up, but my players speed will keep being lowered until I canāt move
local Sprinting = false
local Exhausted = false
local SpeedDiff = 6
local function sprint(active)
if Exhausted then return end
if active then
Humanoid.WalkSpeed = Humanoid.WalkSpeed + SpeedDiff
else
Humanoid.WalkSpeed = Humanoid.WalkSpeed - SpeedDiff
end
Sprinting = active
end
UserInputService.InputBegan:Connect(function(input)
if input.KeyCode ~= Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.ButtonR2 then return end
sprint(true)
end)
UserInputService.InputEnded:Connect(function(input)
if input.KeyCode ~= Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.ButtonR2 then return end
sprint(false)
end)
RunService.Heartbeat:Connect(function()
if Sprinting then
if Stamina > 0 then
Stamina = Stamina - 0.5
else
sprint(false)
Exhausted = true
end
else
if Stamina < 100 then
Stamina = Stamina + 0.5
end
end
if Exhausted and Stamina > 0 then
Exhausted = false
end
local RoundedNumber = math.floor(Stamina + 0.5)
PlayerGui.HUD.Game.StaminaBar.Stamina.Text = RoundedNumber
PlayerGui.HUD.Game.StaminaBar.Bar:TweenSize(UDim2.new(RoundedNumber / 100, 0, 1, 0), 'Out', 'Linear', 0.05, true)
end)
Ah, I wouldāve included a regenerate stamina but you didnāt have it in your original post, thought it was a one time thing.
local Stamina = 100
local SprintHeld = false
local Sprinting = false
local Exhausted = false
local RunRefresh = 20 -- when to allow running after exhausted
local SpeedDiff = 6
local DrainRate = 20 -- drain per second
local function sprint(active)
if Exhausted then return end -- we can't run because we're exhausted!
if active then
Humanoid.WalkSpeed = Humanoid.WalkSpeed + SpeedDiff
else
Humanoid.WalkSpeed = Humanoid.WalkSpeed - SpeedDiff
end
Sprinting = active
end
UserInputService.InputBegan:Connect(function(input)
if input.KeyCode ~= Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.ButtonR2 then return end
SprintHeld = true
sprint(SprintHeld)
end)
UserInputService.InputEnded:Connect(function(input)
if input.KeyCode ~= Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.ButtonR2 then return end
SprintHeld = false
sprint(SprintHeld)
end)
RunService.Heartbeat:Connect(function(DeltaTime)
if Sprinting then
if Stamina > 0 then
Stamina = Stamina - DrainRate * DeltaTime
else
sprint(false)
Exhausted = true
end
elseif Stamina < 100 then
Stamina = Stamina + DrainRate * DeltaTime
if Stamina > RunRefresh then -- we can now run again!
Exhausted = false
if SprintHeld then -- resume running because player is still holding down the sprint key!
sprint(SprintHeld)
end
end
end
end)
^ Can I just note, is it better to update a UI element from this, or fire a BindableEvent inside the Heartbeat function to update the UI from StarterGui
Small bump, again, but for future references: you canāt nor are you supposed to. If the exploiter modifies and changes their own UI, thatās kinda⦠to their own detriment as long as they arenāt changing the speed value itself (which, if it were me Iād handle that on the server-side rather than client-side), I donāt see why youād worry about āoh no, an exploiter hacked their own UI and now it doesnāt work anymore!ā Reason Iām telling you to not worry nor care too much about it is because, speaking from experience, you canāt save everything from exploiters, and trying to do so is burning through energy and wasting time. So, while Iād make the speed change server-side, donāt think too much in depth about everything purely visually related .