• 3D
  • Recording the time between the moment when the player is in a free fall and touching the ground

Hello! I want to record, in a variable, the time between the moment when the player is in a free fall and the moment when he touches the ground, in order to implement the health loss, when jumping from a great height.

How should I do this?

I'm not sure, but I think this will measure the time the player has been in the air (assuming a KinematicBody2D):

var in_air_time = 0

func _physics_process(delta):
	if (is_on_floor()):
		if (in_air_time > 0):
			# Make the player take damage here!
			print ("player was in the air for ", in_air_time, " seconds!")
			in_air_time = 0
	else:
		in_air_time += delta

Awesome explanation! :)

Yes, this code measures very well the time spent in the air.

Thanks!

If you're implementing fall damage, I think an easier and more realistic way to do so would be to damage the player based on their downward velocity when they hit the ground. It might not be necessary for your game, but this would allow you to implement things like pools of water that cushion the player's fall.

Good idea!

Now, I don't have any pool of water in my game, but I will consider this solution. )

14 days later

@Azedaxen , I just met the necessity to implement the damage based on downward velocity instead of time spent in the air. =)

When I jump off the top of the tree trunk, and then land on the same tree trunk, but nearer the ground (basically, I land on a flight zone) and then I peacefully get off the tree , when I touch the ground, the player receives damage from the time spent in the air, a few seconds ago, when he is not even falling.

It turns out that if I stay on the base of the trunk for a minute, then, when I touch the ground, I will receive damage from time spent in the air a minute ago. It is weird xD

Even if this solution works very well, in order to get rid of this bug, I have to modify it.

Do you guys know how to record the downward velocity when the player touches the ground?

I tried a modification of the previous solution:

	if is_on_floor():
		if (in_air_time > 0):	
			print(velocity.y)
			in_air_time = 0
	elif !flying:
		in_air_time += delta

but this solution measures the velocity when the player is already on the ground. The velocity is, of course, 0. ))

I have to record the velocity just before landing.

@Tudor said: Do you guys know how to record the downward velocity when the player touches the ground? Sorry, I would've seen this sooner but I've been busy.

Ultimately, this depends on how your character controller is programmed. For instance, my character has separate variables for horizontal and vertical velocity. The reason I did this was to make it easier to handle gravity and jumping separate from the WASD movement that the player inputs. Because of this, I also have convenient access to a vertical_movement property. My fall damage code would look something like this:

if grounded:
	if vertical_move <= -25.0:
		var falldamage = lerp(50, 100, abs(vertical_move)/terminal_velocity)
		# Player health is handled elsewhere

In this example, the player starts to take fall damage if they are falling at least as fast as half of their terminal velocity, (50) and hitting the ground at terminal velocity is fatal. lerp is used to ramp the fall damage to be anywhere from a minimum of 50 to a maximum 100 based on downward speed, where vertical_move/terminal_velocity is a percentage with 100% being terminal velocity, used to interpolate between these two values.

Edit: To further elaborate on this system, the way gravity works in my game is that the player's vertical_move is constantly being subtracted from if they are not on the ground, clamped to the terminal velocity. When they are on the ground, vertical_move is set to 0, so the check for a certain falling speed will only happen once. Setting the fall speed to 0 should happen after the fall damage check in order for this to work correctly.

What sort of variables do you use to handle gravity?

@Azedaxen said: Ultimately, this depends on how your character controller is programmed. What sort of variables do you use to handle gravity?

In my game's character controller, the "velocity" variable is a Vector3(), the "gravity" is a float, and I also use a boolean variable "has_contact", to check if the player is on floor:

	var velocity = Vector3()
	var gravity = -9.8 * 3
	var has_contact = false

How I handle gravity:

	var n = $Tail.get_collision_normal()

	if (is_on_floor()):
		has_contact = true
	
	var floor_angle = rad2deg(acos(n.dot(Vector3(0, 1, 0))))
	if floor_angle > MAX_SLOPE_ANGLE:
		velocity.y += gravity * delta
		
	else:
		if !$Tail.is_colliding():
			has_contact = false 
		velocity.y += gravity * delta

where "Tail" is the player's Raycast.

The rest of the "velocity" variable (x and z directions) are used for handle WASD movement system.

i would do it lil bit differently: i would take the velocity.y wich represents the speed the player falls and then calculate the dmg according to this..

for example: if velocity.y > 1000 && is_on_floor(): ..take dmg..

@Azedaxen said: For instance, my character has separate variables for horizontal and vertical velocity.

How did you separate the horizontal and vertical velocity?

In my game, the whole "walk(delta)" function stands on "velocity" variable. =)

@GogetaX said: i would do it lil bit differently: i would take the velocity.y wich represents the speed the player falls and then calculate the dmg according to this..

I would do the same, but this requires a way to measure the vertical velocity just before falling.

13 days later

@Tudor said: When I jump off the top of the tree trunk, and then land on the same tree trunk, but nearer the ground (basically, I land on a flight zone) and then I peacefully get off the tree , when I touch the ground, the player receives damage from the time spent in the air, a few seconds ago, when he is not even falling.

It turns out that if I stay on the base of the trunk for a minute, then, when I touch the ground, I will receive damage from time spent in the air a minute ago. It is weird xD

I solved this bug, by using another else statement in the initial solution, that sets the air time to 0, when the player is on a fly zone (tree trunk, in my case):

if is_on_floor():
		if (in_air_time > 0):
			# Make the player take damage here!
			if in_air_time > 1 && in_air_time < 1.4:
				impulse += 1
			elif in_air_time > 1.4:
				impulse += 2
			
			print("player was in the air for ", in_air_time, " seconds!")
			print("Impulse = ", impulse)
			if impulse > 0:
				if check_jump_attack():
					print("boom")
				else:
					health -= impulse
					print("not boom")
			$Armature/Skeleton/BoneAttachment/Head/Camera/CanvasLayer/HealthBar.set_value(health)
			
			in_air_time = 0
			impulse = 0
	
	elif flying:
		in_air_time = 0		
	
	elif !flying:
		in_air_time += delta

@Tudor said: When I jump off the top of the tree trunk, and then land on the same tree trunk, but nearer the ground (basically, I land on a flight zone) and then I peacefully get off the tree , when I touch the ground, the player receives damage from the time spent in the air, a few seconds ago, when he is not even falling.

It turns out that if I stay on the base of the trunk for a minute, then, when I touch the ground, I will receive damage from time spent in the air a minute ago. It is weird xD

I solved this bug, by using another else statement in the initial solution, that sets the air time to 0, when the player is on a fly zone (tree trunk, in my case):

if is_on_floor():
		if (in_air_time > 0):
			# Make the player take damage here!
			if in_air_time > 1 && in_air_time < 1.4:
				impulse += 1
			elif in_air_time > 1.4:
				impulse += 2
			
			print("player was in the air for ", in_air_time, " seconds!")
			print("Impulse = ", impulse)
			if impulse > 0:
				if check_jump_attack():
					print("boom")
				else:
					health -= impulse
					print("not boom")
			#Setting health value in the HealthBar
			
			in_air_time = 0
			impulse = 0
	
	elif flying:
		in_air_time = 0		
	
	elif !flying:
		in_air_time += delta