Creating a camera with a Rigidbody and Collider

I’m trying to create a camera that moves according to physics, such that it can collide with other objects and slide off of them in the case of a collision. This is similar to how the camera works in Totally Accurate Battle Simulator, where they have a completely smooth camera than can bounce off of objects or slide along them (such as a large wall).

I’ve tried implementing this with the built-in Rigidbody and the AddForce method in FixedUpdate. This mostly works, but it can cause some jitter since FixedUpdate doesn’t run at the same frame rate as the game.

If I move these methods into LateUpdate, then, as expected, the camera collider can end up going through walls, which is also no good.

Is there another method of using Unity’s physics to get this working nicely with the camera? Or would the only route be to implement this from scratch, without using a Rigidbody? If so, are there any suggestions on how to do this? I can use a SphereCast to prevent the camera from moving if it will hit another collider, but how would the slide and bounce physics be implemented?

Are you using “Interpolate” (Rigidbody)?

There’s plenty of discussion on the issues and an alternative suggestion (last post) here Coding practice

AddForce should definitely be applied in FixedUpdate, have you tried using interpolate on the rigidbody you are aiming the camera at? [Sorry @lightbug14 I did not see your reply in time]

If you just want your camera to move so as to not get clipped by gameobjects, there is a simple example towards the end of this Orbit Camera to achieve this.

Hopefully something there helps.

Thanks for the replies. I have tried using “Interpolate” on the Rigidbody, but this produces terrible jitter when the camera moves and rotates at the same time. Setting it to “None” produces the best result for my setup. I both move and rotate the camera using the Rigidbody APIs, applied in FixedUpdate.

The jitter I currently have is not too noticeable. It is only evident when the camera moves in a straight line. It looks like a very minor stutter in the image. This is not due to frame drops, as a solid 60 FPS is maintained the entire time. I also confirmed this by switching to the more normal “LateUpdate” method and simply update the transform’s position. This produces completely smooth movement of the camera.

The issue is only evident when I try to move the camera in FixedUpdate via the Rigidbody (AddForce). To clarify my original post, the camera itself is the rigidbody. It’s a free camera, so it does not track any objects.

Post the code you are using.
Any moving object in FixedUpdate + interpolation enabled should move really smoothly. This is possible when you use velocity, MovePosition/Rotation, forces, torque, etc. If for some reason you are changing the position directly (rb.position) that would be bad, interpolation doesn’t work with that.
Here is a basic example:

void FixedUpdate()
    {
        rb.AddForce( Vector3.forward * 50f * Time.deltaTime );
    }
}

Enable/disable interpolation and you will see a very noticeable difference.
i’m using a fixed time step of 0.1, super high!.
Also, the gif is very low quality, it is much more smooth in the editor.
Interpolated:

Non-interpolated:

Thanks for the gifs. You’re right about the interpolation, it does make my movement more smooth with it turned it. The issue with turning it on is that when I rotate the camera and move it at the same time, it causes terrible jitter. If interpolation is not on, the jitter is not there.

This is the code I’m using for both movement and rotation:

void Awake() {
    targetRotation = transform.rotation;
}

void FixedUpdate() {
    // Movement
    Vector3 moveX = Input.GetAxis("Move X") * transform.right;
    Vector3 moveZ = Input.GetAxis("Move Z") * transform.forward;
    rb.AddForce((moveX + moveZ).normalized * 5000f * Time.deltaTime);

    // Rotation
    Vector3 newEuler = targetRotation.eulerAngles;
    newEuler.y += Input.GetAxis("Mouse X");
    newEuler.x += Input.GetAxis("Mouse Y");
    targetRotation.eulerAngles = newEuler;

    float delta = Quaternion.Angle(transform.rotation, targetRotation);
    if (delta == 0) {
        return;
    }

    float t = Mathf.SmoothDampAngle(delta, 0, ref rotateVelocity, 0.1f, float.MaxValue, Time.fixedDeltaTime);
    t = 1.0f - t / delta;
    Vector3 newRotation = Quaternion.Slerp(transform.rotation, targetRotation, t).eulerAngles;
    newRotation.z = 0;
    rb.MoveRotation(Quaternion.Euler(newRotation));
    }

Bro , im having a same issue, if u please would help me out …actually i have a free flying camera , but it doesnt get collided with any gameobject…how can i make it collide with everything in my game? here is the code below, Big Thanks in advance…please try to help me out, im extremely in need of guideline.

using UnityEngine;
using System.Collections;

///


/// Controls:
/// WASD : Directional movement
/// Shift/Control : Hold to increase/decrease speed
/// Q/E : Moves camera up/down on the Y-axis
/// Right Click : Zooms in
/// End : Toggle cursor locking to screen
///

[RequireComponent(typeof(Camera))]
public class iCECamera : MonoBehaviour
{
[Range(1.0f, 360.0f)]
public float cameraSensitivity = 90;

[Range(1.0f, 250.0f)]
public float climbSpeed = 4;

[Range(1.0f, 250.0f)]
public float moveSpeed = 10;

[Range(2.0f, 20.0f)]
public float rotateSpeed = 5.0f;

[Range(0.0f, 20.0f)]
public float zoomSpeed = 2.0f;

[Range(10.0f, 100.0f)]
public float zoomInFov = 45.0f;

[Range(0.0f, 45.0f)]
public float tiltAmount = 5.0f;

private float slowMoveFactor = 0.25f;
private float fastMoveFactor = 3;

private float rotationX;
private float rotationY;
private float rotationZ;

private float initialFov = 60.0f;
private float fovDampen;
private Camera currentCamera;

///


/// Used to setup any methods or fields before start is called
///

private void Awake ()
{
// Fix for initial rotation being reset to 0
rotationX = transform.rotation.eulerAngles.y;
}

///


/// Initializing fields for camera fly script
///

private void Start ()
{
// Start with the cursor locked to the screen
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;

currentCamera = GetComponent();
initialFov = currentCamera.fieldOfView;
}

///


/// Handles the FOV zoom based on mouse input
///

private void HandleZoom ()
{
bool isHoldingRightMouse = Input.GetMouseButton(1);

if (currentCamera)
{
if (!isHoldingRightMouse)
{
currentCamera.fieldOfView = Mathf.SmoothDamp(currentCamera.fieldOfView, initialFov, ref fovDampen, 1 / zoomSpeed);
}

else if (isHoldingRightMouse)
{
currentCamera.fieldOfView = Mathf.SmoothDamp(currentCamera.fieldOfView, zoomInFov, ref fovDampen, 1 / zoomSpeed);
}
}
}

///


/// Handles the cursor visibility/lock state
///

private void HandleCursor ()
{
bool isCursorLocked = Cursor.lockState == CursorLockMode.Locked;

// Always hide the cursor if it’s position is locked
if (isCursorLocked)
{
Cursor.visible = false;
}

// Toggle the current state if we press ESC
if (Input.GetKeyDown(KeyCode.Escape))
{
Cursor.lockState = isCursorLocked
? CursorLockMode.None
: CursorLockMode.Locked;
}
}

///


/// Handles the rotation based on mouse input and sensitivity
///

private void HandleRotation ()
{
float canRotate = (Cursor.lockState == CursorLockMode.Locked) ? 1 : 0;

rotationX += Input.GetAxis(“Mouse X”) * cameraSensitivity * canRotate * Time.deltaTime;
rotationY += Input.GetAxis(“Mouse Y”) * cameraSensitivity * canRotate * Time.deltaTime;
rotationZ = -Input.GetAxis(“Mouse X”) * tiltAmount * canRotate;

// Clamp y-axis rotation as we don’t want to flip the camera upside down
rotationY = Mathf.Clamp(rotationY, -90, 90);

Quaternion targetRotation = Quaternion.Euler(-rotationY, rotationX, rotationZ);

transform.localRotation = Quaternion.Slerp(transform.localRotation, targetRotation, rotateSpeed * Time.deltaTime);
}

///


/// Handles the position changes based on input
///

private void HandleMovement ()
{
if (Input.GetKey (KeyCode.LeftShift) || Input.GetKey (KeyCode.RightShift))
{
transform.position += transform.forward * (moveSpeed * fastMoveFactor) * Input.GetAxis(“Vertical”) * Time.deltaTime;
transform.position += transform.right * (moveSpeed * fastMoveFactor) * Input.GetAxis(“Horizontal”) * Time.deltaTime;
}
else if (Input.GetKey (KeyCode.LeftControl) || Input.GetKey (KeyCode.RightControl))
{
transform.position += transform.forward * (moveSpeed * slowMoveFactor) * Input.GetAxis(“Vertical”) * Time.deltaTime;
transform.position += transform.right * (moveSpeed * slowMoveFactor) * Input.GetAxis(“Horizontal”) * Time.deltaTime;
}
else
{
transform.position += transform.forward * moveSpeed * Input.GetAxis(“Vertical”) * Time.deltaTime;
transform.position += transform.right * moveSpeed * Input.GetAxis(“Horizontal”) * Time.deltaTime;
}
}

///


/// Handles the increase and decrease in elevation based on input
///

private void HandleElevation ()
{
if (Input.GetKey (KeyCode.Q)) transform.position += transform.up * climbSpeed * Time.deltaTime;
if (Input.GetKey (KeyCode.E)) transform.position -= transform.up * climbSpeed * Time.deltaTime;
}

private void Update ()
{
HandleCursor();

HandleZoom();

HandleRotation();

HandleMovement();

HandleElevation();
}
}