How to make a PROPER stamina/sprinting system

Welcome! This is a resource that I myself needed until I figured out how to make a sprinting system correctly. The main issue that I had was the stamina bar draining while the player was standing still yet still holding down “shift”. If you have had this issue before as well, this is the thing that you need!
Feel free to change the script to best suit your needs, and change the gui as well.

local player = game.Players.LocalPlayer
local character = player.Character

local UserInputService = game:GetService("UserInputService")

local baseSpeed = character.Humanoid.WalkSpeed

local stamina = 250
local staminaMaximum = 250
local running = false

stamina = math.clamp(stamina, 0, staminaMaximum)

UserInputService.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.LeftShift then
		running = true
		character.Humanoid.WalkSpeed = baseSpeed * 1.5 -- you can change this to your desired speed
		player.CameraMaxZoomDistance = 20
		player.CameraMinZoomDistance = 20
		
		while stamina > 0 and running and player.Character.Humanoid.Running do
			stamina = stamina - 1
			script.Parent:TweenSize(UDim2.new(1, 0, stamina/staminaMaximum, 0), "Out", "Linear",0)
			wait()
			if player.Character.Humanoid.MoveDirection ~= Vector3.new(0, 0, 0) then
				if stamina <= 0 then
					character.Humanoid.WalkSpeed = baseSpeed
				else
					continue
				end
			else
				repeat
					wait()
					stamina = stamina + 0.5
                    if stamina >= staminaMaximum then
						stamina = staminaMaximum
						script.Parent:TweenSize(UDim2.new(1, 0, stamina/staminaMaximum, 0), "Out", "Linear", 0)
						break
					end
					if running == false then
						break
					end
					script.Parent:TweenSize(UDim2.new(1, 0, stamina/staminaMaximum, 0), "Out", "Linear",0)
				until player.Character.Humanoid.MoveDirection ~= Vector3.new(0, 0, 0)
			end
			if stamina <= 0 then
				character.Humanoid.WalkSpeed = baseSpeed
			end
		end
	end
end)

UserInputService.InputEnded:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.LeftShift then
		running = false
		character.Humanoid.WalkSpeed = baseSpeed
		player.CameraMinZoomDistance = 15
		for i = 19, 15, -1 do
            player.CameraMaxZoomDistance = i
            wait()
        end
		
		while stamina < staminaMaximum and not running do
			stamina = stamina + 0.5
			script.Parent:TweenSize(UDim2.new(1, 0, stamina/staminaMaximum, 0), "Out", "Linear",0)
			wait()
			if stamina == 0 then
				character.Humanoid.WalkSpeed = baseSpeed
			end
			if stamina >= staminaMaximum then
				stamina = staminaMaximum
				script.Parent:TweenSize(UDim2.new(1, 0, stamina/staminaMaximum, 0), "Out", "Linear", 0)
				break
			end
		end
	end
end)

This script is probably a bit messy for some people. I will edit it if you give feedback on how I could make it better. If you need an explanation on what the heck is going on in this script, I will be happy to provide one if I am sitting at my computer. Also, there is no need to credit me because I got most of this script from someone else (I programmed it back when I knew next to nothing). Hope this helped!

Note: This script must be parented to the GUI object that is tweening. Tweens should be edited to fit your preference.

Edit: fixed a bug where the player could sprint forever regardless of whether or not the stamina had run out.

Edit2: fixed the spam scam bug (basically the player could spam the shift key and it would overfill their stamina) and removed my extra stuff. This should work for you now if you simply copy and paste it

Edit3: Cleanupcleanupeverywhere

8 Likes

It is messy but it’s also great!
Thank you for this resource

2 Likes

posting this comment because I want more people to be able to share the joy I experienced when this worked. Also the old version was bugged ¯_(ツ)_/¯

I read a bit of the script system and I found it quite interesting!

A few things that made me wonder is the usage of math.clamp to create the global variable (stamina)… I am not aware of this since I learned about the math library. And, another question is why did you change the CameraMaxZoomDistance multiple times in the second event (InputEnded):

Wouldn’t it be better if using a control flow statement? For instance:

UserInputService.InputEnded:Connect(function(input)
    if input.KeyCode == Enum.KeyCode.LeftShirt then
    running = false
    character.Humanoid.WalkSpeed = baseSpeed
    player.CameraMinZoomDistance = 15
    for i = 19, 15, -1 do
        player.CameraMaxZoomDistance = i
        wait()
    end
end

The screen zooms out and back in like other games to simulate you sprinting, and back when I first wrote this script I did not know about control flow statements. Thank you for the suggestion! It would clean the script up. The math.clamp value makes sure the number stays within the min value and the max value.