#CharacterBody3D with local gravity override and character's feet always pointing downwards

2 messages · Page 1 of 1 (latest)

rustic loom
#

I have a character using CharacterBody3D, and I want to have the character orient itself correctly depending on the current applied gravity. I have a solution mostly working which works for gravity (0, 9.8, 0) and (0, -9.8, 0), but for (9.8, 0, 0) I'm getting some weirdness.
The movement I have works correctly for gravity along the x-axis, which is to say that I walk in the correct directions with the character, but the applied gravity is not at all what I expect. It's not that the direction is wrong, but rather that the applied gravity along the x-axis is super low and the character falls extremely slowly.

I've attached an example project.

#
func _physics_process(delta: float) -> void:
    var state = PhysicsServer3D.body_get_direct_state(get_rid())

    # Make sure that the up direction is the oposite of gravity, so that on_floor always works.
    if state.total_gravity != Vector3.ZERO:
        up_direction = -state.total_gravity.normalized()

    # Make our feet go towards the gravity direction.
    var rotated_transform = global_transform.looking_at(global_position + (Vector3.FORWARD * global_transform.basis), up_direction)
    if global_transform.is_equal_approx(rotated_transform):
        global_transform = rotated_transform
    else:
        global_transform = global_transform.interpolate_with(rotated_transform, delta * 5)

    # Convert the velocity into local space to make it easier to work with.
    var local_velocity = transform.basis * velocity

    # Handle Jump.
    if Input.is_action_just_pressed("jump") and is_on_floor():
        local_velocity = Vector3.UP * JUMP_VELOCITY

    # Get the input direction and handle the movement.
    var input_dir = Input.get_vector("strafe_left", "strafe_right", "walk_forwards", "walk_backwards")
    var direction = (neck.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
    if direction:
        local_velocity.x = direction.x * SPEED
        local_velocity.z = direction.z * SPEED

    else:
        local_velocity.x = 0
        local_velocity.z = 0

    velocity = global_transform.basis * local_velocity
    velocity += state.total_gravity * delta

    move_and_slide()