Could use a bit of help here, I’ve been at it for around 6 ish hours now.
Essentially, I’m making a sprinting system that uses stamina. After 2 seconds of not sprinting, your stamina regenerates.
Here’s the video showing what’s happening: untitled horror game framework testing - Roblox Studio 2023-03-04 13-58-02 . As you can see, its really unpredictable, random and really inconsistent.
Here’s my code (it looks long but its mostly because there are empty lines in between for readability):
-- services
local TS = game:GetService("TweenService")
local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")
local player = game:GetService("Players").LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")
-- UI
local plrgui = player:WaitForChild("PlayerGui")
local stamsys = plrgui:WaitForChild("StaminaSystem")
local cover = stamsys:WaitForChild("Cover")
-- camera
local cam = game:GetService("Workspace").CurrentCamera
-- stamina system + walkspeed settings
local stamina = 200
local maxstamina = 200
local stamcost = 3
local stamregen = 2
local sprinting = Instance.new("BoolValue")
sprinting.Parent = player
sprinting.Name = "sprinting"
local lastSprintTime = tick()
stamina = math.clamp(stamina, 0, maxstamina)
local walkspeed = 16
local sprintspeed = 24
hum.WalkSpeed = walkspeed
-- update stamina bar
local function updatestamina()
stamina = math.clamp(stamina, 0, maxstamina)
cover:TweenSize(
UDim2.new((stamina/maxstamina) * 0.258, 0, 0.048, 0),
Enum.EasingDirection.InOut,
Enum.EasingStyle.Linear,
0
)
end
-- deplete stamina when shift is held down
local function sprint()
sprinting.Value = true
hum.WalkSpeed = sprintspeed
lastSprintTime = tick()
while sprinting.Value and stamina > 0 do
wait()
if UIS:IsKeyDown(Enum.KeyCode.LeftShift) then
stamina = stamina - stamcost
updatestamina()
else
sprinting.Value = false
end
end
sprinting.Value = false
end
local function regenerate()
task.wait(2)
if not sprinting.Value and stamina < maxstamina and tick() - lastSprintTime > 2 then
for i = stamina, maxstamina, stamregen do
stamina += stamregen
updatestamina()
task.wait(0.1)
end
end
task.wait()
end
-- when shift is pressed
UIS.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.LeftShift then
if stamina > 0 then
sprint()
end
end
end)
-- when shift is released
UIS.InputEnded:Connect(function(input)
if input.KeyCode == Enum.KeyCode.LeftShift then
hum.WalkSpeed = walkspeed
end
end)
-- update stamina every frame
RS.RenderStepped:Connect(function()
if sprinting.Value then
if stamina == 0 then
sprinting.Value = false
hum.WalkSpeed = walkspeed
end
else
if stamina < maxstamina then
if stamina == 0 then
sprinting.Value = false
hum.WalkSpeed = walkspeed
end
stamregen = 3
print(stamregen)
regenerate()
else
print(stamregen)
stamregen = 0
end
end
end)
Any help would be appreciated, this is mostly a logic error (and I am no logician).
Unfortunately this didn’t work and doesn’t show the stamina value regenerating.
local function regenerate()
task.wait(2)
if not sprinting.Value and stamina < maxstamina and tick() - lastSprintTime > 2 then
local properties = {Value = maxstamina}
local regeninfo = TweenInfo.new(
4,
Enum.EasingStyle.Linear,
Enum.EasingDirection.InOut,
0,
false,
0
)
local regentween = TS:Create(staminainstance, regeninfo, properties)
updatestamina()
regentween:Play()
updatestamina()
end
task.wait()
end
Instead of using variables to track stamina. Create 2 new NumberValue called Stamina and MaxStamina. You can put these values somewhere inside the game.Players.LocalPlayer
Build a logic for reducing stamina or increasing stamina, but you will reduce the NumberValue that you just created above.
Use GetPropertyChangedSignal(Value) to track stamina. When the value is changed, update the bar.
local staminaNumberValue = player.Stamina
local maxStaminaNumberValue = player.MaxStamina
local BarUI = ???
staminaNumberValue.GetPropertyChangedSignal("Value"):Connect(function()
BarUI.Size = UDim2.fromScale((staminaNumberValue.Value * 100) / maxStaminaNumberValue.Value,1)
end)
Note: This implementation is no longer use TweenService, but the bar should scale linearly as you would expect.
Note 2: The bar should be scaled smoothly as long as you do not abruptly change the value of stamina will a large number.
if not sprinting.Value and stamina < maxstamina and tick() - lastSprintTime > 2 then
lastSprintTime = tick()
local properties = {Value = maxstamina}
local regeninfo = TweenInfo.new(
4,
Enum.EasingStyle.Linear,
Enum.EasingDirection.InOut,
0,
false,
0
)
local regentween = TS:Create(staminainstance, regeninfo, properties)
updatestamina()
regentween:Play()
updatestamina()
end
I don’t think that would really solve my issue. The only issue lies within the sprint() function and the regenerate() function.
The reason for the jitter and the inconsistency, as shown by the video, is because of the lines near the bottom which change the value of stamregen in order to enable or disable regeneration. Using :GetPropertyChangedSignal to repeatedly call updatestamina() won’t really fix this… Hence, using numbervalues won’t do anything either. It’s solely in that last chunk which breaks the whole system. Sorry for not making this clear.
As I said, I’d like it so that it’ll only start to regenerate after 2 seconds of not sprinting.
I tried your script and unfortunately it doesn’t tween the stamina value either…
In regards to your Note 2, as I said, the reason it doesn’t scale smoothly is exactly as you say, because the value of stamregen (which controls the change in Stamina) is changed in the last segment. That’s particularly what needs fixing.
I removed the task.wait(2) and it regenerates instantly, and doesn’t wait 2 seconds of non-sprinting. This probably means that the lastSprintTime isn’t being properly used so I’ll try fix that.
update: tried messing around with it, and what ends up happening is that once the bar reaches full, it glitches out and when you try and sprint, the bar doesn’t go down at all and starts to jitter. It’s most likely because its trying to regenerate and sprint at the same time.
Ok, so. Personally, I think your method should be reworked. So here is my idea.
How about we try regenerate() inside InputEnded()? Instead of using RenderStepped, we regenerate stamina right after the player stop running (stop pressing Shift key.) We create a new variable to keep tracking if the player is already regenerating their stamina. There should be only 1 thread running for this. task.spawn() can also be implemented.
local regenerationThreadCount = 0
local function regeneration()
regenerationThreadCount += 1 -- Increasing thread, cancel previous regeneration function.
-- This line will break the latest regeneration loop if there is any.
task.wait(2) -- Hold for 2 sec before regen again.
if regenerationThreadCount == 2 then
regenerationThreadCount = 1 -- Resetting thread count. Keep it at 1 so the loop below can run.
end
-- The top line will make the thread count == 2, to see the picture clearly, I use extra if statement.
-- Start new loop increasing stamina
while regenerationThreadCount ~= 2 and stamina < maxStamina and not sprinting.Value do
-- Regen
end
end
Note: The code above is solely my idea, never tested, and never tried it.
Also, now you have to track players’ stamina when they are holding Shift key, but their stamina is already zero.
Thank you so much, this works really well and really efficiently. I never though about considering the number of times regenerate was called, this was a clever approach! Thanks again!
There is a slight problem but I think it could be easily resolved using the RegenerationThreadCount.
If you try sprinting while it’s regenerating, it starts to jitter again. Is there a way to override regeneration if you are sprinting?