Troubles with KinematicBody move()

SomnivoreSomnivore Posts: 48Member
in 3D

I have a KinematicBody character with a capsule collider attached to it. I am attempting to slide this character along a surface. The Vector3.slide() function seems to work as you'd expect: with some horizontal movement vector and gravity, sliding across a surface with an upwards-facing normal zeroes out the character's vertical velocity and leaves just a horizontal movement, which one would reasonably expect to enable the player to glide along the surface without any issue; after all, gravity has already been applied and will not be applied again until next fixed process, so it's perfectly horizontal and thus should not be interrupted by a collider beneath the body.

So far so good: the issue is that, after applying the slide to both the extra motion and the character's velocity, and feeding the motion into move(), move() spits out the exact same vector as was given to it; in other words it doesn't move anywhere as it still believes it's colliding with something, and that something is apparently the floor that I was supposed to slide across. The result is that the KinematicBody gets stuck after a short time and refuses to move until you feed it another movement vector, and even that won't work 99 fixed processes out of 100

Modifying the KinematicBody's collision margin to a high number (at least 0.01 afaict) entirely removes the problem, but makes it so that the KinematicBody snaps to the floor when it gets close enough. The lower this number is, the more often the body will stick. Any number higher than the lowest number possible is unworkable since the snapping is very jarring and unnatural.

Oddly the Kinematic Character 3D example works just fine; I even copied over the code from that project into mine just to see if it would work, but it does not and my body still gets stuck.

I've tried doing exactly the same on the KinematicBody2D and that works perfectly fine as well.

Does anyone have experience using KinematicBody? Is there something I'm simply not getting? Is there something about KinematicBody's move() that I need to compensate for, such as just ensuring my player hovers away from colliders by a small bit? Here's the logic I'm working with now (stripped of irrelevant bits):

func _fixed_process(delta):
    velocity += desired_motion

    velocity.y -= delta * gravity

    var motion = move(velocity * delta)

    var col_checks = 4
    while(is_colliding() and col_checks):
        var normal = get_collision_normal()

        velocity = normal.slide(velocity)
        motion = normal.slide(motion)

        motion = move(motion)

        col_checks -= 1

Answers

  • TwistedTwiglegTwistedTwigleg Posts: 111Member

    Not sure, but your while loop might be the problem. Instead of while(is_colliding() and col_checks):, I would try changing it to while(is_colliding() and col_checks > 0): and see if that helps.

    I found in Godot 3.0 (might be in Godot 2 as well) that switching the order for the slide helped with a similar problem (my character got stuck in the floor). Here's the logic bit of the code that worked for me:

    func _fixed_process(delta):
    
            var motion = velocity * delta;
            motion = move(motion);
    
    
            if (is_colliding()):
            var norm = get_collision_normal();
    
            motion = motion.slide(norm);
            velocity = velocity.slide(norm);
    
            move(motion);
    
  • SomnivoreSomnivore Posts: 48Member

    @TwistedTwigleg The while loop is fine, the and col_checks is how the Kinematic Character 3D example does it and I stuck with it; I assume it's just shorthand for checking whether col_checks is non-zero. It runs on the floor all four times in any case.

    Testing the inversion of what slides what, it seems that the character will hop around a lot and then get stuck, so I switched back to the way I was doing it initially. From what I'm able to gather, so long as I give the KinematicBody a consistent velocity, it will not get stuck, though it may stutter. As soon as I attempt to take control over the KinematicBody's velocity, it will stutter and ultimately stick. My only possible guess is that the Kinematic Character 3D demo is using linear interpolation to keep the body moving around smoothly, which I feel is a hacky way of getting that done; having debugged that, I found that its is_colliding() loop wasn't being called, despite being pushed into a floor plane and a wall. But I only tried that once so maybe it was a fluke.

    Thanks for replying in any case; though I still don't understand why KinematicBody is not working as I expect it to, I found another solution to my problem: I wound up rolling with a RigidBody in Character mode with a custom integrator; it does the sliding bit automatically already, while giving a great deal of flexibility over how the body moves.

  • TwistedTwiglegTwistedTwigleg Posts: 111Member

    I'm glad you found a solution!

    I've found that the KinematicBody is rather complicated and only seems to work with special hacks, like the inversion, which seems to only work in my case. Probably means I'm doing something wrong :D. Having read your explanation above, I think I'll use a RigidBody next time!

    My apologies on the while loop. I went into full debug mode, which for me means tweaking little things that I assume work, and generally finding the bug was something silly on my part.

  • SomnivoreSomnivore Posts: 48Member
    Answer ✓

    I think the problem I was facing is not in the code I was using. The problem seems to stem from the collider the KinematicBody is colliding with. I think I was setting this floor collider to be infinitely thin, which was causing odd behavior in the KinematicBody. With a floor collider that has some thickness to it, the KinematicBody appears to work fine.

    I faced a similar issue some time ago implementing a RigidBody character, and was looking through all my code to see what I was doing wrong. Ultimately it turned out to be that bodies and infinitely flat surfaces strongly disagree with one another; in this instance, the rigidbody was sometimes reading the normal of the infinitely thin floor as being the opposite of what it was supposed to be (i.e. (0, -1, 0), a ceiling), which caused the body to occasionally remain in a state of falling until something or some miracle got it unstuck. I believe this is due to there being precisely two sides of the same collider fighting for the same exact space.

    Anyway moral of the story is, make sure your collider bounds are non-zero.

Sign In or Register to comment.