Detect if player is in air

I want to detect if the player is in the air as the player’s Humanoid State Type is “Physics,” the current jump system sets the player’s state type to jump then waits 0.1 seconds and switches it back to physics, there’s one problem though. The player can jump mid air.

I’ve tried to make it detect if the player is in the air but FloorMaterial doesn’t work, I don’t know anything about raycasting, is there an easier method? Or something that has raycasting that is simple to implement?

Current Code:

--// Variables

local Player = game.Players.LocalPlayer
local Character = script.Parent
local Humanoid = Character:WaitForChild("Humanoid")
local Root = Character:WaitForChild("HumanoidRootPart")
local uis = game:GetService("UserInputService")
local physics = false
local retracted = false

local jumpAnim = Humanoid:LoadAnimation(script.JumpAnim)
local retractAnim = Humanoid:LoadAnimation(script.RetractAnim)

Humanoid.HipHeight = -0.5

--// Event

uis.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end

	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		if retracted == false then
			if physics == false then
				physics = true
				jumpAnim:Play()
				Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
			else
				physics = false
				jumpAnim:Stop()
				Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
				task.wait(0.1)
				Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
				wait(0.5)
				Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
				script.ParticlesEvent:FireServer(Character, physics)
			end
		end
	end
	
	if input.KeyCode == Enum.KeyCode.Q then
		if physics == false then
			if retracted == false then
				retracted = true
				retractAnim:Play(0.2)
				Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
			else
				retracted = false
				retractAnim:Stop(0.2)
				Humanoid:ChangeState(Enum.HumanoidStateType.GettingUp) 
			end
		end
	end
	
end)

What happens:

2 Likes

Also it’s not that the player jumps twice in the script, that’s normal but when you spam click you can jump an infinite amount of times.

Humanoid.FloorMaterial == enum.Material.Air
or
Humanoid:GetState() == enum.humanoidstatetype.jumping/freefall

I think that raycasting is probably the easiest way to check if the player is on ground

Basically to do it with raycasting, we need to cast a ray using the Workspace:Raycast() function,
the function has 3 parameters:

  1. origin: Vector3 - Where the ray starts from
  2. direction: Vector3 - In which direction the ray goes from the starting point (The Magnitude of the Vector3 defines the length of the ray)
  3. raycastParams: RaycastParams - Defines which objects the ray will collide and not collide with

We need to make the ray start from the HumanoidRootPart.Position and make it go down some amount of studs

and if the ray hits something it will return a RaycastResult object, otherwise, it will return nil

We can use this to check if the player is on ground, if the ray returns a RaycastResult object,
it means that the ray hit something and that the player is on ground,
but if the ray returns nil, it means that the ray did not hit anything and that the player is probably in the air

But there is a problem, the ray might collide with the player in which case the function will think that the player is on ground, even tho the player might be floating in air

To fix this problem, we need to prevent the ray from colliding with the Player
We can do this by creating a new RaycastParams object in which we can define:

  • FilterDescendantsInstances: Array - Which objects the ray will collide and not collide with
  • FilterType: RaycastFilterType - whether the ray will avoid hitting the objects in the FilterDescendantsInstances table (Enum.RaycastFilterType.Exclude)
    or avoid hitting objects that are not in the table
    (Enum.RaycastFilterType.Include)

We can then set RaycastParams.FilterType to Enum.RaycastFilterType.Exclude and add the character to the FilterDescendantsInstances table

This makes the ray avoid colliding with the player, since the FilterType is set to Exclude which makes the ray avoid hitting all objects inside the FilterDescendantsInstances table, and our character is inside of it

Now we can put the RaycastParams object that we created as the third parameter in the Workspace:Raycast() function, this makes the ray use that RaycastParams object that we created

Here’s an example on how to check if the player is on ground with raycasting

local NewRaycastParams = RaycastParams.new()
NewRaycastParams.FilterType = Enum.RaycastFilterType.Exclude
NewRaycastParams.FilterDescendantsInstances = {Character}

local HeightAboveGround = 3.5

local function IsPlayerOnGround()
	local Origin = Root.Position
	local Direction = Vector3.new(0, -HeightAboveGround, 0)
	
	local Raycast = workspace:Raycast(Origin, Direction, NewRaycastParams)
	return Raycast ~= nil
end

I hope this helps, good luck with your project! :smiley:

Edits: I fixed all typos with FilterDescendantInstances (it’s supposed to be FilterDescendantsInstances)
and fixed the example code (i forgot to put the Character into the FilterDescendantsInstances table)
also, i added some more description about adding the Character into FilterDescendantsInstances table

Both of those won’t work as the statetype is physics.

Do I put this in a localscript?

I tried to add it but it doesn’t work.

--// Variables

local Player = game.Players.LocalPlayer
local Character = script.Parent
local Humanoid = Character:WaitForChild("Humanoid")
local Root = Character:WaitForChild("HumanoidRootPart")
local uis = game:GetService("UserInputService")
local physics = false
local retracted = false

local jumpAnim = Humanoid:LoadAnimation(script.JumpAnim)
local retractAnim = Humanoid:LoadAnimation(script.RetractAnim)

Humanoid.HipHeight = -0.5
Humanoid.WalkSpeed = 25

local NewRaycastParams = RaycastParams.new()
NewRaycastParams.FilterType = Enum.RaycastFilterType.Exclude
NewRaycastParams.FilterDescendantInstances = {}

local HeightAboveGround = 3.5

local function IsPlayerOnGround()
	local Origin = Root.Position
	local Direction = Vector3.new(0, -HeightAboveGround, 0)

	local Raycast = workspace:Raycast(Origin, Direction, NewRaycastParams)
	
	if Raycast == nil then
		physics = false
		jumpAnim:Stop()
		Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		task.wait(0.1)
		Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
		wait(0.8)
		Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		script.ParticlesEvent:FireServer(Character, physics)
	end
	
	return Raycast ~= nil
end

--// Event

uis.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end

	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		if retracted == false then
			if physics == false then
				physics = true
				jumpAnim:Play()
				Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
			else
				IsPlayerOnGround()
			end
		end
	end```

For some reason there’s something wrong with this which isn’t running the rest of the code.

local NewRaycastParams = RaycastParams.new()
NewRaycastParams.FilterType = Enum.RaycastFilterType.Exclude
NewRaycastParams.FilterDescendantInstances = {}

local HeightAboveGround = 3.5

local function IsPlayerOnGround()
local Origin = Root.Position
local Direction = Vector3.new(0, -HeightAboveGround, 0)

local Raycast = workspace:Raycast(Origin, Direction, NewRaycastParams)
return Raycast ~= nil

end

its filterdescendantsinstances not filterdescendantinstances

k lemme try that!!!

it barely works, only when i’m falling straight down I think. Heres a vid of what happens.

This is my code:

--// Variables

local Player = game.Players.LocalPlayer
local Character = script.Parent
local Humanoid = Character:WaitForChild("Humanoid")
local Root = Character:WaitForChild("HumanoidRootPart")
local uis = game:GetService("UserInputService")
local physics = false
local retracted = false

local jumpAnim = Humanoid:LoadAnimation(script.JumpAnim)
local retractAnim = Humanoid:LoadAnimation(script.RetractAnim)

Humanoid.HipHeight = -0.5
Humanoid.WalkSpeed = 25

local NewRaycastParams = RaycastParams.new()
NewRaycastParams.FilterType = Enum.RaycastFilterType.Exclude
NewRaycastParams.FilterDescendantsInstances = {}

local HeightAboveGround = 3.5

local function IsPlayerOnGround()
	local Origin = Root.Position
	local Direction = Vector3.new(0, -HeightAboveGround, 0)

	local Raycast = workspace:Raycast(Origin, Direction, NewRaycastParams)
	
	if Raycast ~= nil then
		
		physics = false
		jumpAnim:Stop()
		Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		task.wait(0.1)
		Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
		wait(0.8)
		Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		
	end
	
	return Raycast ~= nil
end

--// Event

uis.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end

	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		if retracted == false then
			if physics == false then
				physics = true
				jumpAnim:Play()
				 
                Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
			else
				IsPlayerOnGround()
			end
		end
	end
end)

oh, I just realized that I forgot to add the Character to the FilterDescendantsInstances table, which means that the ray will hit the character most of the time and think that it’s on ground

Change the third line with NewRaycastParams in it to this

NewRaycastParams.FilterDescendantsInstances = {Character}

Alr! Lemme try that and see what happens.

Thanks, it works but there’s one problem. When the player is rolling down a slope and gets flung just a bit, they can’t jump anymore. Here’s a video of what happens.

Increase the HeightAboveGround variable to something like 4 or 4.5, this should fix the problem

Thank you so much! It worked perfectly.