public override void Update(Transform self, Transform target, Vector3 targetOffset, float deltaTime)
    {
        if (target == null)
        {
            return;
        }

        m_orbitX += GetInputForAxis(horizontalAxis) * horizontalSpeed;
        m_orbitY -= GetInputForAxis(verticalAxis) * verticalSpeed;
        distance -= GetInputForAxis(distanceAxis) * distanceSpeed;

        m_orbitY = Mathf.Clamp(m_orbitY, minVerticalAngle, maxVerticalAngle);
        distance = Mathf.Clamp(distance, minDistance, maxDistance);

        m_orbitDistance = Mathf.Lerp(m_orbitDistance, distance, distanceDamping * deltaTime);

        self.rotation = Quaternion.Slerp(self.rotation, Quaternion.Euler(m_orbitY, m_orbitX, 0), orbitDamping * deltaTime);

        if (m_vehicle != null && controller.cameraCollisions)
        {
            Vector3 origin    = target.position + targetOffset;
            Vector3 direction = self.rotation * -Vector3.forward;

            // If a camera is present then perform a sphere cast. Otherwise do a raycast.

            if (m_camera != null)
            {
                float radius      = m_camera.nearClipPlane * Mathf.Tan(m_camera.fieldOfView * Mathf.Deg2Rad * 0.5f) + 0.05f;
                float rayDistance = m_orbitDistance - m_camera.nearClipPlane;

                self.position = origin + direction * m_vehicle.SphereRaycastOthers(origin, direction, radius, rayDistance, controller.collisionMask);
            }
            else
            {
                self.position = m_vehicle.RaycastOthers(origin, origin + direction * m_orbitDistance, controller.collisionMask);
            }
        }
        else
        {
            self.position = target.position + targetOffset + self.rotation * new Vector3(0.0f, 0.0f, -m_orbitDistance);
        }
    }
    public override void Update(Transform self, Transform target, Vector3 targetOffset, float deltaTime)
    {
        if (target == null)
        {
            return;
        }

        Vector3 updatedVelocity = (target.position + targetOffset - m_smoothLastPos) / deltaTime;

        if (lookBehind)
        {
            updatedVelocity = -updatedVelocity;
        }
        m_smoothLastPos = target.position + targetOffset;

        updatedVelocity.y = 0.0f;

        if (updatedVelocity.magnitude > 1.0f)
        {
            m_smoothVelocity    = Vector3.Lerp(m_smoothVelocity, updatedVelocity, velocityDamping * deltaTime);
            m_smoothTargetAngle = Mathf.Atan2(m_smoothVelocity.x, m_smoothVelocity.z) * Mathf.Rad2Deg;
        }

        if (!followVelocity)
        {
            m_smoothTargetAngle = target.eulerAngles.y;
        }

        float wantedHeight = target.position.y + targetOffset.y + height;

        m_selfRotationAngle = Mathf.LerpAngle(m_selfRotationAngle, m_smoothTargetAngle, rotationDamping * deltaTime);
        m_selfHeight        = Mathf.Lerp(m_selfHeight, wantedHeight, heightDamping * deltaTime);
        Quaternion currentRotation = Quaternion.Euler(0, m_selfRotationAngle, 0);

        Vector3 selfPos = target.position + targetOffset;

        selfPos  -= currentRotation * Vector3.forward * distance;
        selfPos.y = m_selfHeight;

        Vector3 lookAtTarget = target.position + targetOffset + Vector3.up * height * viewHeightRatio;

        if (m_vehicle != null && controller.cameraCollisions)
        {
            if (m_camera != null)
            {
                Vector3 origin      = lookAtTarget;
                Vector3 path        = selfPos - lookAtTarget;
                Vector3 direction   = path.normalized;
                float   rayDistance = path.magnitude - m_camera.nearClipPlane;
                float   radius      = m_camera.nearClipPlane * Mathf.Tan(m_camera.fieldOfView * Mathf.Deg2Rad * 0.5f) + 0.1f;

                selfPos = origin + direction * m_vehicle.SphereRaycastOthers(origin, direction, radius, rayDistance, controller.collisionMask);
            }
            else
            {
                selfPos = m_vehicle.RaycastOthers(lookAtTarget, selfPos, controller.collisionMask);
            }
        }

        self.position = selfPos;
        self.LookAt(lookAtTarget);
    }