Jump Fatigue Implementation

Hello,

I found a script that adds a Jump Cooldown, and I was trying to modify it so that instead of Jump Cooldown, it introduces Jump Fatigue. Jump Fatigue is a little different because it gradually limits your Jump Height, and after 1 second from the first jump, it resets, as compared to Jump Cooldown where you jump and then have to wait another second to jump again.

I’m having trouble making these changes, If anyone has any ideas or a post already made explaining how to make a jump fatigue script, let me know!

Jump Cooldown Script:

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")


local Player = Players.LocalPlayer
local Character = script.Parent
local Humanoid = script.Parent:WaitForChild("Humanoid")

local Landed = 0
local JumpState = true
local Jumping = false

local function SetJumpState(state)
	-- Avoid calling Humanoid:SetStateEnabled more than once

	if JumpState ~= state then
		JumpState = state
		Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, state)
	end
end

local function OnCharacter(character)
	Character = character
	Humanoid = character:FindFirstChildOfClass("Humanoid")

	Humanoid:GetPropertyChangedSignal("FloorMaterial"):Connect(function()
		if Humanoid.FloorMaterial ~= Enum.Material.Air and Jumping and JumpState then
			Landed = tick()
			Jumping = false

			SetJumpState(false)
		end
	end)
end

if Player.Character then
	OnCharacter(Player.Character)
end

Player.CharacterAdded:Connect(function(character)
	Landed = 0
	JumpState = true
	Jumping = false

	OnCharacter(character)
end)

RunService.Heartbeat:Connect(function()
	if not JumpState and tick() - Landed > 0.5 then
		SetJumpState(true)
	end
end)

UserInputService.JumpRequest:Connect(function()
	if Humanoid.FloorMaterial ~= Enum.Material.Air then
		Jumping = true
	end
end)
3 Likes

This would be possible by reducing Humanoid.JumpPower for consecutive jumps (or Humanoid.JumpHeight if Humanoid.UseJumpPower is set to false).

Just to clarify before I try to create something that answers your question: Is Humanoid.UseJumpPower enabled for the Characters in your game, or do you want to use the JumpHeight property to create a “Jump Fatigue” feature, instead?


Edit: In addition to that, when you mentioned this:

Would you want it to reset 1 second after the Character lands on the ground as a result of jumping? And if a player tries to jump again within that timeframe (before it resets), their jump power / height would continue decreasing and the 1 second timer would restart after they land again? Just want to make sure I understand the specific implementation that you are trying to achieve

2 Likes

In theory, this script should work:

-- Jump Fatigue Script (Client - StarterCharacterScripts)

-- Config
local TIME_TO_RESET = 5 -- In seconds

local DECREASE_POWER = 10
local DECREASE_HEIGHT = 1

-- Variables
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local Player = Players.LocalPlayer
local Character = script.Parent
local Humanoid: Humanoid = script.Parent:WaitForChild("Humanoid")

local DefaultPower = Humanoid.JumpPower
local DefaultHeight = Humanoid.JumpHeight

local Landed = 0
local Jumping = false

-- Functions
local function SetJumpAmount(reset: boolean)
	if Humanoid.UseJumpPower then
		if reset then
			Humanoid.JumpPower = DefaultPower
		else
			Humanoid.JumpPower -= DECREASE_POWER
		end
	else
		if reset then
			Humanoid.JumpHeight = DefaultHeight
		else
			Humanoid.JumpHeight -= DECREASE_HEIGHT
		end
	end
end

local function OnCharacter(character)
	Character = character
	Humanoid = character:FindFirstChildOfClass("Humanoid")

	Humanoid:GetPropertyChangedSignal("FloorMaterial"):Connect(function()
		if Humanoid.FloorMaterial ~= Enum.Material.Air and Jumping then
			Landed = tick()
			Jumping = false

			SetJumpAmount(false)
		end
	end)
end

-- Initialize
if Player.Character then
	OnCharacter(Player.Character)
end

Player.CharacterAdded:Connect(function(character)
	Landed = 0
	Jumping = false

	OnCharacter(character)
end)

RunService.Heartbeat:Connect(function()
	if tick() - Landed > TIME_TO_RESET then
		SetJumpAmount(true)
	end
end)

UserInputService.JumpRequest:Connect(function()
	if Humanoid.FloorMaterial ~= Enum.Material.Air then
		Jumping = true
	end
end)

Let me know if you have any questions.

1 Like

i have jump fatigue implemented in a custom movement system that I wrote a few weeks ago–

the code in the video uses very different code than shown below, but this is how i generally implemented jump fatigue:

---game/StarterPlayer/StarterCharacterScripts/Fatigue.client.lua

local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

local Humanoid: Humanoid = Character:WaitForChild("Humanoid")

local JumpPower = 50
local JumpFatigueScalar = 0.5
local JumpFatigueCooldown = 2
local LastNonFatigueJump = 0

local UseFatigueGradient = false

local function Lerp(v0: number, v1: number, a: number): number
	return (1 - a) * v0 + a * v1
end

local function IsJumpFatigued(): (boolean, number)
	local delta = os.clock() - LastNonFatigueJump
	return delta < JumpFatigueCooldown, delta / JumpFatigueCooldown
end

local function StateChanged(_, New: Enum.HumanoidStateType)
	if New ~= Enum.HumanoidStateType.Jumping then return end
	
	local IsFatigued, Gradient = IsJumpFatigued()
	if IsFatigued then
		if UseFatigueGradient then
			Humanoid.JumpPower = Lerp(JumpPower * JumpFatigueScalar, JumpPower, Gradient)
		else
			Humanoid.JumpPower = JumpPower * JumpFatigueScalar
		end
	else
		Humanoid.JumpPower = JumpPower
		LastNonFatigueJump = os.clock()
	end
end

Humanoid.StateChanged:Connect(StateChanged)

there’s also a setting for the jump fatigue to gradually increase as the cooldown approaches 0, which i personally don’t really like but it’s possible anyway so here’s a pretty picture i drew that shows how either settings will affect jumppower

2 Likes

This is exactly what I need! Very organized script btw and stupid proof too, I like it! (if Humanoid.UseJumpPower then) Anyways I was trying to make a jump fatigue system similar to the game Fortnite and just found out that they have it a little different to the one I asked for:

Jump fatigue begins after jumping twice and starts at the beginning of your second jump.
Jump fatigue resets if you wait about 1 second after the start of your last jump not the beginning jump.

Do you mind adjusting accordingly? I can pay 100 robux for your time.

1 Like

Thanks for contributing and sharing your code! personally I prefer this: (its kinda like the “usegradient” but oppilate and its what Fortnite uses!)

ohhh that makes sense
i wrote mine around what ive sort of observed in games like apex which i guess is like the opposite of what you want LOL

changing the return value of the IsJumpFatigued() function should make jumps behave like how you want:

return delta < JumpFatigueCooldown, 1 - (delta / JumpFatigueCooldown)

yeah LOL exactly
yea but idk fortnite just has a very clean jump fatigue mechanic.

This doesn’t seem to work but I’ll give the same deal to Implement Fortnite jump fatigue into roblox for 100 robux:

How Fortnite Jump Fatigue Works?

Yes, I can adjust it for you. No need to pay anything lol. (Sorry for the delay)

1 Like

Alright. It’s not perfect, but I have a system that works pretty well.

Details:
It’s a lot more “full” than the last script (lol), but it works. There are several new configuration options that allow you to tweak everything to your liking.

Configuration:

JUMPS_TO_START -- Amount of jumps to trigger fatigue
TIME_TO_START -- Amount of time after the last jump number to start degrading
TIME_TO_RESET -- Amount of time after the last jump to activate recovery
GRADUAL_RESET -- Gradually reset the jump power/height (Instantly resets if false)
INCREAST_PER_SEC -- Amount of times per second the jump power/height increases
INCREASE_POWER -- Amount of power that is increased during gradual recovery
INCREASE_HEIGHT -- Amount of height that is increased during gradual recovery
DECREASE_POWER -- Amount of power that is decreased after each jump
DECREASE_HEIGHT -- Amount of power that is decreased after each jump
MIN_POWER -- The minimum jump power that can be set
MIN_HEIGHT -- The minimum jump height that can be set
USE_MIN_JUMP -- If true, uses MIN_POWER and MIN_HEIGHT

I have pre-configured everything to match what you have described in the replies above, however, feel free to adjust anything you want to test things out.

Script:

-- Jump Fatigue Script (Client - StarterCharacterScripts)

-- Constants
local JUMPS_TO_START = 2 -- Amount of jumps to trigger fatigue (Starts on the last jump number)
local TIME_TO_START = 1 -- Amount of time after the last jump number to start degrading

local TIME_TO_RESET = 1 -- Amount of time after the last jump to activate recovery

local GRADUAL_RESET = false -- Gradually reset the jump power/height (Instantly resets if false)
local INCREASE_PER_SEC = 2 -- Amount of times per second the jump power/height will increase (Ignore if above is false)
local INCREASE_POWER = 10 -- Amount of power that is increased during gradual recovery (JumpPower)
local INCREASE_HEIGHT = 1 -- Amount of height that is increased during gradual recovery (JumpHeight)

local DECREASE_POWER = 10 -- Amount of power that is decreased after each jump (JumpPower)
local DECREASE_HEIGHT = 1 -- Amount of height that is decreased after each jump (JumpHeight)

local MIN_POWER = 20 -- The minimum jump power that can be set
local MIN_HEIGHT = 2 -- The minimum jump height that can be set
local USE_MIN_JUMP = true -- If true, will use the MIN_POWER and MIN_HEIGHT values

-- Variables
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local Player = Players.LocalPlayer
local Character = script.Parent
local Humanoid: Humanoid = script.Parent:WaitForChild("Humanoid")

local DefaultPower = Humanoid.JumpPower
local DefaultHeight = Humanoid.JumpHeight

local Landed = 0
local TimesJumped = 0
local Jumping = false
local Degrading = false
local Increasing = false

-- Functions
local function ChangeJumpPower(amount: number)
	if USE_MIN_JUMP and (amount) < MIN_POWER then return end
	if amount > DefaultPower then Humanoid.JumpPower = DefaultPower return end
	
	Humanoid.JumpPower = amount
end

local function ChangeJumpHeight(amount: number)
	if USE_MIN_JUMP and (amount) < MIN_HEIGHT then return end
	if amount > DefaultHeight then Humanoid.JumpHeight = DefaultHeight return end
	
	Humanoid.JumpHeight = amount
end

local function SetJumpFatigue(reset: boolean)	
	if Humanoid.UseJumpPower then
		if reset and not Increasing then
			Increasing = true
			Degrading = false
			
			if GRADUAL_RESET then
				repeat
					ChangeJumpPower(Humanoid.JumpPower + INCREASE_POWER)
					task.wait(1/INCREASE_PER_SEC)
				until
					Humanoid.JumpPower >= DefaultPower
			else
				ChangeJumpPower(DefaultPower)
			end
			Increasing = false
		elseif not Increasing then
			ChangeJumpPower(Humanoid.JumpPower - DECREASE_POWER)
		end
	else
		if reset and not Increasing then
			Increasing = true
			Degrading = false
			
			if GRADUAL_RESET then
				repeat
					ChangeJumpHeight(Humanoid.JumpHeight + INCREASE_HEIGHT)
					task.wait(1/INCREASE_PER_SEC)
				until
					Humanoid.JumpHeight >= DefaultHeight
			else
				ChangeJumpHeight(DefaultHeight)
			end
			Increasing = false
		elseif not Increasing then
			ChangeJumpHeight(Humanoid.JumpHeight - DECREASE_HEIGHT)
		end
	end
end

local function OnCharacter(character)
	Character = character
	Humanoid = character:FindFirstChildOfClass("Humanoid")

	Humanoid:GetPropertyChangedSignal("FloorMaterial"):Connect(function()
		if Humanoid.FloorMaterial ~= Enum.Material.Air and Jumping then
			Landed = tick()
			Jumping = false
			
			TimesJumped += 1
			
			if not Degrading and TimesJumped >= JUMPS_TO_START then
				repeat task.wait() until tick() - Landed >= TIME_TO_START
				SetJumpFatigue(false)
			else
				SetJumpFatigue(false)
			end
			
			Degrading = true
		end
	end)
end

-- Initialize
if Player.Character then
	OnCharacter(Player.Character)
end

Player.CharacterAdded:Connect(function(character)
	Landed = 0
	Jumping = false

	OnCharacter(character)
end)

RunService.Heartbeat:Connect(function()
	if tick() - Landed > TIME_TO_RESET then
		TimesJumped = 0
		Degrading = false
		SetJumpFatigue(true)
	end
end)

UserInputService.JumpRequest:Connect(function()
	if Humanoid.FloorMaterial ~= Enum.Material.Air then
		Jumping = true
	end
end)

Let me know if you have any further questions or requests!

2 Likes

Thanks a lot bro! The configuration will make it really easy to implement it! Let me try it out and let you know

1 Like

Few things I noticed right off the bat, Jumps to Start and Time to Start doesn’t seem to be working. When I spam jump it always starts the jump fatigue right away and takes the time from the first jump to reset so maybe just Jumps to Start doesn’t work. Once again I really appreciate your help on this :slight_smile:

1 Like

it works for me on an empty baseplate, maybe u forgot to flip the boolean value of UseFatigueGradient?

also this is a help forum and offering/receiving robux outside of explicit commissions isnt what a help forum is about :^)!

---game/StarterPlayer/StarterCharacterScripts/Fatigue.client.lua

local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

local Humanoid: Humanoid = Character:WaitForChild("Humanoid")

local JumpPower = 50
local JumpFatigueScalar = 0.5
local JumpFatigueCooldown = 2
local LastNonFatigueJump = 0

local UseFatigueGradient = true

local function Lerp(v0: number, v1: number, a: number): number
	return (1 - a) * v0 + a * v1
end

local function IsJumpFatigued(): (boolean, number)
	local delta = os.clock() - LastNonFatigueJump
	return delta < JumpFatigueCooldown, 1 - (delta / JumpFatigueCooldown)
end

local function StateChanged(_, New: Enum.HumanoidStateType)
	if New ~= Enum.HumanoidStateType.Jumping then return end

	local IsFatigued, Gradient = IsJumpFatigued()
	if IsFatigued then
		if UseFatigueGradient then
			Humanoid.JumpPower = Lerp(JumpPower * JumpFatigueScalar, JumpPower, Gradient)
		else
			Humanoid.JumpPower = JumpPower * JumpFatigueScalar
		end
	else
		Humanoid.JumpPower = JumpPower
		LastNonFatigueJump = os.clock()
	end
	
	print(Humanoid.JumpPower)
end

Humanoid.StateChanged:Connect(StateChanged)
2 Likes

Sorry for the late reply

Hmm weird, is your jump height/jump power taking 2 jumps to start?

Yeahhh I know, I just feel grateful for people like you who help and want to show a bit of appreciation :slight_smile:

1 Like

i didn’t know u wanted it to take 2 jumps to start :​p

i rewrote some of the variables to be part of a settings table so u can import custom settings in the future, and also made it so u can define how many jumps it takes for the actual fatigue to be applied, as well as a reset timer so that spaced-out jump inputs can have the internal fatigue counter reset

---game/StarterPlayer/StarterCharacterScripts/Fatigue.client.lua

local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

local Humanoid: Humanoid = Character:WaitForChild("Humanoid")

local Settings = {
	JumpPower = 50;
	JumpFatigueScalar = 0.5;
	JumpFatigueCooldown = 2;
	--# of jumps executed until fatigue is applied
	JumpLenience = 2;
	--# of seconds until JumpLenience is reset
	JumpLenienceReset = 1;
	
	--jump fatigue follows a linear gradient (true) or a constant gradient (false)
	UseFatigueGradient = true;
}

local FatigueStart = 0
local LastNonFatigueJump = 0
local CurrentLenience = Settings.JumpLenience

local function Lerp(v0: number, v1: number, a: number): number
	return (1 - a) * v0 + a * v1
end

local function OnNonFatigueJump()
	local Now = os.clock()
	
	if Now - LastNonFatigueJump > Settings.JumpLenienceReset then
		CurrentLenience = Settings.JumpLenience
	end

	LastNonFatigueJump = Now
	CurrentLenience -= 1

	if CurrentLenience == 0 then
		FatigueStart = Now
	end
end

local function IsJumpFatigued(): (boolean, number)
	local delta = os.clock() - FatigueStart
	return delta < Settings.JumpFatigueCooldown, 1 - (delta / Settings.JumpFatigueCooldown)
end

local function StateChanged(_, New: Enum.HumanoidStateType)
	if New ~= Enum.HumanoidStateType.Jumping then return end

	local IsFatigued, Gradient = IsJumpFatigued()
	if IsFatigued then
		if Settings.UseFatigueGradient then
			Humanoid.JumpPower = Lerp(Settings.JumpPower * Settings.JumpFatigueScalar, Settings.JumpPower, Gradient)
		else
			Humanoid.JumpPower = Settings.JumpPower * Settings.JumpFatigueScalar
		end
		CurrentLenience = Settings.JumpLenience
	else
		Humanoid.JumpPower = Settings.JumpPower
		OnNonFatigueJump()
	end
	
	print(Humanoid.JumpPower)
end

Humanoid.StateChanged:Connect(StateChanged)

2 Likes

Wow this is exactly what I needed! Sorry for the late reply btw was away from my computer for a while. Is there a setting to change if the cooldown starts at the last jump fatigue to reset jump fatigue? (Like a player has to stop jumping for 1 second to reset jump fatigue)

1 Like

necrobump in response to a dm

---game/StarterPlayer/StarterCharacterScripts/Fatigue.client.lua

local TS = game:GetService("TweenService")

local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

local Humanoid: Humanoid? = Character:FindFirstChildOfClass("Humanoid") or Character:WaitForChild("Humanoid")

local Settings = {
	JumpPower = 50;
	JumpFatigueScalar = 0.5;
	JumpFatigueTime = 1;
	--# of jumps executed until fatigue is applied
	JumpLenience = 2;
	--# of seconds until JumpLenience is reset
	JumpLenienceReset = 1;

	--apply custom curve to fatigue gradient; set easingstyle enum to "Linear" for a normal gradient
	FatigueCurve = {Enum.EasingStyle.Quad, Enum.EasingDirection.In}
}

local FatigueStart = 0
local LastNonFatigueJump = 0
local CurrentLenience = Settings.JumpLenience

local function Lerp(v0: number, v1: number, a: number): number
	return (1 - a) * v0 + a * v1
end

local function OnNonFatigueJump(): ()
	local Now = os.clock()

	if Now - LastNonFatigueJump > Settings.JumpLenienceReset then
		CurrentLenience = Settings.JumpLenience
	end

	LastNonFatigueJump = Now
	CurrentLenience -= 1

	if CurrentLenience == 0 then
		FatigueStart = Now
	end
end

local function OnFatigueJump(): ()
	local Now = os.clock()
	FatigueStart = Now
end

local function IsJumpFatigued(): (boolean, number)
	local delta = os.clock() - FatigueStart
	return delta < Settings.JumpFatigueTime, 1 - (delta / Settings.JumpFatigueTime)
end

local function StateChanged(_, New: Enum.HumanoidStateType)
	if New ~= Enum.HumanoidStateType.Jumping then return end

	local IsFatigued, Gradient = IsJumpFatigued()
	if IsFatigued then
		Humanoid.JumpPower = Lerp(Settings.JumpPower * Settings.JumpFatigueScalar, Settings.JumpPower, TS:GetValue(Gradient, unpack(Settings.FatigueCurve)))
		CurrentLenience = Settings.JumpLenience
		OnFatigueJump()
	else
		Humanoid.JumpPower = Settings.JumpPower
		OnNonFatigueJump()
	end
end

Humanoid.StateChanged:Connect(StateChanged)
2 Likes