a literal 'edge case' for SphereCast

I have been working out a physics based character controller and am about 90% there; however, for the past day I’ve been trying to solve this one particular issue with my follow camera in a literal ‘edge case’.

When the SphereCast hit returns a contact normal that is near perpendicular to its’ cast direction, I get an odd flickering movement of my camera position. It seems to be happening at an angle between 80 to 90 degrees (been a tad difficult to nail it down), so I believe the SphereCast method I am using needs to have some way to pre-evaluate the contact normal and if it is near perpendicular… perhaps smooth the transform position in either a positive or negative direction. (I hope that makes sense).

I’ve illustrated the issue here and a snapshot of an orthographic view of my test case which shows the debug.draw lines of the contact normals against the surface.

here’'s some source I’ve been mucking with:

    void FixedUpdate()
    {
        targetRotation = LookAtTarget.rotation;
        Quaternion currentRotation = Quaternion.Lerp(previousRotation, targetRotation, _damp * Time.deltaTime);
        previousRotation = currentRotation;

        reverseLookat = transform.position - LookAtTarget.position; //gets vector from control to camera
        distance = reverseLookat.magnitude;  //get distant
        direction = reverseLookat / distance;  //normalized direction

        transform.position = LookAtTarget.position;  //begin calc of camera position based on "follow" target

        CameraContact = Physics.SphereCast(LookAtTarget.position, _safetyZone, direction, out CameraHit, deltaDistance, layerMask); //cast 'thick' ray between control and camera to check for possible collisions
        if (!CameraContact)
        {   //do normal camera rotation and maintain distance
            transform.position -= currentRotation * Vector3.right * deltaDistance;
        }
        else
        {   //a collision will occur adjust camera 
            transform.position -= currentRotation * Vector3.right * CameraHit.distance; //shorten 'deltadistance' of camera to follow target
            Debug.DrawRay(CameraHit.point, CameraHit.normal, Color.red, 500);  //draw contact normal on hit surface
            Debug.Log("angle: " + Vector3.Angle(LookAtTarget.position - transform.position, CameraHit.normal));  // what is the angle between contact normal and direction vector
        }
        transform.position += currentRotation * Vector3.up * deltaHeight;  //keep camera slightly above target for 3rd person view
        transform.LookAt(LookAtTarget, LookAtTarget.TransformDirection(Vector3.up));  //maintain verticle position aligned with lookat target using worldup 'hint'
    }

As can be seen in the screen shot, the red debug.draw lines stop at an angle that would be perpendicular to the direction vector of which the SphereCast lays out. It is within a few degrees of this contact angle, that the camera jumps between its’ prefered deltaDistance, and the adjusted distance (the distance between the contact and origin) causing an odd jumping/flickering.

I’ve attempted to Lerp between the distances by pre-evaluating the transforms, but I could not get it working. So I thought I would ask if someone who has seen or delt with this, may have some suggestions on the best way to adjust the camera in this edge case?

1 Like

incase anyone is curious, i’ve managed to mitigate this somewhat by lerping the distance values with Mathf on collisions… still causes some jittering when the camera approaches near perpendicular contact point, but the “jump” is far smaller… plus it adds a nice easing to the camera instead of just snapping.

I think I could totally eliminate the edge case on this by altering the height and lerp a small distance away from the point of contact and construct a final “resting” position if it is near the edge… but i’m not so sure yet… perhaps when I have nothing else to do.

1 Like

You should put your camera movement in lateupdate.

I assume this whole thing is designed to zoom your camera in if it gets behind an object? Why not just use line cast if thats the case.

I also personally dont see any need for the use of Time.deltaTime in camera movement for smoothing, since almost always, the thing that is being watched is already using delta to smooth its movement

I tried lateupdate, and it didn’t make any difference.

I used deltaTime here to add an easing effect to the camera. Visually it just gives it a feeling that the observer is disconnected from the object being controlled. Perhaps it’s just a perception thing on my part…

I found that linecast provides a detection that would result in a late collision solution, where as the spherecast provided a type of potential collision (the radius of the thick cast). This basically allows me to translate the camera to a position necessary to “avoid” the corner before the direction vector intersects that contact point.

It’s not for smoothing, it’s for framerate independence.