How do I make my players stamina decrease

I have a script here, its JS. I want to decrease the Player’s Stamina by 1 every 2 seconds but it decreases the Stamina by 1 every frame, so the Stamina goes crazy.

var Speed : float = 6.0;
var Stamina : int = 10;
var Sprinting : boolean = false;
var rotateSpeed : float = 3.0;
	
function Update () 
{
	var controller : CharacterController = GetComponent(CharacterController);
	// Rotate around y - axis
	transform.Rotate(0, Input.GetAxis ("Horizontal") * rotateSpeed, 0);
	
	// Move forward / backward
	var forward : Vector3 = transform.TransformDirection(Vector3.forward);
	var curSpeed : float = Speed * Input.GetAxis ("Vertical");
	controller.SimpleMove(forward * curSpeed);
	
	if (Input.GetKey (KeyCode.RightShift) && curSpeed > 3.0)
	{
		Speed = 13;
		Sprinting = true;
	}
	else
	{
		Speed = 6;
		Sprinting = false;
	}
	
	if (Sprinting == true)
	{
		Sprinting1 ();
	}
}

function OnGUI ()
{
	GUI.Box (new Rect (10, 10, 100, 20), "Stamina: " + Stamina.ToString());
}

function Sprinting1 ()
{
	if (Sprinting == true)
	{
		Sprinting2 ();
	}
}

function Sprinting2 ()
{
	yield WaitForSeconds (2.0);
	Stamina -= 1;
	Sprinting1();
}

@script RequireComponent(CharacterController)

I used the simple move script, I add a Sprint function where when the player holds the Right Shift key the player runs faster. I also add Stamina to the player so he cant sprint forever.

When I tried to make the Stamina decrease by 1 every 2 seconds, its decreases but super fast, how can I fix it.

P.S IF THE QUESTION DOESNT MAKE SENSE IM SORRY I HAVE NO IDEA HOW TO EXPLAIN IT.

You have to enable sprinting when the Shift key is pressed down, and disable sprinting when the Shift key is let go. Right now you are executing Sprinting1() every frame as long as shift is being held down. You should use GetKeyDown and GetKeyUp instead, like this:

In Update():

if (Input.GetKeyDown(KeyCode.RightShift) && curSpeed > 3.0)
{
	Speed = 13;
	Sprinting = true;
	// We call the sprint function here instead
	Sprinting2();
}
else if (Input.GetKeyUp(KeyCode.RightShift))
{
	Speed = 6;
	Sprinting = false;
}

Get rid of Sprinting1() and change Sprinting2() into this:

function Sprinting2 ()
{
    yield WaitForSeconds (2.0);
    Stamina -= 1;
    
    if (Sprinting) {
    	Sprinting2();
    }
}

Now, Sprinting2() just checks if it’s still supposed to be subtracting stamina and re-runs itself if that is the case. Whenever shift is let go, it will stop subtracting stamina.

Your issue is lines 41 - 44. When ‘Sprinting’ is true, you call Sprinting2() every frame. This means you start a new coroutine every frame. If a user were to sprint for 2 seconds, and if your game was running at 60 fps, then you would stack up approximately 120 coroutines. As each coroutine comes due (i.e. 2.0 seconds has passed since its start), it will take off a point. So you will 120 coroutines reducing the stamina. And more will pile up as the sprinting continues.

As usual, I agree with @Fattie: Invoke() and InvokeRepeating() are cleaner, less error prone ways of doing something that repeats. In addition according to one analysis, they are slightly more efficient.

But I think your approach is flawed, plus I think there is a better way of accomplishing your task. Imagine the user starts to sprint then immediately stops, then 1.9 seconds later he starts again. Your code will subtract one even though the user was not sprinting the whole time. Figuring out how to track a continuous 2 seconds would take a different approach…setting a timestamp when the sprinting starts, decreasing stamina and resetting the timestamp if the current time is greater than 2 seconds in front of the timestamp.

But here is my real suggestion. Make ‘Stamina’ a float. Then each frame the user is sprinting:

Stamina -= Time.deltaTime * 0.5;

You can always round ‘Stamina’ to an int if you need to display the value to the user.
Note when you check to see if the user has stamina for sprinting, you need to check:

if (Stamina > 0.0)

…since there is no guarentee that you will hit exactly 0.0.