Help with stamina jumping

I have an error: attempt to index nil with ‘FindFirstChild’, called on the first line. My overall goal is to make a system where when the player jumps, it takes 10 stamina. It will wait 3 seconds before it starts to refill. If they jump again, it stops refilling and takes 10 more stamina away. The stamina will continue to refill until either it is full or they jump.

Can you help with the error, and then possibly point out how I might go about this? I don’t know if my code logic is correct. All variables are defined.

My code:

player.Character:FindFirstChild('Humanoid').Jumping:Connect(function(starting)
    	if starting then
    		jumpedTime = tick()
    		stamina = stamina - 10
    	elseif starting == false then
    		repeat
    			if tick() - jumpedTime >= 3 then
    				stamina = stamina + 1
    			end
    		until
    			stamina == 100 or starting
    	end
end)
4 Likes

Hello,
Will this Help Humanoid | Documentation - Roblox Creator Hub

2 Likes

I would assume this would have to do with “Humanoid” not being loaded correctly. Can you try

player.Character:WaitForChild('Humanoid').Jumping:Connect(function(starting)
4 Likes

The character is not properly loaded when you are calling FindFirstChild on it. Wait until the character is not nil or use a CharacterAdded event.

3 Likes

This is how to do that:

local char = player.Character or player.CharacterAdded:Wait()

I waited for the character, that is no longer a problem.

When declaring the humanoid, use WaitForChild().

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()

local stamina = 100

local function refill()
	-- Ensuring that the while-loop would definitely cancel
	-- This must NEVER be set to false ever again after being set to true or it might all happen within the iteration and the while-loop won't stop
	local isJumping = false
	spawn(function()
		isJumping = char.Humanoid.Jumping:Wait()
	end) -- This has to be local or else it may be set to false again within the iteration
	wait(3) -- We can afford a wait because isJumping will never be set to false again after the player jumped for the first time since iterating. If it's set to true while waiting, then the while-loop won't run.
	while stamina < 100 and not isJumping do -- Add until stamina is full but if the player jumped since beginning iteration, it will cancel.
        stamina = stamina + 1
		if stamina == 10 then
			-- Undo the effect from being banned from jumping due to lack of stamina
			char.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true)
		end
        print('Refilling, stamina is', stamina)
		wait(.1)
	end
end

char.Humanoid.Jumping:Connect(function(starting)
    if starting then
		print("Jumped")
		stamina = stamina - 10
		if stamina < 10 then
			-- This will only take effect for the NEXT jump so you need to disable jumping a bit earlier
			char.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
			print("Next jump blocked! Stamina is", stamina)
		end
	else -- When the player exits jump
		print("Jump event exited with stamina", stamina)
		refill()
    end
end)

Alternate solutions include tweening the stamina value. This could clean up the code a bit since tweening, unlike while-loops, doesn’t block the thread so there’s no need to mess around with threading. They can also be canceled directly so you’d just need to cancel the tween when a jump is detected. Keep in mind, though, that you’d still need to find a way to re-enable jumping for the player once the stamina surpasses 10.

If you want to use a global isJumping variable for canceling refills, then you’d need to refill and set isJumping to false on the Landed event through Humanoid.StateChanged and then you can rely on the refill loop to cancel because isJumping won’t be set to true (jumps) then false (lands) within one iteration unless jumping and landing in your game takes less than 0.1 seconds. However, you wouldn’t be able to wait() for 3 seconds like I did because the player may jump within that time and you’d probably have to check tick() in the loop like you did in your original post.

Alternate solution with refilling on land instead of jump exit
--[[
	Alternate jump script using except refill begins on land
]]

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()

local stamina = 100

local function refill()
	-- Ensuring that the while-loop would definitely cancel
	-- This must NEVER be set to false ever again after being set to true or it might all happen within the iteration and the while-loop won't stop
	local isJumping = false
	spawn(function()
		isJumping = char.Humanoid.Jumping:Wait()
	end) -- This has to be local or else it may be set to false again within the iteration
	wait(3) -- We can afford a wait because isJumping will never be set to false again after the player jumped for the first time since iterating. If it's set to true while waiting, then the while-loop won't run.
	while stamina < 100 and not isJumping do -- Add until stamina is full but if the player jumped since beginning iteration, it will cancel.
        stamina = stamina + 1
		if stamina == 10 then
			-- Undo the effect from being banned from jumping due to lack of stamina
			char.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true)
		end
        print('Refilling, stamina is', stamina)
		wait(.1)
	end
end

char.Humanoid.StateChanged:Connect(function(oldState, newState)
	if newState == Enum.HumanoidStateType.Jumping then
		print("Jumped")
		stamina = stamina - 10
		if stamina < 10 then
			-- This will only take effect for the NEXT jump so you need to disable jumping a bit earlier
			char.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
			print("Next jump blocked! Stamina is", stamina)
		end
	elseif newState == Enum.HumanoidStateType.Landed then -- When the player lands
		print("Jump landed with stamina", stamina)
		refill()
    end
end)
Alternate solution with global isJumping and refill beginning on land
--[[
	Alternate jump script using global isJumping taking advantage of relibability with Landed
]]

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()

local stamina = 100
local isJumping = false -- Now it's global and is set to true/false depending on jumping/landing

local function refill()
	isJumping = false
	-- We CAN'T afford a wait because isJumping can be set to true then false, failing to cancel the loop during a 3 second wait
	local t = tick()
	while stamina < 100 and not isJumping do -- Add until stamina is full but if the player jumped since beginning iteration, it will cancel.
		if tick() - t >= 3 then
			stamina = stamina + 1
			if stamina == 10 then
				-- Undo the effect from being banned from jumping due to lack of stamina
				char.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true)
			end
	        print('Refilling, stamina is', stamina)
		end
		wait(.1)
	end
end

char.Humanoid.StateChanged:Connect(function(oldState, newState)
	if newState == Enum.HumanoidStateType.Jumping then
		print("Jumped")
		isJumping = true
		stamina = stamina - 10
		if stamina < 10 then
			-- This will only take effect for the NEXT jump so you need to disable jumping a bit earlier
			char.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
			print("Next jump blocked! Stamina is", stamina)
		end
	elseif newState == Enum.HumanoidStateType.Landed then -- When the player lands
		print("Jump landed with stamina", stamina)
		refill()
    end
end)

If you have any questions about how this works even after reading through my fairly extensive comments, just ask!

2 Likes