Search Unity

Creating a camera with a Rigidbody and Collider

Discussion in 'Physics' started by namcap, Jan 11, 2021.

  1. namcap

    namcap

    Joined:
    Jan 8, 2016
    Posts:
    49
    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?
     
  2. lightbug14

    lightbug14

    Joined:
    Feb 3, 2018
    Posts:
    447
    Are you using "Interpolate" (Rigidbody)?
     
  3. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    There's plenty of discussion on the issues and an alternative suggestion (last post) here https://forum.unity.com/threads/coding-practice.991934/

    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 https://catlikecoding.com/unity/tutorials/movement/orbit-camera/ to achieve this.

    Hopefully something there helps.
     
  4. namcap

    namcap

    Joined:
    Jan 8, 2016
    Posts:
    49
    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.
     
  5. lightbug14

    lightbug14

    Joined:
    Feb 3, 2018
    Posts:
    447
    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:
    Code (CSharp):
    1. void FixedUpdate()
    2.     {
    3.         rb.AddForce( Vector3.forward * 50f * Time.deltaTime );
    4.     }
    5. }
    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:
     
  6. namcap

    namcap

    Joined:
    Jan 8, 2016
    Posts:
    49
    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:

    Code (CSharp):
    1.  
    2. void Awake() {
    3.     targetRotation = transform.rotation;
    4. }
    5.  
    6. void FixedUpdate() {
    7.     // Movement
    8.     Vector3 moveX = Input.GetAxis("Move X") * transform.right;
    9.     Vector3 moveZ = Input.GetAxis("Move Z") * transform.forward;
    10.     rb.AddForce((moveX + moveZ).normalized * 5000f * Time.deltaTime);
    11.  
    12.     // Rotation
    13.     Vector3 newEuler = targetRotation.eulerAngles;
    14.     newEuler.y += Input.GetAxis("Mouse X");
    15.     newEuler.x += Input.GetAxis("Mouse Y");
    16.     targetRotation.eulerAngles = newEuler;
    17.  
    18.     float delta = Quaternion.Angle(transform.rotation, targetRotation);
    19.     if (delta == 0) {
    20.         return;
    21.     }
    22.  
    23.     float t = Mathf.SmoothDampAngle(delta, 0, ref rotateVelocity, 0.1f, float.MaxValue, Time.fixedDeltaTime);
    24.     t = 1.0f - t / delta;
    25.     Vector3 newRotation = Quaternion.Slerp(transform.rotation, targetRotation, t).eulerAngles;
    26.     newRotation.z = 0;
    27.     rb.MoveRotation(Quaternion.Euler(newRotation));
    28.     }
     
  7. jubaerjams8548

    jubaerjams8548

    Joined:
    Jun 8, 2020
    Posts:
    41
    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;

    /// <summary>
    /// 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
    /// </summary>
    [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;

    /// <summary>
    /// Used to setup any methods or fields before start is called
    /// </summary>
    private void Awake ()
    {
    // Fix for initial rotation being reset to 0
    rotationX = transform.rotation.eulerAngles.y;
    }

    /// <summary>
    /// Initializing fields for camera fly script
    /// </summary>
    private void Start ()
    {
    // Start with the cursor locked to the screen
    Cursor.visible = false;
    Cursor.lockState = CursorLockMode.Locked;

    currentCamera = GetComponent<Camera>();
    initialFov = currentCamera.fieldOfView;
    }

    /// <summary>
    /// Handles the FOV zoom based on mouse input
    /// </summary>
    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);
    }
    }
    }

    /// <summary>
    /// Handles the cursor visibility/lock state
    /// </summary>
    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;
    }
    }

    /// <summary>
    /// Handles the rotation based on mouse input and sensitivity
    /// </summary>
    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);
    }

    /// <summary>
    /// Handles the position changes based on input
    /// </summary>
    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;
    }
    }

    /// <summary>
    /// Handles the increase and decrease in elevation based on input
    /// </summary>
    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();
    }
    }