How do capsulecasts work?

I’m testing out capsule casts and I can’t understand how they work.

Right now I’m casting a capsule forwards to check if anything is obstructing my movement with

        Vector3 p1 = transform.position + charContr.center + Vector3.up * -charContr.height * 0.5F;
        Vector3 p2 = p1 + Vector3.up * charContr.height;
       if (Physics.CapsuleCast (p1, p2, charContr.radius, transform.forward, out hit, Mathf.Infinity)) {
            distanceToObstacle = hit.distance;
        }

I was expecting to get

distanceToObstacle = 0

When my character is directly in front of an obstacle, instead I’m getting 0 when there’s still a big distance between me and the obstacle:

Why is this happening?

This is happening because transform.forward is normalized vector showing just direction where your character is looking, but not related to it’s position. Wherever you move your player object, transform.forward will stay on sphere 1 radius around (0,0,0). Use transform.position + transform.forward to cast capsule at 1 meter in front of the player object.

Seems like I’m getting the exact same results even with this

        if (Physics.CapsuleCast (p1, p2, charContr.radius, transform.position + transform.forward, out hit, Mathf.Infinity)) {
            distanceToObstacle = hit.distance;
        }

Do the player object have a collider itself?

Yes, I’m getting it with

        CapsuleCollider charContr = GetComponent<CapsuleCollider> ();

I think my problem might be related to this one here Is the CapsuleCast function broken? - Questions & Answers - Unity Discussions

But I don’t understand how they fixed it

Okay so, for future reference if anybody has this problem I think I understand what I was doing wrong.

First of all getting radius and height of a collider returns the actual number that is set in the inspector. If these have been scaled down by setting a scale value on the object they’re attached to that will not be counted. In my case, both my height and my radius were much bigger than they should’ve actually been in my scene since my player object was set to Scale (.2, .2, .2)

Secondly, I had to start casting my capsule above the actual capsule collider attached to my player. I’m not sure why this is, but that fixed it for me.

always keep the player scale at vector one. if you want to change the model size add another gameobject inside the player and put the model in/on that. then scale that new object. that way everything else is unaffected in this way

I don’t know if this is still relevant for someone but I wrote a little struct that contains the needed arguments for capsule casting and overlap collisions accounting for the object’s global scale.

public struct CapsuleUtility
{
    public Vector3 Point0;
    public Vector3 Point1;
    public float Radius;
    public CapsuleUtility(CapsuleCollider capsuleCollider)
    {
        Transform capsuleTransform = capsuleCollider.transform;
        Vector3 capsuleCenter = capsuleTransform.TransformPoint(capsuleCollider.center);

        Radius = GetRadius(capsuleCollider);
        float height = GetHeight(capsuleCollider);

        float capsuleHalfHeight = height / 2 - Radius;
        Vector3 capsuleDirection = capsuleTransform.TransformDirection(GetDirection(capsuleCollider));

        Point0 = capsuleCenter + capsuleDirection * capsuleHalfHeight;
        Point1 = capsuleCenter - capsuleDirection * capsuleHalfHeight;
    }
    private static Vector3 GetDirection(CapsuleCollider capsuleCollider)
    {
        switch (capsuleCollider.direction)
        {
            case 0: //direction X-Axis
                return Vector3.right;
            case 1: //direction Y-Axis
                return Vector3.up;
            case 2: //direction Z-Axis
                return Vector3.forward;
            default:
                return Vector3.right;
        }
    }
    private static float GetRadius(CapsuleCollider capsuleCollider)
    {
        Vector3 globalScale = capsuleCollider.transform.lossyScale;
        switch (capsuleCollider.direction)
        {
            case 0: //direction X-Axis
                return capsuleCollider.radius * Mathf.Max(globalScale.y, globalScale.z);
            case 1: //direction Y-Axis
                return capsuleCollider.radius * Mathf.Max(globalScale.x, globalScale.z);
            case 2: //direction Z-Axis
                return capsuleCollider.radius * Mathf.Max(globalScale.x, globalScale.y);
            default:
                return capsuleCollider.radius;
        }
    }
    private static float GetHeight(CapsuleCollider capsuleCollider)
    {
        Vector3 globalScale = capsuleCollider.transform.lossyScale;
        switch (capsuleCollider.direction)
        {
            case 0: //direction X-Axis
                return capsuleCollider.height * globalScale.x;
            case 1: //direction Y-Axis
                return capsuleCollider.height * globalScale.y;
            case 2: //direction Z-Axis
                return capsuleCollider.height * globalScale.z;
            default:
                return capsuleCollider.height;
        }
    }