godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
87.54k stars 19.71k forks source link

CharacterBody3D with skeleton and root motion animations gets stuck against other physics objects #90402

Open proggen-com opened 3 months ago

proggen-com commented 3 months ago

Tested versions

System information

Godot v4.3.dev (0c6b5efab) - Windows 10.0.22631 - Vulkan (Forward+)

Issue description

When running against another physics object with CharacterBody3D with a skeleton and root motion animations the body gets stuck against other physics objects. Using a capsule as the main physics collider.

Looking into this a bit more it seems there are three places in move_and_slide() that can cause the horizontal velocities set to zero.


@@ -178,11 +178,11 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
                    if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) {
                        velocity = ceiling_vertical_velocity + velocity.slide(up_direction);
                    }
                }
            }
-
+           // HERE (the velocity vector is cleared a bit lower, but the < 0.01 seems a bit arbitrary)
            if (collision_state.floor && floor_stop_on_slope && (velocity.normalized() + up_direction).length() < 0.01) {
                Transform3D gt = get_global_transform();
                if (result.travel.length() <= margin + CMP_EPSILON) {
                    gt.origin -= result.travel;
                }
@@ -250,10 +250,11 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
                        if (vel_dir_facing_up) {
                            Vector3 slide_motion = velocity.slide(result.collisions[0].normal);
                            // Keeps the vertical motion from velocity and add the horizontal motion of the projection.
                            velocity = up_direction * up_direction.dot(velocity) + slide_motion.slide(up_direction);
                        } else {
+                           // HERE (this cancels out the movement sometimes, perhaps a rotation issue)
                            velocity = velocity.slide(forward);
                        }

                        // Allow only lateral motion along previous floor when already on floor.
                        // Fixes slowing down when moving in diagonal against an inclined wall.
@@ -291,10 +292,11 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
                if (p_was_on_floor && (wall_min_slide_angle > 0.0) && result_state.wall) {
                    Vector3 horizontal_normal = wall_normal.slide(up_direction).normalized();
                    real_t motion_angle = Math::abs(Math::acos(-horizontal_normal.dot(motion_slide_up.normalized())));
                    if (motion_angle < wall_min_slide_angle) {
                        motion = up_direction * motion.dot(up_direction);
+                       // HERE (this cancels the movement sometimes, perhaps a rotation issue)
                        velocity = up_direction * velocity.dot(up_direction);

                        apply_default_sliding = false;
                    }
                }

Removing these velocity assignments on those lines will fix the getting stuck. But the character will slip and slide.

Steps to reproduce

Run the test project and walk against the cylinder wall for some time.

Minimal reproduction project (MRP)

stuck.zip

https://github.com/godotengine/godot/assets/91064515/b66fef75-3144-4d61-822f-4b89d1a1cc9c

fire commented 3 months ago

@TokageItLab Is this related to the root motion in animations problem?

TokageItLab commented 3 months ago

@fire No, it is not relevant animation as the root motion is just a value. I do not know how the authors of the issue apply the root motion, but this is a physical area issue as long as it is applied as follows as I have wrote in the document:

func _process(delta):
    if Input.is_action_just_pressed("animate"):
        state_machine.travel("Animate")
    set_quaternion(get_quaternion() * animation_tree.get_root_motion_rotation())
    var velocity: Vector3 = (animation_tree.get_root_motion_rotation_accumulator().inverse() * get_quaternion()) * animation_tree.get_root_motion_position() / delta
    set_velocity(velocity)
    move_and_slide()

If the root motion is being added directly to the coordinate values, then the script is wrong.