How can I make cam move in a perfect circular orbit, but without diving into colliders?

I am making a moving-sphere game from scratch. For the camera, I decided to control it through transform instead of rigidbody. I’ve tried several kind of approaches, but using a circle function around the target has offered me the most accurate rotation and following.

Why I move cam through Transform rather than Rigidbody

The motive I decided to implement cam moving through transform is that no matter how fast the ball is moving, or which direction the cam is looking: operating cam coords directly makes sure the cam is always over a perfect circle around the sphere. Also, due to my approach sets the camera position relative to the target’s position (the sphere) in every frame, this way the relative positions of both the camera and the sphere remain the same, independent from the sphere’s rotation.

I just couldn’t figure out how to achieve the same without overriding cam’s physics (ALERT: I am assuming that any translation or rotation through transform overrides physics, but I am not sure if this assumption is completely true).

Other approaches I tried and failed (for the record)

  • Making cam a child of the sphere: epic fail, cam starts following the sphere rotation and I don't want that
  • The accepted answer in [this question][1]: It works, but it lacks accuracy. The orbit might be a perfect circle when the ball is not moving, but when moving(specially at high speeds), even through the cam follows the ball, the orbit gets unacceptable imperfections. || **UPDATE:** @whebert, who posted that answer, published in a comment an updated version of the snippet in his answer. I tried it and it works as fine as my approach, but it is based on `transform` as well.
  • My approach works well, but…

    …it becomes a problem when avoiding that the cam goes through walls, though. I tried tracing rays out of the wall, but then the cam makes an annoying ‘inside-out’ alternative move.

    What can I do to prevent the cam going inside the walls, but at the same time mantaing this “perfect orbit” system? (for the record, I’d throw away my cam’s beautiful code if necessary) In other words, i would like that the orbit’s arc that is inside the wall, becomes a straight line stuck to the wall’s collider. Any tips for a better approach are welcome.

    The code

    Here is the script(in Javascript) I attached to the cam, for the sake of completeness:

    #pragma strict
    public var target : Transform;
    public var height: float = 1.0f;
    public var cam_distance: float = 4.0f;
    public var orbit_speed: float = 40.0f;
    
    private var trans: Transform;
    
    function Start () {
        trans = GetComponent.<Transform>();
    }
    
    function Update () {
        //Initial positioning: later we change it depending on cam's angle
        trans.position.x = target.position.x;
        trans.position.y = target.position.y + height;
        trans.position.z = target.position.z - cam_distance;
    
        //Rotate the cam with A and D keys
        if(Input.GetKey(KeyCode.A)) {
            trans.Rotate(Vector3.up, -orbit_speed * Time.deltaTime);
        }
        if(Input.GetKey(KeyCode.D)) {
            trans.Rotate(Vector3.up, orbit_speed * Time.deltaTime);
        }
        
        var base_angle : float;
        // First, we get the cam's angle and turn it 180º
        base_angle = (trans.rotation.eulerAngles.y + 180) % 360;
    
        // Using trigonometry to translate the cam to the corresponding orbit position depending on angle
        trans.position.x = cam_distance * Mathf.Sin(base_angle * Mathf.Deg2Rad) + target.position.x;
        trans.position.z = cam_distance * Mathf.Cos(base_angle * Mathf.Deg2Rad) + target.position.z;
    }
    

    Once you have computed the “DESIRED” position of the camera (your existing code), you can use a physics.RAYCAST from the center of rotation to the desired camera position (sounds like you did the opposite), to determine if a wall is in the way. If so, simply decrease your (cam_distance) distance to the center, to the value provided by the raycast result.
    (let me know if you need more details on that last part which can be done with vector subtraction, Vector magnitude and Vector scaling).

    (scroll down to see version with hit-point as an out parameter)

    Edit: Collider.Raycast is useful to detect collisions with a particular object, as opposed to the first collider hit by the ray (like Physics.Raycast).

    I implemented @Glurth 's approach by the following snippet, and it worked perfectly.

    I made this fragment of code, and appended it to the script that I posted in the question:

    function LateUpdate() {
        // Correct radius if collider is in the way
        var direction : Vector3 = trans.position - target.position;
        direction.Normalize();
    
        var radius : float = cam_distance;
        var target_to_camera : Ray = new Ray(target.position, direction);
        var hit : RaycastHit;
    
        if (Physics.Raycast(target.position, direction, hit, cam_distance)) {
            // the sphere radius is 0.5f <- actually, this wasn't relevant at all
            // but you might need to tweak distance to avoid camera seeing the wall from behind
            radius = hit.distance - 0.5f;
            trans.position.x = radius * Mathf.Sin(base_angle * Mathf.Deg2Rad) + target.position.x;
            trans.position.z = radius * Mathf.Cos(base_angle * Mathf.Deg2Rad) + target.position.z;
        }
    }