Having trouble resetting headbob height when not moving

Godot Version

4.2.1 Stable

Question

Hi,

I followed LegionGames “Juiced Up First Person Character Controller Tutorial”, and am having some trouble with improving the system.

The problem I have is that the height that the head is bobbing at isn’t resetting back to a default height when not moving, meaning that from the players perspective, the character is an inconsistent height based on how far through the sine wave the head bob is. I’m wondering how I might make it so that when the player isn’t moving, the camera transform will lerp back to the default height.

Here is the relevant code, any help would be greatly appreciated. Thanks

t_bob += delta * velocity.length() * float(is_on_floor())
camera.transform.origin = _headbob(t_bob)

func _headbob(time) -> Vector3:
  var pos = Vector3.ZERO
  pos.y = sin(time * BOB_FREQ) * BOB_AMP
  pos.x = cos(time * BOB_FREQ / 2) * BOB_AMP
  return pos

You can do like that:

if velocity.length() == 0:
    camera.transform.origin = lerp(camera.transform.origin, Vector2.ZERO, delta * 10.0)
else:
    t_bob += delta * velocity.length() * float(is_on_floor())
    camera.transform.origin = _headbob(t_bob)
2 Likes

slight suggested changes to your solution:

Velocity.length() is a float, not a vector2, so you’d want more like:

if is_zero_approx(velocity.length()):
     (... the rest...)

but i think it would be better to cache a value that is the velocity length times (is on floor), and based on the value of that result, then do the rest.

so more like:


var _bob_scale = velocity.length() * float(is_on_floor())
if is_zero_approx(_bob_scale):
#   (... fade out the bob...)
else:
#   (... do the animated bob effect...)
1 Like

Hi, I’ve made some some edits to the code, and I feel like I’m getting closer, my current code is:

#Headbob
if (!is_zero_approx(velocity.x) or !is_zero_approx(velocity.z)) and is_on_floor():
		camera.transform.origin = _headbob(t_bob)
		t_bob += delta * velocity.length() * float(is_on_floor())
	else:
		camera.transform.origin = lerp(camera.transform.origin,camera_origin,0.2)
		t_bob = 0

#Headbob
func _headbob(time) -> Vector3:
	var pos = Vector3.ZERO
	pos.y = sin(time * BOB_FREQ) * BOB_AMP
	pos.x = cos(time * BOB_FREQ / 2) * BOB_AMP
	return pos

It’s now doing something weird where it will only reset if I’m walking into a wall, which puts my velocity to very low levels, and makes the bob camera movement go very slowly, not sure why this is the only instance where it will reset back to default position. Any help would be appreciated!

perhaps your CharacterBody3D’s motion_mode parameter is set to floating, instead of grounded?

also, where you multiply delta times velocity length, that will currently have problems with gravity and falling, jumping, etc. So I’d suggest caching velocity’s current value to a new vector3, then zero’ing out that vector’s Y component, and then using the length of that modified vector for the head bob scaling. Also in that same statement, the multiplication by float(is_on_floor()) is no longer necessary, since it will always be true at that point because of the if block it’s within.

Other things worth a look: make sure you’re calling move and slide, even if the input has not indicated it’s necessary (ie, there was no input, so maybe you’re not moving_and_sliding). Otherwise I suspect velocity is not going to update correctly, and that might create issues (edit: I’m fairly unclear on this part, like if it would actually matter, so if anyone knows for sure one way or another, please say so).