Search Unity

Creating a POV camera

Discussion in 'Cinemachine' started by Gregoryl, Nov 20, 2017.

  1. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    Hi Isaac,

    Thanks for the kind words. I'm replying publicly to your PM because I think the topic is of general interest.

    The FreeLook negative-radius hack from earlier versions is now outdated. We're working on a POV rig, but until it's ready there are a couple of ways to create POV cameras with Cinemachine.

    1. Create a simple vcam, with Body set to HardLockToTarget on the character's head, and Aim set to POV, like this:

    upload_2017-11-20_10-56-12.png


    2. Or, if you want to use the standard FirstPersonCharacterController, you can do it like this:
    • Drag the FPSController or the RigibBodyFPSController prefab into your scene.
    • The prefab includes an embedded Camera, meant to replace the Main Camera. You need to keep your original Main Camera, and replace the embedded Camera with a Cinemachine Virtual Camera. To do this, you disable (not delete) the embedded Camera component, and add a Cinemachine Virtual Camera component in its place, like this:

    upload_2017-11-20_11-6-3.png
    • Make sure that your actual Main Camera has a CinemachineBrain on it (this will happen automatically if you've used the Cinemachine menu to create a Virtual Camera). You can add the Brain component onto the main camera if you don't have it there already.
    • In the image above, I've untagged the embedded MainCamera. It would probably be a good idea to rename it as well. Two objects called MainCamera can be confusing.
    You now have a vcam that is controlled by the FirstPersonController. It will play nicely with the other vcams in your scene (e.g. blending, timeline, post-processing, etc).
     
    Last edited: Nov 20, 2017
    protopop, KosmoDED and C10110 like this.
  2. C10110

    C10110

    Joined:
    Jan 19, 2013
    Posts:
    58
    Thanks, Gregory! This was an epic way to start the week! Thank you for taking the time to explain this. I know other users will be just as happy once they find this workaround. Can't wait to get started!!
     
  3. alexandre-fiset

    alexandre-fiset

    Joined:
    Mar 19, 2012
    Posts:
    715
    ETA? :D
     
  4. KosmoDED

    KosmoDED

    Joined:
    May 22, 2017
    Posts:
    8

    I just found that this way wouldn’t work correctly for FPS Controller (camera will move left and right only).
     
  5. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    @KosmoDED It works, you have probably not set it up correctly. Can you post images of your vcam inspector?
     
  6. KosmoDED

    KosmoDED

    Joined:
    May 22, 2017
    Posts:
    8
    upload_2018-1-21_12-41-50.png

    Camera moving only left and right.
    As i said, it happens only with FPS Controller ( Rigidbody FPS Controller working well).
     
  7. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    Looks like you're using an old version of CM. Upgrade to the latest, and follow the instructions exactly - e.g. "Do Nothing" in Aim and Body, not "Hard Constraint".
     
  8. KosmoDED

    KosmoDED

    Joined:
    May 22, 2017
    Posts:
    8
    Thank you for mentioning that I use previous version of CM. I updated it.

    Tried again. Unfortunately, result is the same:
    • RigidBodyFPSController working well with cinemachine, timeline etc.
    • FPSController working too, but camera move only horizontally. I installed latest beta of Unity and latest Standard Character Asset.


    I suspect that problem is in FPS controller script.
     
  9. KosmoDED

    KosmoDED

    Joined:
    May 22, 2017
    Posts:
    8
    I nailed it!

    FirstPersonController: (you can make changes in original script but i created new one. Unfortunately i need to set all properties ( Walk speed, Run speed etc.) manually in this case.

    • I made m_Camera public.
    • In Start() function disabled one line.
    • Aded Camera from FPS controller manualy.

    P.S. Now i can use FPS controller and its camera as cinemachine Virtual Camera camera 100 %.


    Screenshot_1.png Screenshot_2.png Screenshot_3.png
     
    Gregoryl likes this.
  10. cfree

    cfree

    Joined:
    Sep 30, 2014
    Posts:
    72
    Hi Gregoryl!
    Is this still the best way to configure a First Person Camera with Cinemachine? Or there were / there will be some advance on that subject on the latest releases?
    Thanks a lot!
     
  11. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    @cfree The best way is still to take a working FPS camera controller and replace the Camera component with a "Do Nothing" vcam. That opens up the FPS controller to all the benefits of Cinemachine: Timeline, blending, Collider, Noise, etc.
     
    corb555 and joelsinbarba like this.
  12. discofhc

    discofhc

    Joined:
    Dec 18, 2017
    Posts:
    47
    What about the "Cinemachine FPS / POV Camera" (Unity 2019.2) mentioned at Unite LA Roadmap talk?
     
    crawfis likes this.
  13. oregilron

    oregilron

    Joined:
    Nov 11, 2017
    Posts:
    2
    I'm going through this .. and still don't see how can i use cinemachine in a 1st person game,
    if I make a vCM that follows the player with POV aim then follow is fine but looking around won't make the gun move with the camera.
    and if I make vCM that is only POV aim and i child the gun in the main camera then in case of cutscenes the gun will fly with the camera to.. basically anywhere...
    isn't there a normal solution?
     
    RythmosRnD likes this.
  14. cfree

    cfree

    Joined:
    Sep 30, 2014
    Posts:
    72
    Very excited to see it on official Cinemachine roadmap.
    Cinemachine is so awesome, i am looking forward to see its usage on first person scenarios.

    @Adam_Myhill
    @Gregoryl
    Please, any news on that? Is it really being worked?

    Thanks!
     
  15. Insanebolt315

    Insanebolt315

    Joined:
    Jan 9, 2019
    Posts:
    1
    Thank you @Gregoryl for the guidance on how to configure cinemachine for FPS POV. This help tremendously!
     
  16. kingofbananas

    kingofbananas

    Joined:
    Oct 1, 2015
    Posts:
    1
    Yes thank you @Gregoryl, I was struggling a bit to figure this out on my own! Any way to make this part of an official tutorial of upgrading the FirstPersonController to use Cinemachine?
     
  17. toomasio

    toomasio

    Joined:
    Nov 19, 2013
    Posts:
    198
    I have a suggestion. It would be great if you could ignore or lock rotations in the AIM | POV settings. I just need a simple lock aim Y rotation to the follow target...but keep the vertical and X rotations the same as the POV settings.

    When I try to implement my own solution on top of cinemachine and choose "Do Nothing" for all options, the camera wont let me change its position or rotation.... it just locks in place. Tried it in Update, FixedUpdate and LateUpdate.
     
  18. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    You can do that very simply with a custom CinemachineExtension that overrides the Y rotation at the end of the CM pipeline. Enable it when needed.
     
    toomasio likes this.
  19. toomasio

    toomasio

    Joined:
    Nov 19, 2013
    Posts:
    198
    For those Interested...you can change the Raw orientation and position on the States in extension classes...this occurs after the vcam script does its calculations. Thanks @Gregoryl

    Code (CSharp):
    1. using UnityEngine;
    2. using Cinemachine;
    3.  
    4. public class CinemachineFPOVExtension : CinemachineExtension
    5. {
    6.  
    7.     protected override void Awake()
    8.     {
    9.         base.Awake();
    10.         ConnectToVcam(true);
    11.     }
    12.  
    13.     protected override void PostPipelineStageCallback(CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    14.     {
    15.         if (vcam.Follow)
    16.         {
    17.             if (stage == CinemachineCore.Stage.Aim)
    18.             {
    19.                 var followRot = vcam.Follow.rotation.eulerAngles;
    20.                 var camRot = state.RawOrientation.eulerAngles;
    21.                 state.RawOrientation = Quaternion.Euler(new Vector3(camRot.x, followRot.y, camRot.z));
    22.             }
    23.         }
    24.        
    25.     }
    26. }
     
    samyam_, bmcapereira and Gregoryl like this.
  20. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    Very nice. However, you don't need Awake(). That's taken care of automatically by the base class.
     
    Last edited: Sep 23, 2019
    toomasio likes this.
  21. thatscraigz

    thatscraigz

    Joined:
    Mar 27, 2015
    Posts:
    100
    Hiya @KosmoDED I'm running into this same issue :/

    I'm using the Rigid Body First Person Controller instead (seems you were using the other one?) and can't seem to get this to work. Any insights you could maybe give looking at it?

    I'd really appreciate it!
    -craigz

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityStandardAssets.CrossPlatformInput;
    4.  
    5. namespace UnityStandardAssets.Characters.FirstPerson
    6. {
    7.     [RequireComponent(typeof (Rigidbody))]
    8.     [RequireComponent(typeof (CapsuleCollider))]
    9.     public class RigidbodyFirstPersonController : MonoBehaviour
    10.     {
    11.         [Serializable]
    12.         public class MovementSettings
    13.         {
    14.             public float ForwardSpeed = 8.0f;   // Speed when walking forward
    15.             public float BackwardSpeed = 4.0f;  // Speed when walking backwards
    16.             public float StrafeSpeed = 4.0f;    // Speed when walking sideways
    17.             public float RunMultiplier = 2.0f;   // Speed when sprinting
    18.             public KeyCode RunKey = KeyCode.LeftShift;
    19.             public float JumpForce = 30f;
    20.             public AnimationCurve SlopeCurveModifier = new AnimationCurve(new Keyframe(-90.0f, 1.0f), new Keyframe(0.0f, 1.0f), new Keyframe(90.0f, 0.0f));
    21.             [HideInInspector] public float CurrentTargetSpeed = 8f;
    22.  
    23. #if !MOBILE_INPUT
    24.             private bool m_Running;
    25. #endif
    26.  
    27.             public void UpdateDesiredTargetSpeed(Vector2 input)
    28.             {
    29.                 if (input == Vector2.zero) return;
    30.                 if (input.x > 0 || input.x < 0)
    31.                 {
    32.                     //strafe
    33.                     CurrentTargetSpeed = StrafeSpeed;
    34.                 }
    35.                 if (input.y < 0)
    36.                 {
    37.                     //backwards
    38.                     CurrentTargetSpeed = BackwardSpeed;
    39.                 }
    40.                 if (input.y > 0)
    41.                 {
    42.                     //forwards
    43.                     //handled last as if strafing and moving forward at the same time forwards speed should take precedence
    44.                     CurrentTargetSpeed = ForwardSpeed;
    45.                 }
    46. #if !MOBILE_INPUT
    47.                 if (Input.GetKey(RunKey))
    48.                 {
    49.                     CurrentTargetSpeed *= RunMultiplier;
    50.                     m_Running = true;
    51.                 }
    52.                 else
    53.                 {
    54.                     m_Running = false;
    55.                 }
    56. #endif
    57.             }
    58.  
    59. #if !MOBILE_INPUT
    60.             public bool Running
    61.             {
    62.                 get { return m_Running; }
    63.             }
    64. #endif
    65.         }
    66.  
    67.  
    68.         [Serializable]
    69.         public class AdvancedSettings
    70.         {
    71.             public float groundCheckDistance = 0.01f; // distance for checking if the controller is grounded ( 0.01f seems to work best for this )
    72.             public float stickToGroundHelperDistance = 0.5f; // stops the character
    73.             public float slowDownRate = 20f; // rate at which the controller comes to a stop when there is no input
    74.             public bool airControl; // can the user control the direction that is being moved in the air
    75.             [Tooltip("set it to 0.1 or more if you get stuck in wall")]
    76.             public float shellOffset; //reduce the radius by that ratio to avoid getting stuck in wall (a value of 0.1f is nice)
    77.         }
    78.  
    79.  
    80.         public Camera cam;
    81.         public MovementSettings movementSettings = new MovementSettings();
    82.         public MouseLook mouseLook = new MouseLook();
    83.         public AdvancedSettings advancedSettings = new AdvancedSettings();
    84.  
    85.  
    86.         private Rigidbody m_RigidBody;
    87.         private CapsuleCollider m_Capsule;
    88.         private float m_YRotation;
    89.         private Vector3 m_GroundContactNormal;
    90.         private bool m_Jump, m_PreviouslyGrounded, m_Jumping, m_IsGrounded;
    91.  
    92.  
    93.         public Vector3 Velocity
    94.         {
    95.             get { return m_RigidBody.velocity; }
    96.         }
    97.  
    98.         public bool Grounded
    99.         {
    100.             get { return m_IsGrounded; }
    101.         }
    102.  
    103.         public bool Jumping
    104.         {
    105.             get { return m_Jumping; }
    106.         }
    107.  
    108.         public bool Running
    109.         {
    110.             get
    111.             {
    112. #if !MOBILE_INPUT
    113.                 return movementSettings.Running;
    114. #else
    115.                 return false;
    116. #endif
    117.             }
    118.         }
    119.  
    120.  
    121.         private void Start()
    122.         {
    123.             m_RigidBody = GetComponent<Rigidbody>();
    124.             m_Capsule = GetComponent<CapsuleCollider>();
    125.             mouseLook.Init (transform, cam.transform);
    126.         }
    127.  
    128.  
    129.         private void Update()
    130.         {
    131.             RotateView();
    132.  
    133.             if (CrossPlatformInputManager.GetButtonDown("Jump") && !m_Jump)
    134.             {
    135.                 m_Jump = true;
    136.             }
    137.         }
    138.  
    139.  
    140.         private void FixedUpdate()
    141.         {
    142.             GroundCheck();
    143.             Vector2 input = GetInput();
    144.  
    145.             if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) && (advancedSettings.airControl || m_IsGrounded))
    146.             {
    147.                 // always move along the camera forward as it is the direction that it being aimed at
    148.                 Vector3 desiredMove = cam.transform.forward*input.y + cam.transform.right*input.x;
    149.                 desiredMove = Vector3.ProjectOnPlane(desiredMove, m_GroundContactNormal).normalized;
    150.  
    151.                 desiredMove.x = desiredMove.x*movementSettings.CurrentTargetSpeed;
    152.                 desiredMove.z = desiredMove.z*movementSettings.CurrentTargetSpeed;
    153.                 desiredMove.y = desiredMove.y*movementSettings.CurrentTargetSpeed;
    154.                 if (m_RigidBody.velocity.sqrMagnitude <
    155.                     (movementSettings.CurrentTargetSpeed*movementSettings.CurrentTargetSpeed))
    156.                 {
    157.                     m_RigidBody.AddForce(desiredMove*SlopeMultiplier(), ForceMode.Impulse);
    158.                 }
    159.             }
    160.  
    161.             if (m_IsGrounded)
    162.             {
    163.                 m_RigidBody.drag = 5f;
    164.  
    165.                 if (m_Jump)
    166.                 {
    167.                     m_RigidBody.drag = 0f;
    168.                     m_RigidBody.velocity = new Vector3(m_RigidBody.velocity.x, 0f, m_RigidBody.velocity.z);
    169.                     m_RigidBody.AddForce(new Vector3(0f, movementSettings.JumpForce, 0f), ForceMode.Impulse);
    170.                     m_Jumping = true;
    171.                 }
    172.  
    173.                 if (!m_Jumping && Mathf.Abs(input.x) < float.Epsilon && Mathf.Abs(input.y) < float.Epsilon && m_RigidBody.velocity.magnitude < 1f)
    174.                 {
    175.                     m_RigidBody.Sleep();
    176.                 }
    177.             }
    178.             else
    179.             {
    180.                 m_RigidBody.drag = 0f;
    181.                 if (m_PreviouslyGrounded && !m_Jumping)
    182.                 {
    183.                     StickToGroundHelper();
    184.                 }
    185.             }
    186.             m_Jump = false;
    187.         }
    188.  
    189.  
    190.         private float SlopeMultiplier()
    191.         {
    192.             float angle = Vector3.Angle(m_GroundContactNormal, Vector3.up);
    193.             return movementSettings.SlopeCurveModifier.Evaluate(angle);
    194.         }
    195.  
    196.  
    197.         private void StickToGroundHelper()
    198.         {
    199.             RaycastHit hitInfo;
    200.             if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
    201.                                    ((m_Capsule.height/2f) - m_Capsule.radius) +
    202.                                    advancedSettings.stickToGroundHelperDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
    203.             {
    204.                 if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f)
    205.                 {
    206.                     m_RigidBody.velocity = Vector3.ProjectOnPlane(m_RigidBody.velocity, hitInfo.normal);
    207.                 }
    208.             }
    209.         }
    210.  
    211.  
    212.         private Vector2 GetInput()
    213.         {
    214.            
    215.             Vector2 input = new Vector2
    216.                 {
    217.                     x = CrossPlatformInputManager.GetAxis("Horizontal"),
    218.                     y = CrossPlatformInputManager.GetAxis("Vertical")
    219.                 };
    220.             movementSettings.UpdateDesiredTargetSpeed(input);
    221.             return input;
    222.         }
    223.  
    224.  
    225.         private void RotateView()
    226.         {
    227.             //avoids the mouse looking if the game is effectively paused
    228.             if (Mathf.Abs(Time.timeScale) < float.Epsilon) return;
    229.  
    230.             // get the rotation before it's changed
    231.             float oldYRotation = transform.eulerAngles.y;
    232.  
    233.             mouseLook.LookRotation (transform, cam.transform);
    234.  
    235.             if (m_IsGrounded || advancedSettings.airControl)
    236.             {
    237.                 // Rotate the rigidbody velocity to match the new direction that the character is looking
    238.                 Quaternion velRotation = Quaternion.AngleAxis(transform.eulerAngles.y - oldYRotation, Vector3.up);
    239.                 m_RigidBody.velocity = velRotation*m_RigidBody.velocity;
    240.             }
    241.         }
    242.  
    243.         /// sphere cast down just beyond the bottom of the capsule to see if the capsule is colliding round the bottom
    244.         private void GroundCheck()
    245.         {
    246.             m_PreviouslyGrounded = m_IsGrounded;
    247.             RaycastHit hitInfo;
    248.             if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
    249.                                    ((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.groundCheckDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
    250.             {
    251.                 m_IsGrounded = true;
    252.                 m_GroundContactNormal = hitInfo.normal;
    253.             }
    254.             else
    255.             {
    256.                 m_IsGrounded = false;
    257.                 m_GroundContactNormal = Vector3.up;
    258.             }
    259.             if (!m_PreviouslyGrounded && m_IsGrounded && m_Jumping)
    260.             {
    261.                 m_Jumping = false;
    262.             }
    263.         }
    264.     }
    265. }
    266.  
     
  22. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,560
    Cinemachine is working so well in so many cases:) It works great for POV and I was able to set it up in minutes.

    I used a follow instead of hard track with damping set to zero so that I could offset the z a touch to simulate how your head leans over the front of your chest a bit when you look down. CinemachinePipeline even auto handled the transition between my two cams, and I like the proxy architecture of having the cameras be virtual cams that control the main camera isnread of being attached to it themselves. This and the UI system are two of my favourite Unity extensions because they have so many options when you come up with a new problem.

     
  23. bmcapereira

    bmcapereira

    Joined:
    Mar 18, 2020
    Posts:
    2
    How did you managed to prevent the camera from looking at the character's body and rotating weirdly? I started working with Cinemachine a couple of days ago and I'm kinda lost here..

    This is my current Virtual Camera settings. If you could explain what I'm doing wrong here it would be really helpful. Thanks in advance! :)

     

    Attached Files:

    protopop likes this.
  24. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    @bmcapereira looks like you're using an outdated version of CM. We are now up to 2.6.0-preview.2. What version are you using?
     
    Last edited: Mar 20, 2020
    bmcapereira likes this.
  25. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,560
    I just let it look at the body. I turn the body to face the camera direction so if I look down I just see my feet running and I can see my hands jogging in front of me. I hide the head which is a separate mesh so that I don’t see it from my point of view. If you position the camera right it’s just like looking at your own body. I also have an option to hide the body completely which is probably the simplest option.

     
    bmcapereira likes this.
  26. bmcapereira

    bmcapereira

    Joined:
    Mar 18, 2020
    Posts:
    2
    Thanks for the early reply.

    Ok, I've already upgraded to the lastest version.

    I want to limit how much the camera can rotate, so I can proper simulate the character's head. Should I use the Axis Value Range for that? Aren't those values relative to the mouse inicial position. I may be confused.
     
  27. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    Yes, the axis value range is for this. Values are in degrees, and 0 is straight ahead.
     
    bmcapereira likes this.
  28. holyfot

    holyfot

    Joined:
    Dec 16, 2016
    Posts:
    42
    Is VirtualCamera still the best FirstPerson camera? Or was there another made?
     
  29. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    It's still the best.