11
\$\begingroup\$

I'm currently running into an issue with my character controller using physics-based movement of rigidbodies in Unity.

Here's an illustration of the issue I'm facing:

character sticks on landing

Basically, after a jump, my character sticks to the ground for a split second after landing from a jump. While I have no idea, I just assumed this had something to do with the way Physics were being implemented, given that a portion of the character's velocity is heading downward, it makes sense that there would be some "impact time". I drew a bit of depenetration in the diagram, but that's mostly some creative thinking by me, I don't actually know if that has anything to do with it.

My current solution is to do this during the "landing" frame:

// first frame that the ground is detected
var velocity = rigidbody.velocity;
velocity.y = 0;
rigidbody.velocity = velocity;

This works nicely. However, I've now implemented custom gravity into the project, which allows for completely arbitrary gravity vectors (meaning I can't just set something to 0 and be done with it).

Is it possible to nullify all force in the "gravity" direction upon landing? It's also possible I've got a problem elsewhere if this reads like something that should be happening.

Here's a snippet that has the meat of my movement code:

        var velocity = rigidbody.velocity;
        var normal = CollisionHelper.GetFloorNormal(capsuleCollider, true);

        Vector3 xAxis = ProjectDirectionOnPlane(rightAxis, normal);
        Vector3 zAxis = ProjectDirectionOnPlane(forwardAxis, normal);
        float currentX = Vector3.Dot(velocity, xAxis);
        float currentZ = Vector3.Dot(velocity, zAxis);
        float acceleration = isGrounded ? defaultAcceleration : airAcceleration;
        float maxSpeedChange = acceleration * Time.deltaTime;

        var desiredVelocity = new Vector3(wrapped.input.move_H, 0, wrapped.input.move_V) * defaultSpeed;

        float newX = Mathf.MoveTowards(currentX, desiredVelocity.x, maxSpeedChange);
        float newZ = Mathf.MoveTowards(currentZ, desiredVelocity.z, maxSpeedChange);
        velocity += xAxis * (newX - currentX) + zAxis * (newZ - currentZ);

        rigidbody.velocity = velocity;
\$\endgroup\$
2
  • 2
    \$\begingroup\$ What you probably want to do isn't to "nullify all force in the 'gravity' direction", but nullify all force in the direction perpendicular to the surface you've hit so that, for example, if your character lands vertically on a sloped surface, it'll stop moving into the surface but still keep rolling / running down along the surface. \$\endgroup\$ Nov 29 '21 at 14:57
  • \$\begingroup\$ I may end up doing something like that @IlmariKaronen - depending on how it feels, I may still want a bit of "impact" when hitting surfaces that aren't exactly orthogonal to gravity, like a hill that's on a mini planetoid. Either way it's a good note that should be considered. \$\endgroup\$
    – lase
    Nov 29 '21 at 15:40
15
\$\begingroup\$

This is pretty simple linear algebra. You can make a little helper method for it:

public static class VectorExtensions {

    public static Vector3 RemoveComponent(this Vector3 vector, Vector3 direction) {
        direction = direction.normalized;

        return vector - direction * Vector3.Dot(vector, direction);
    }
}

Then just say:

velocity = velocity.RemoveComponent(Physics.gravity);
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Wow - earlier I had tried nearly exactly this but was trying to multiply with something like var percentage = 1 - dotProduct because I was worried about removing too much. Overthought it! This works like a charm. Thanks so much @DMGregory. \$\endgroup\$
    – lase
    Nov 29 '21 at 1:21

You must log in to answer this question.

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