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 .