Detecing if the player lands, and if so Just_landed = true for set amount of time, unless in air again in air

Current Issue, with the way I am detecting if the player just lands is not working as expected.
Code

	if humanoid.FloorMaterial ~= Enum.Material.Air and not Player_In_Air then
		Just_Landed = true
		task.delay(LAND_FREE_TIME, function()
			if Player_In_Air then
				Just_Landed = false
			else
				Just_Landed = false
			end
		end)
	end

The expected result I want is if the player lands then Just_Landed = true else its false, but if the player jumps again during the timeframe Just_Landed = false.

Anybody have any ideas or better methods this?

FullCode

function renderteppedFunction(ResponseDelay: number)
	if humanoid.FloorMaterial == Enum.Material.Air then
		Player_In_Air = true
	else
		Player_In_Air = false
	end

	Current_Move_Direction = Vector3.new(humanoid.MoveDirection.X, humanoid.MoveDirection.Y, humanoid.MoveDirection.Z)

	if humanoid.FloorMaterial ~= Enum.Material.Air and not Player_In_Air then
		Just_Landed = true
		task.delay(LAND_FREE_TIME, function()
			if Player_In_Air then
				Just_Landed = false
			else
				Just_Landed = false
			end
		end)
	end
	print(Just_Landed)

	task.wait(ResponseDelay)
end

You can use Humanoid’s StateChanged method to check for the Landed state with the HumanoidStateType Enumeration.

Issue with this Is I am going to be doing custom state management later.

BUT the main issue with this is I have tried doing Humanoid:Getstate() of landed, and added a os.clock to it but that did not work at all, even though my code was 100% correct after I talked to 3 other people about it.

Humanoid:GetState() is not a good thing to use in my opinion after what I just dealt with it.

You are setting Just_Landed to false with both conditions. Why check for Player_In_Air if you’re going to set it to false either way?

incorrect.

	if humanoid.FloorMaterial ~= Enum.Material.Air and not Player_In_Air then
		Just_Landed = true

The reason why I set it up like

		task.delay(LAND_FREE_TIME, function()
			if Player_In_Air then
				Just_Landed = false
			else
				Just_Landed = false

is if the player IS in the air Just_Landed is = false else Just_Landed = false after the LAND_FREE_TIME wait is over.

Looking at this I may have a dif idea

You are setting Just_Landed to true every frame that you’re on the ground, and then setting it to false after a delay? As long as you stay on the ground, I presume that would change your value back and forth.

It shouldent change back and forth, in which it Doesent. But right now I am thinking of something to fix it, which i think I may have found. I just need to think harder

forget that, I just lost it.

here is the updated Renderstepped function.

function renderteppedFunction(ResponseDelay: number)
	if humanoid.FloorMaterial == Enum.Material.Air then
		Player_In_Air = true
	else
		Player_In_Air = false
	end

	Current_Move_Direction = Vector3.new(humanoid.MoveDirection.X, humanoid.MoveDirection.Y, humanoid.MoveDirection.Z)

	if humanoid.FloorMaterial ~= Enum.Material.Air and not Player_In_Air then
		Just_Landed = true
		task.delay(LAND_FREE_TIME, function()
			if Player_In_Air then
				Just_Landed = false
			else
				Just_Landed = false
			end
		end)
	end

	if Player_In_Air then
		Just_Landed = false
	end

	
	print(Just_Landed)

	task.wait(ResponseDelay)
end

I added

	if Player_In_Air then
		Just_Landed = false
	end

because that will not be checked nonstop in the task.delay

humanoid state is pretty unreliable sometimes, so I found the most reliable way is to use a combination of both humanoid state and a raycast downwards (or towards where the player is heading) to check if it hit the ground.

Below is some basic pseudocode to get you started. Commented some parts but if there’s something you don’t understand lmk

local raycastParams = ...
local previousPosition = character.PrimaryPart.Position
local currentPosition = character.PrimaryPart.Position
local raycastDistance = 3
local direction
local connection = nil

connection = RunService.Heartbeat:Connect(function()
	currentPosition = character.PrimaryPart.Position
	direction = (currentPosition - previousPosition).Unit --direction here

	if currentPosition ~= previousPosition then
		--raycast towards the direction the player is being thrown in
		local raycastResult = workspace:Raycast(character.PrimaryPart.Position, direction * raycastDistance, raycastParams)
		if raycastResult then
			--create a new part and set its cframe for visual effect to see where it landed
			local groundPart = Instance.new("Part")
			groundPart.CFrame = CFrame.lookAt(raycastResult.Position, raycastResult.Position + raycastResult.Normal)
			groundPart.Parent = workspace

			--disconnect the connection
			connection:Disconnect()
			connection = nil
		end
	end

	--set the new position here
	previousPosition = currentPosition
end)

I’d personally skip the task.delay() mechanism, since it really only adds confusion, and just do something more like this:

local inAir = false
local justLanded = false
local lastLandingTime = 0
local LAND_FREE_TIME = 0.5

function OnRenderstep( dt )
	local now = tick()
	
	local prevInAir = inAir
	inAir = humanoid.FloorMaterial == Enum.Material.Air
	
	if prevInAir and (not inAir) then
		justLanded = true
		lastLandingTime = now
	end
	
	if inAir or now > lastLandingTime + LAND_FREE_TIME then
		justLanded = false
	end
end
2 Likes

I never knew about ticking, or atleast a use case like this. This is a much better solution than raycasts. :heartpulse:

There are a few different functions built into Roblox Luau that get you a time: time(), os.time(), and tick(). They are all in seconds, the difference is that time() is seconds since the server/client started up, where as tick() and os.time() are seconds since the Unix Epoch of 1/1/1970. One of them adjusts for time zone as well. When all you care about is elapsed differences, it’s pretty common to use either tick() or time(), and just be consistent with it.

Since everything is happening on Renderstepped, you could also just accumulate the dt value yourself in a local variable. The end result would be the same.