I'm a total beginner, so I barely know what I'm doing, but here goes: I'm using this First Person Starter template and I would like to add a "springy legs" feature on character landing. The higher is the jump, the stronger the springy effect is on landing (but there should probably be a maximum cap value specified).

How can this be achieved?

EDIT: Here's a demonstration of what more or less what I'd like it to feel like: https://youtu.be/dSsV657JYRU

  • 2
    \$\begingroup\$ It'd help if you could more clearly describe what "springy legs" means with examples of games that do what you want. Does it allow you to jump higher if you jump right after landing (like 3D mario)? Does it make you bobble when you land? etc... \$\endgroup\$
    – idbrii
    Commented Nov 9, 2023 at 21:24
  • \$\begingroup\$ I'm sorry, I don't know what it's called or how to best achieve this effect - with just the camera or with the whole collision shape. I called it "springy legs" just because this is what knees do basically. This is more or less what it should work like: youtu.be/dSsV657JYRU \$\endgroup\$
    – ThetaJones
    Commented Nov 9, 2023 at 22:15

1 Answer 1


You could use a tween to bounce the camera when you land.

That requires two things:

  1. detecting when you land
  2. bouncing the camera

Modifying MovementController.gd in the template you're using to something like this might work. It's untested and I'm assuming that the camera always sits at its local position. If it's not working, you'd need to rework it so there's a CameraRoot with a Camera3D child and the system moves the CameraRoot around so we're free to animate the Camera3D.

@export var camera : Camera3D
@export var springy_leg_distance := 0.5
@export var springy_leg_seconds := 0.7

var _was_on_floor := false
var _camera_tween

func _physics_process(delta: float) -> void:
    input_axis = Input.get_vector("move_back", "move_forward", "move_left", "move_right")
    if is_on_floor():
        if Input.is_action_just_pressed("jump"):
            velocity.y = jump_height
        if _was_on_floor:
            # (1) At this point, we went from not on floor to on floor, so we
            # want to bounce.
            # We might also want to scale the amount of bounce by our
            # *previous* downward velocity.
        velocity.y -= gravity * delta
    # is_on_floor returns a different value after calling move_and_slide
    _was_on_floor = is_on_floor()

func _play_camera_ground_impact():
    # (2) Fire off a tween to bounce the camera to give a springy legs effect.
    if _camera_tween:
        # Prevent two overlapping tweens.
        # You may want to stop if you stop touching the ground too.
    _camera_tween = get_tree().create_tween()
    # Assuming default local position is zero!
    camera.position = Vector3.DOWN * springy_leg_distance # start down a bit
            Vector3.ZERO,                                 # tween back up

You could also use an animation to have more control over the motion of the camera.

  • \$\begingroup\$ Thank you! I'll try it out and report back. \$\endgroup\$
    – ThetaJones
    Commented Nov 10, 2023 at 3:46
  • \$\begingroup\$ I'm getting this: i.imgur.com/GLtTNQH.png \$\endgroup\$
    – ThetaJones
    Commented Nov 10, 2023 at 5:38
  • \$\begingroup\$ That means I made a mistake in how many items are in between the () braces after _camera_tween.tween_property. It's expecting 4, but I passed 6. You can look at the Tween docs to figure out the correct arguments: Object object, NodePath property, Variant final_val, float duration. I've passed a start position (which is wrong) and passed speed and duration (but it only wants duration). \$\endgroup\$
    – idbrii
    Commented Nov 14, 2023 at 23:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .