PcoWSkbVqDnWTu_dm2ix
The information on this page may no longer be accurate. To see the latest, go to our new and improved documentation. You can also learn more about the future of documentation.

Double Jumping

Double Jumping

10 min

Double jumping is a feature that appears in many games. This tutorial will cover how to implement it in Roblox.

Setting Up

To get started, you will need to insert a LocalScript into StarterPlayerScripts. You can name it whatever you want, but you should name it something descriptive so you can find it quickly if you need to. In this tutorial, it will be named DoubleJump.

In this script, you will need to get the player’s character and that character’s humanoid. The humanoid is the controller inside the character model that allows it to move and jump.

local localPlayer = game.Players.LocalPlayer
local character
local humanoid

local function characterAdded(newCharacter)
	character = newCharacter
	humanoid = newCharacter:WaitForChild("Humanoid")
end

if localPlayer.Character then
	characterAdded(localPlayer.Character)
end

localPlayer.CharacterAdded:connect(characterAdded)

The characterAdded function takes a character model as an argument and stores that character into the variable character. It also stores that character’s humanoid in the humanoid variable. You should use Instance/WaitForChild instead of simply accessing newCharacter.Humanoid, as when the character first loads it could take a bit of time for the humanoid to load.

The characterAdded function is bound to the player’s Player/CharacterAdded event. Binding to CharacterAdded makes sure that this function is called every time the player respawns. However, sometimes on game initialization this script will run after the first player is spawned. To cover this case, you also need to call characterAdded on the player’s character if it already exists.

Double Jumping

To make the player’s character double jump, it is important to know how the default jump works. The humanoid instance which controls movement and jumping of characters has a built-in state machine that says how it should behave. When a player jumps, the state of their humanoid is set to Jumping, which applies an upwards force to the character model. A brief moment later, the humanoid is set to the Freefall state, which lets physics take over and pull the character back to the ground with gravity. When the character hits the ground again the state of the humanoid is set briefly to landed.

Double_jump_01.png

A simple way to implement double jump is to force the character back into the Jumping state. We can also listen to when the state changes back to Landed to prevent the character from double jumping more than once.

Double_jump_02.png

You can use Humanoid/ChangeState to force the character back into the jumping state in order to cause the double jump.

To make the player’s character double jump you will also need to know when the player pressed the jump input. To do this you can use an event of UserInputService called UserInputService/JumpRequest. This event is fired whenever the player tries to jump.

Add the highlighted code to your script:

local UserInputService = game:GetService("UserInputService")
local localPlayer = game.Players.LocalPlayer
local character
local humanoid

local canDoubleJump = false
local hasDoubleJumped = false

function onJumpRequest()
	if not character or not humanoid or not character:IsDescendantOf(workspace) or
	 humanoid:GetState() == Enum.HumanoidStateType.Dead then
		return
	end
	
	if canDoubleJump and not hasDoubleJumped then
		hasDoubleJumped = true
		humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
	end
end

local function characterAdded(newCharacter)
	character = newCharacter
	humanoid = newCharacter:WaitForChild("Humanoid")
	
	humanoid.StateChanged:connect(function(old, new)
		if new == Enum.HumanoidStateType.Freefall then
			canDoubleJump = true
		end
	end)
end

if localPlayer.Character then
	characterAdded(localPlayer.Character)
end

localPlayer.CharacterAdded:connect(characterAdded)
UserInputService.JumpRequest:connect(onJumpRequest)

JumpRequest is bound to the function onJumpRequest. This function first checks if these conditions are met:

  • The character and humanoid variables are not nil
  • The character is currently somewhere in the workspace
  • The character is currently spawned

If any of these are not fulfilled, the code stops executing by calling return. If all of them are, it moves on to the next statement. This is to prevent double jumping when there is no character.

A player can double jump if the Humanoid is currently in freefall and has not already double jumped. The first of these conditions can be checked with canDoubleJump which is set to true when the humanoid is in the Freefall state. The next condition, making sure the player hasn’t already done a double jump, can be handled with a technique called Debounce. In this case, the variable hasDoubledJumped is initially false but gets set to true as soon as the humanoid performs the double jump.

If the conditions are right for a double jump, you can call ChangeState to force the jump.

Resetting

Although the player can now double jump, there is still some cleanup to do. In your testing, you probably noticed that you can only double jump once; after that, you cannot do it again. This is a simple fix: when the player has landed, reset canDoubleJump and hasDoubleJumped to false.

local UserInputService = game:GetService("UserInputService")
local localPlayer = game.Players.LocalPlayer
local character
local humanoid

local canDoubleJump = false
local hasDoubleJumped = false

function onJumpRequest()
	if not character or not humanoid or not character:IsDescendantOf(workspace) or
	 humanoid:GetState() == Enum.HumanoidStateType.Dead then
		return
	end
	
	if canDoubleJump and not hasDoubleJumped then
		hasDoubleJumped = true
		humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
	end
end

local function characterAdded(newCharacter)
	character = newCharacter
	humanoid = newCharacter:WaitForChild("Humanoid")
	hasDoubleJumped = false
	canDoubleJump = false
	
	humanoid.StateChanged:connect(function(old, new)
		if new == Enum.HumanoidStateType.Freefall then
			canDoubleJump = true
		elseif new == Enum.HumanoidStateType.Landed then
			canDoubleJump = false
			hasDoubleJumped = false
		end
	end)
end

if localPlayer.Character then
	characterAdded(localPlayer.Character)
end

localPlayer.CharacterAdded:connect(characterAdded)
UserInputService.JumpRequest:connect(onJumpRequest)

Tuning

If you test the code so far you may have noticed that if you hold the spacebar, the double jump looks more like a bigger jump than a second one. This is because Enum/HumanoidStateType/Freefall is set before the player starts to actually fall. To fix this, and make the double jump look more like a second jump, you should wait before setting canDoubleJump to true.

You can also make the second jump more pronounced by increasing the jump power for the second jump. You can set the force of the jump with Humanoid/JumpPower. Just be sure to reset JumpPower back to its initial value once the character has landed.

local UserInputService = game:GetService("UserInputService")
local localPlayer = game.Players.LocalPlayer
local character
local humanoid

local canDoubleJump = false
local hasDoubleJumped = false
local oldPower
local TIME_BETWEEN_JUMPS = 0.2
local DOUBLE_JUMP_POWER_MULTIPLIER = 2

function onJumpRequest()
	if not character or not humanoid or not character:IsDescendantOf(workspace) or
	 humanoid:GetState() == Enum.HumanoidStateType.Dead then
		return
	end
	
	if canDoubleJump and not hasDoubleJumped then
		hasDoubleJumped = true
		humanoid.JumpPower = oldPower * DOUBLE_JUMP_POWER_MULTIPLIER
		humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
	end
end

local function characterAdded(newCharacter)
	character = newCharacter
	humanoid = newCharacter:WaitForChild("Humanoid")
	hasDoubleJumped = false
	canDoubleJump = false
	oldPower = humanoid.JumpPower
	
	humanoid.StateChanged:connect(function(old, new)
		if new == Enum.HumanoidStateType.Landed then
			canDoubleJump = false
			hasDoubleJumped = false
			humanoid.JumpPower = oldPower
		elseif new == Enum.HumanoidStateType.Freefall then
			wait(TIME_BETWEEN_JUMPS)
			canDoubleJump = true
		end
	end)
end

if localPlayer.Character then
	characterAdded(localPlayer.Character)
end

localPlayer.CharacterAdded:connect(characterAdded)
UserInputService.JumpRequest:connect(onJumpRequest)

Anyone who joins your game can now double jump as they wish.

Conclusion

Double jumping is a useful mechanic to add depth to your game. There are some things that you might want to tweak, such as TIME_BETWEEN_JUMPS and DOUBLE_JUMP_POWER_MULTIPLIER, to change how long after the first jump a player must wait before double jumping or the power of the double jump respectively.

Tags:
  • animation
  • gameplay
  • coding