What's the deal with CharacterController?

What I’m trying to do is, as demonstrated here, have the blue unit push the red one out of the way as it’s moving then have the red unit try to snap back to its original position if nothing is in its way (or at least move as close to it as it can without colliding with anything). While the entire thing is taking place, the red unit and the blue unit are always suppose to be distance away from each other. And, while the code works just fine when the colliders attached to the units are capsule colliders, everything falls apart as soon as I swap them for character controllers and I have no idea why.

7191859--863116--Shove demo.gif

This is the relevant code for everything that’s happening in the scene (assuming the colliders are character controllers)

Blue move code

void Update()
        {
            if (isSelected && transform.position != destination)
            {
                //Set angle and magnitude
                float angle = AngleBetween(transform.position, destination);
                float magnitude = Time.deltaTime * speed;
                if (magnitude > Vector3.Distance(transform.position, destination))
                {
                    magnitude = Vector3.Distance(transform.position, destination);
                }
                //Capsule cast
                float radius = cc.radius * transform.localScale.x + MARKER_EXTENSION * 2 + SUBJECT_CORRECTION;
                RaycastHit[] hits = Physics.CapsuleCastAll(transform.position, Top(transform.position), radius, AngleToVector(angle), magnitude);
                //Shove block
                foreach (RaycastHit hit in hits)
                {
                    if (hit.collider != GetComponent<Collider>() && hit.collider.GetComponent<CharacterController>() != null)
                    {
                        hit.collider.GetComponent<TestScript>().Shove(gameObject, angle, magnitude);
                    }
                }
                //Move block
                if (magnitude == Vector3.Distance(transform.position, destination))
                {
                    transform.position = destination;
                }
                else
                {
                    cc.Move(AngleToVector(angle) * magnitude);
                }
            }
        }

Red move code

void LateUpdate()
        {
            //Attempt to snapback
            if (!isSelected && transform.position != anchoredPosition)
            {
                //Set angle and magnitude
                float angle = AngleBetween(transform.position, anchoredPosition);
                float magnitude = Time.deltaTime * speed;
                if (magnitude > Vector3.Distance(transform.position, anchoredPosition))
                {
                    magnitude = Vector3.Distance(transform.position, anchoredPosition);
                }
                //Overlap capsule to avoid snagging on anything behind this unit
                float radius = cc.radius * transform.localScale.x + MARKER_EXTENSION * 2 + OBJECT_CORRECTION;
                Collider[] hits = Physics.OverlapCapsule(transform.position + AngleToVector(angle) * magnitude, Top(transform.position) + AngleToVector(angle) * magnitude, radius);
                magnitude = AdjustMagnitude(hits, magnitude);
                //Move block
                if (magnitude == Vector3.Distance(transform.position, anchoredPosition))
                {
                    transform.position = anchoredPosition;
                }
                else
                {
                    cc.Move(AngleToVector(angle) * magnitude);
                }
                print(Vector3.Distance(transform.position, GameObject.Find("Evil Lich").transform.position));
            }
        }

Shove function

public void Shove(GameObject caller, float cAngle, float cMagnitude)
    {
        //Only works if the unit isn't selected
        if (!isSelected)
        {
            //Zero out positions
            Vector3 myPosition = new Vector3(transform.position.x, 0, transform.position.z);
            Vector3 callerPosition = new Vector3(caller.transform.position.x, 0, caller.transform.position.z) + AngleToVector(cAngle) * cMagnitude;
            //Calculate magnitude
            float radiusum = cc.radius * transform.localScale.x + caller.GetComponent<CharacterController>().radius * caller.transform.localScale.x + MARKER_EXTENSION * 2;
            float distance = Vector3.Distance(callerPosition, myPosition);
            float angleBetween = Vector3.Angle(AngleToVector(cAngle), (myPosition - callerPosition).normalized);
            float theta = 180 - (90 + angleBetween + Mathf.Asin(Mathf.Cos(Mathf.Deg2Rad * angleBetween) * distance / radiusum) * Mathf.Rad2Deg);
            float magnitude = Mathf.Sqrt(Mathf.Pow(distance, 2) + Mathf.Pow(radiusum, 2) - 2 * distance * radiusum * Mathf.Cos(Mathf.Deg2Rad * theta));
            //Decide direction 
            Vector3 rightPosition = myPosition + AngleToVector(cAngle + 90) * magnitude;
            Vector3 leftPosition = myPosition + AngleToVector(cAngle - 90) * magnitude;
            float shoveAngle;
            if (Vector3.Distance(callerPosition, rightPosition) >= Vector3.Distance(callerPosition, leftPosition))
            {
                shoveAngle = cAngle + 90;
            }
            else
            {
                shoveAngle = cAngle - 90;
            }
            //cc.Move(AngleToVector(shoveAngle) * magnitude);
            if (magnitude > 0)
            {
                cc.Move(AngleToVector(shoveAngle) * magnitude);
            }
        }
    }

Adjust Magnitude function

float AdjustMagnitude(Collider[] hits, float magnitude)
    {
        foreach (Collider hit in hits)
        {
            if (hit != GetComponent<Collider>() && hit.GetComponent<CharacterController>() != null)
            {
                //Sides
                float radiusum = cc.radius * transform.localScale.x + hit.GetComponent<CharacterController>().radius * hit.transform.localScale.x + MARKER_EXTENSION * 2;
                float returnDistance = Vector3.Distance(transform.position, anchoredPosition);
                float actorTrajectory = Vector3.Distance(hit.transform.position, anchoredPosition);
                //Angles
                float theta = Mathf.Acos((Mathf.Pow(returnDistance, 2) + Mathf.Pow(actorTrajectory, 2) - Mathf.Pow(radiusum, 2)) / (2 * returnDistance * actorTrajectory));
                float phi = Mathf.Asin(Mathf.Sin(theta) * actorTrajectory / radiusum);
                //Magnitude
                float leftover = radiusum * Mathf.Sin(Mathf.PI - (theta + phi)) / Mathf.Sin(theta);
                float prospectiveMagnitude = returnDistance - leftover;
               
                if (prospectiveMagnitude < magnitude)
                {
                    magnitude = prospectiveMagnitude;
                }
            }
        }
        return magnitude;
    }

I’m confused: The video looks reasonable to me… is the video showing what it does with colliders and what you WANT it to do with CC and it is doing something else?

It’s certainly close enough to what it’s suppose to look like to get the basic picture of what the code is ultimately suppose to do, but there are still problems such as the distancing between the two units at any given time. There are frames where they’re more than distance apart when they aren’t suppose to be and there’s even a frame where the red unit apparently doesn’t see where the blue unit is when it’s suppose to and refuses to no matter how big I set OBJECT_CORRECTION. Like I said, these problems completely disappear when I use capsule colliders instead of character controllers, but as soon as I swap back to character controllers then everything falls apart and I have to use those SUBJECT_CORRECTION/OBJECT_CORRECTION constants and even then they don’t account for everything I want them to account for.