unity – Simulating Road Traffic/Overtaking – Overtake works mostly, but starts clipping other vehicle

I am trying to improve on my Car class, which is used to make a small road scene with 3 lanes each direction with the ability to overtake by changing lanes.

I had a very basic version working with all box colliders and a predictive movement script in my code (so basically I performed the movement of the object, then immediately check a collision and move it to previous_position in same frame). It worked OK but i found i was a bit clunky and I had to have oversized collision boxes on the car which caused me other problems elsewhere in the game.

Now I am using Raycast. At first I used just one Raycast from the centre of the car. But of course once the centre of the car had past the vehicle in front it would think road was clear. I tried to fix this by having 3 rays (centre, left, right in local space). This improved it a lot but the wingmirrors of the car (and often times a slice of the body) would clip into each-other.

I’ve ended up adding around 10 Raycasts trying to make sure it always sees the car in front. I even tried making the edge-most ones slightly past the width of the car.

The only thing I can think to mention is that the cars are rotated 90 degree. So transform.forward is actually facing either +/- X-Axis in world space depending on if isTopLane is true/false.

Here is the Car class in full. For the life of me I cannot figure out why the cars overtake but stop overtaking too early (ie. they continue straight through the edge of the car in front):

public class Car : MonoBehaviour
{
    bool isTopLane;
    float speed;

    float vehicle_width;
    float vehicle_length;
    Material material;



    AudioSource audioSource;
    float start_pitch;
    float max_pitch;

    float ray_length_forwards;
    float ray_length_sideways;



    public void InitCar(int spawn_z)
    {
        Color color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f), 1);
        speed = Random.Range(Level_Road.car_speed_min, Level_Road.car_speed_max);
        

        if (spawn_z <= 0)
            isTopLane = false;
        else
            isTopLane = true;

        float x = -Level_Road.SPAWN_POSITION_X;
        Vector3 rot = Vector3.zero;
        rot.y = 90;
        if (isTopLane)
        {
            x *= -1;
            rot.y *= -1;
        }
            
        transform.position = new Vector3(x, 0, spawn_z);
        transform.eulerAngles = rot;
        
        material = GetComponentInChildren<MeshRenderer>().material;
        material.color = color;

        audioSource = GetComponent<AudioSource>();
        start_pitch = audioSource.pitch + (speed * 0.01f);
        max_pitch = start_pitch * 2.6f;


        vehicle_width = GetComponent<Collider>().bounds.size.x;
        vehicle_length = GetComponent<Collider>().bounds.size.z;
        ray_length_forwards = GetComponent<Collider>().bounds.size.z * 1.4f;
        ray_length_sideways = GetComponent<Collider>().bounds.size.x * 1.4f;

        if ((isTopLane && transform.position.z < 1.1f) || (!isTopLane && transform.position.z > -1.1f))
            speed = Level_Road.car_speed_max;

    }

    private void FixedUpdate()
    {
        if (GameManager.instance.Game_Paused)
            return;

        audioSource.enabled = GameManager.instance.Options_SoundOn;

        audioSource.pitch += (speed * 0.04f) * Time.fixedDeltaTime;
        if (audioSource.pitch > max_pitch)
            audioSource.pitch = start_pitch;

        Drive();
    }

    void Drive()
    {
        Vector3 centre = transform.position;
        Vector3 right1 = centre + Vector3.up * (vehicle_width * 0.1f);
        Vector3 right2 = centre + Vector3.up * (vehicle_width * 0.2f);
        Vector3 right3 = centre + Vector3.up * (vehicle_width * 0.3f);
        Vector3 right4 = centre + Vector3.up * (vehicle_width * 0.4f);
        Vector3 right5 = centre + Vector3.up * (vehicle_width * 0.5f);
        Vector3 right6 = centre + Vector3.up * (vehicle_width * 0.65f);
        Vector3 left1 = centre + Vector3.down * (vehicle_width * 0.1f);
        Vector3 left2 = centre + Vector3.down * (vehicle_width * 0.2f);
        Vector3 left3 = centre + Vector3.down * (vehicle_width * 0.3f);
        Vector3 left4 = centre + Vector3.down * (vehicle_width * 0.4f);
        Vector3 left5 = centre + Vector3.down * (vehicle_width * 0.5f);
        Vector3 left6 = centre + Vector3.down * (vehicle_width * 0.65f);
        Vector3() ray_origins = { centre, right1, right2, right3, right4, right5, right6, left1, left2, left3, left4, left5, left6 };
        for (int i = 0; i < ray_origins.Length; i++)
        {
            if (Physics.Raycast(new Ray(ray_origins(i), transform.forward), out RaycastHit hit, ray_length_forwards))
            {
                if (hit.transform.gameObject.GetComponent<Car>())
                {
                    Overtake();
                    return;
                }

            }
        }
        
        DriveForwards();

    }

    void DriveForwards()
    {
        Vector3 motion = transform.forward * speed * Time.fixedDeltaTime;
        transform.position += motion;
    }


    void Overtake()
    {
        if (isTopLane && transform.position.z <= 1)
            return;

        if (!isTopLane && transform.position.z >= -1)
            return;
        

        if (Physics.Raycast(new Ray(transform.position, -transform.right), out RaycastHit hit, ray_length_sideways))
        {
            if (hit.transform.gameObject.GetComponent<Car>())
                return;
        }

        Vector3 motion = -transform.right * speed * Time.fixedDeltaTime;
        transform.position += motion;
    }

}

Thank you for your time and help.