Search Unity

How to use Cinemachine FreeLook for a Third Person Shooter Game?

Discussion in 'Cinemachine' started by John_Leorid, Sep 4, 2019.

  1. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    651
    Well this should actually be pretty easy, I thought, a few days ago.

    Setting up Cinemachine Free Look to have a cool Third Person Shooter Camera .. well

    Setup:
    Pretty much standard Cinemachine Free Look
    with an Cinemachine Camera Offset (to avoid aiming at the players head but over his shoulder),
    Orbit-Binding-Mode set to "Lock To Target On Assign",
    Tracked Object Offset set to (0 , 1.4 , 0) on all three rigs as my character-origin is at the feet.

    What are the problems:
    1) Move on Mouse Input
    Well, it's a shooter, so I need pretty direct control over the camera rotation based on the player input - I tried everything on the Axis Control
    - setting the accel and decel Time to 0 = jerky movement
    - setting them to small numbers = jerky movement
    - setting them to higher valus = indirect control + deceleration movement after the physical mouse movement has stopped
    - setting only the decel Time to 0 = jerky movement
    - setting the mouse Input with the old InputSystem or setting it with the new InputSystem results in a somewhat direct control = jerky movement, it stops, then acclerates, stops, acclerates again.
    code:

    Code (CSharp):
    1. private void Awake()
    2.        {
    3.             CinemachineCore.GetInputAxis = GetAxisCustom2;
    4.        }
    5.        public float GetAxisCustom2(string axisName)
    6.        {
    7.            input_view = Mouse.current.delta.ReadValue() * sensitivity;
    8.            if (axisName == "Mouse X")
    9.            {
    10.                //return Input.GetAxis("Mouse X");
    11.                return input_view.x;
    12.            }
    13.            else if (axisName == "Mouse Y")
    14.            {
    15.                //return Input.GetAxis("Mouse Y");
    16.                return input_view.y;
    17.            }
    18.  
    19.            return 0;
    20.        }
    jerky movement == from complete jumps (as if the camera teleports in position or rotation) to unconsistent movement/rotation behavior - as if someone is driving the camera, like he's driving a car for the first time, hard breaks, putting in the reverse gear while moving forward, starting slow and then suddenly switching to full throttle and so on.


    2) Rotation of the Camera (without any Mouse-Input)
    No matter what settings I change, the camera always tries to look at the player-movement direction.
    Lookahead time is set to zero, Orbit-Binding-Mode set to "Lock To Target On Assign" - but when the player moves sidewards, the camera stays in place for a while, tries to look at the player, which is restriced by the Orbit-Binding-Mode, so it starts to rotate torwards the player and then look forward again in something like a sine-curve with high frequence. Every 0.3f seconds or so, it changes it's mind and looks either forward as it should or to the player.
    When I don't move the Mouse, the camera should not rotate, just move with the player.

    This makes aiming impossible.
    Now I don't want to give up on Cinemachine as it comes with some handy features and my own Camera-Monobehaviour jitters slightly and feels like the mouse-position is restrained to a grid (which probably happens because of setting the localEulerAngles or even the input itself).

    At this point you might guess my mouse is broken, but it works in every Ego-Shooter / ThirdPersonShooter I bought on Steam - just not in my own games.

    Is there a way to fix these problems? Am I missing something? I've read throught articles, forum posts, Unity Answers and watched almost every Youtube Tutorial on that topic.

    Maybe I should mention that I am not a beginner, I work with Unity since more than 6 years (mostly on FPS-Games).

    Example Video:
     

    Attached Files:

    Last edited: Sep 4, 2019
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    Hey, sorry for all the trouble you're having - you're off to a rocky start with CM, but it will get better. Let's look at your issues one by one.

    Input. By default, the way CM drives the FreeLook axes is designed for joystick input, and is acceleration-based. Often, this is not optimal for mouse input, and you are tripping over that. Fortunately, it's possible to change this behaviour quite easily. This post will show you how to do that: https://forum.unity.com/threads/cm-...-stuck-after-first-cycle.691306/#post-4631089

    Binding Mode. Why are you using LockToTargetOnAssign? That won't guarantee a specific spatial relationship to the target. Normally, it's better to use LockToTargetWithWorldUp. That will put the camera behind the player by default.

    Heading. Use TargetForward, not WorldSpace. That will make X axis.Value=0 match character forward, so recentering will bring the camera properly behind the player.

    Finally, camera movement is jittery in your video when the player moves and you are not moving the mouse. There can be several causes for this, but normally it has to do with uneven animation or inconsistent handling of update and fixed update. How is your player movement implemented? In FixedUpdate or Update? Are you using RigidBodies in the player? It's important that the vcam updates on the same clock as the target's transform. When the game is playing, the vcam's inspector will show you on what clock the vcam is updating. Look at it when the player is moving and when the player is still. What does it say?

    upload_2019-9-4_8-52-49.png
     
    matt1101 and andreiagmu like this.
  3. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    651
    I'll have a look at the thread about the mouse input, thanks for the link and the quick reply.

    I use LockToTargetOnAssign to prevent the camera from rotating torwards the player direction. My player rotates, but I want to keep the camera still. It should not follow the player orientation, how the player moves should not affect the camera rotation at all, it should just move with the player.
    To explain the behaviour in code it might be:

    Code (CSharp):
    1. camera.position = playerTransform.position + cameraOffset;
    For some reason the camera rotates when the player moves - the jittery movement is caused because the camera rotates when it should only move with the player.
    You can see how the camera rotates torwards the player-forward and then switches back to its previous rotation.
    All jitter-problems are caused because the camera rotates when the player moves.

    I only want the cam to rotate when the player moves the mouse.
    In code it might be: (camera parented to an _origin that copies the player-character position but ignores player-character rotation)

    Code (CSharp):
    1.  
    2.             float hAxis = Input.GetAxis("Mouse X");
    3.             float vAxis = -Input.GetAxis("Mouse Y");
    4.  
    5.             _origin.Rotate(Vector3.up, hAxis * _rotationSpeed.x, Space.World);
    6.  
    7.             rotationVertical += vAxis * _rotationSpeed.y;
    8.             rotationVertical = ClampAngle(rotationVertical, _rotationLimitVertical.x, _rotationLimitVertical.y);
    9.  
    10.             _origin.localEulerAngles = new Vector3(rotationVertical, _origin.localEulerAngles.y,  _origin.localEulerAngles.z);
    11.  
    Of course those two code snippets won't work together, I'd have to set the _originPosition in the first one, but I think code might be the best way to explain the behaviour I want to have.

    I could also make a video of the camera behaviour I want, as these code snippets are from my self-written ThirdPersonCamera (Script attached) - it just has no smoothing, no ImpulseListener, no blending between cameras and thats why I want to use Cinemachine now.
     

    Attached Files:

  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    I'm pretty sure that the script included there will give you the mouse behaviour you're looking for.

    You are using the wrong binding mode. To get what you want you should be using WorldSpace binding mode. That is completely independent of target rotation. All the LockToTarget family of binding modes apply the offset in TargetLocal space, which will rotate with the target. WorldSpace binding applies the offset in world space.
     
  5. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    651
    Well it does not change the camera behaviour at all, when I move my mouse, without any movement on the player, the camera accelerates and decellerates all the time while I move my mouse smoothly.

    Seems like my problem is in the Aiming of the camera.
    Without any aim, the camera looks straight forward and does not rotate to the player, so when I move my mouse, the cam goes around the player along the ring of the current rig - the player disaperars from the screen when the camera is in front of the player.
    The aiming composer causes the cam to look at the player and because of the soft zone, it tries to look at him smoothly and thats what causes my problems:
    The camera should not look at the real player position, because the camera movement is smoothed.
    So when the player moves, the camera does not move immediately - it follows the player after some time, which is OK, but this moves the aim position and causes the camera to rotate - because the vector from camera to player has changed.
    In my case I want to look at a virtual point, which is not the player, but the origin of the camera (origin of the rig-rings) at all times.

    I tried to archive that behaviour by setting the dead-zone of all aiming composers to 1, but now the rotation of the camera to the player changes during play - when the player is on the left side of the screen at the beginning, it somehow changes during play, the camera rotation is not fixed anymore, so the player could then be on the very far left or the very far right of the screen as in the dead zone, there are no corrections happening.

    What is the correct setup of the aiming composer to always look at the virtual position of the camera-origin? (like if the cam was parented to the origin and only the origin moves with the player and rotates according to MouseInput)

    Or is this not possible in cinemachine yet? (I tried all Aiming-Options, HardLookAt, SameAsFollowTarget, POV, Composer, none of them seems suitable for Third Person Shooters)
     
  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    Maybe we should back up a little, I think I haven't been properly understanding what you want. Let me describe back to you the camera behaviour I now think you want, and you tell me if it's correct, ok?

    You want the camera to always look at Point X (described below), with the mouse movement orbiting the camera around Point X in both horizontal and vertical directions. The camera rotation will never change unless the user moves the mouse. You want 1:1 tracking of the mouse, no damping or accel/decel of the camera when the mouse is moved.

    Point X is calculated dynamically in relation to the player character's position: it is always a little bit to the right of the character, in screen space. When the character moves, Point X moves with it and the camera follows in a fixed relation to it, without re-orienting unless the mouse is moved. Character rotation is irrelevant.

    You may optionally want some small amount of damping when Point X tracks the character's position.

    Is that right?
     
    Last edited: Sep 5, 2019
    saimun8596 likes this.
  7. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    651
    That's 100% right, yes. Exactly what I want.
     
    Gregoryl likes this.
  8. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    So, the FreeLook is designed by default for looking at and following a target. In your case, the target is Point X, not the Player. We need to build point X to use as a target for the FreeLook.

    Make an empty GameObject, and add to it this custom script (or something like it):
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayerFollowTarget : MonoBehaviour
    4. {
    5.     public Camera Camera;
    6.     public Transform Target;
    7.     public float Damping;
    8.     public Vector3 ScreenSpaceOffset;
    9.  
    10.     Vector3 m_CurrentVelocity;
    11.     Vector3 m_DampedPos;
    12.  
    13.     void OnEnable()
    14.     {
    15.         if (Camera == null)
    16.             Camera = Camera.main;
    17.         if (Target != null)
    18.             m_DampedPos = Target.position;
    19.     }
    20.  
    21.     void LateUpdate()
    22.     {
    23.         if (Target != null)
    24.         {
    25.             var pos = Target.position;
    26.             m_DampedPos = Damping < 0.01f
    27.                 ? pos : Vector3.SmoothDamp(m_DampedPos, pos, ref m_CurrentVelocity, Damping);
    28.             pos = m_DampedPos;
    29.             if (Camera != null)
    30.             {
    31.                 pos = Camera.transform.worldToLocalMatrix * pos;
    32.                 pos += ScreenSpaceOffset;
    33.                 pos = Camera.transform.localToWorldMatrix * pos;
    34.             }
    35.             transform.position = pos;
    36.         }
    37.     }
    38. }
    Set its Target field to be your player, and set the offset and the damping to be what you want. In my test, I set it up like this:
    upload_2019-9-6_10-9-38.png

    Next set that new GameObject to be the LookAt and Follow targets for your FreeLook.
    Set up the FreeLook like this, with all 3 rigs identical:

    upload_2019-9-6_10-14-26.png

    Note that the heading and binding mode are both World Space. Set the orbits how you like them.

    Finally, for smoother mouse input, add this script to the FreeLook to override the default input algorithm. Adjust the settings to tune the sensitivity the way you like it (but try the default settings first).
    Code (CSharp):
    1. using UnityEngine;
    2. using Cinemachine;
    3.  
    4. [RequireComponent(typeof(CinemachineFreeLook)), DisallowMultipleComponent]
    5. public class FreeLookAxisDriver : MonoBehaviour
    6. {
    7.     public CinemachineInputAxisDriver xAxis;
    8.     public CinemachineInputAxisDriver yAxis;
    9.  
    10.     private CinemachineFreeLook freeLook;
    11.  
    12.     private void Awake()
    13.     {
    14.         freeLook = GetComponent<CinemachineFreeLook>();
    15.         freeLook.m_XAxis.m_MaxSpeed = freeLook.m_XAxis.m_AccelTime = freeLook.m_XAxis.m_DecelTime = 0;
    16.         freeLook.m_XAxis.m_InputAxisName = string.Empty;
    17.         freeLook.m_YAxis.m_MaxSpeed = freeLook.m_YAxis.m_AccelTime = freeLook.m_YAxis.m_DecelTime = 0;
    18.         freeLook.m_YAxis.m_InputAxisName = string.Empty;
    19.     }
    20.  
    21.     private void OnValidate()
    22.     {
    23.         xAxis.Validate();
    24.         yAxis.Validate();
    25.     }
    26.  
    27.     private void Reset()
    28.     {
    29.         xAxis = new CinemachineInputAxisDriver
    30.         {
    31.             multiplier = -10f,
    32.             accelTime = 0.1f,
    33.             decelTime = 0.1f,
    34.             name = "Mouse X",
    35.         };
    36.         yAxis = new CinemachineInputAxisDriver
    37.         {
    38.             multiplier = 0.1f,
    39.             accelTime = 0.1f,
    40.             decelTime = 0.1f,
    41.             name = "Mouse Y",
    42.         };
    43.     }
    44.  
    45.     private void Update()
    46.     {
    47.         bool changed = yAxis.Update(Time.deltaTime, ref freeLook.m_YAxis);
    48.         changed |= xAxis.Update(Time.deltaTime, ref freeLook.m_XAxis);
    49.         if (changed)
    50.         {
    51.             freeLook.m_RecenterToTargetHeading.CancelRecentering();
    52.             freeLook.m_YAxisRecentering.CancelRecentering();
    53.         }
    54.     }
    55. }
    56.  
    Let me know if we're on the right track.
     
    Apsuity and andreiagmu like this.
  9. nimbii82

    nimbii82

    Joined:
    Feb 21, 2021
    Posts:
    6
    I realize this is an old thread, but I was running into this same problem. Since there was no followup, I thought I'd just mention that Gregoryl's solution worked perfectly for me.

    Gregoryl, since this sort of follow mode is what a lot of people probably want in a 3rd person camera (or maybe I'm misguided on this assumption?), any chance this will become a single-click option in Cinemachine without having to hack things together like this? Although your solution makes sense, adding the scripts and getting all the proper settings is in my opinion not very intuitive.

    Cheers
     
    Gregoryl likes this.
  10. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    Yes it's an old thread, and since then there have been several new feature in Cinemachine that make this easier.
    Among them, Axis Input Mode setting and 3rdPerson camera:

    upload_2021-2-22_10-25-4.png

     
    nimbii82 likes this.
  11. nimbii82

    nimbii82

    Joined:
    Feb 21, 2021
    Posts:
    6
    Thanks Gregoryl, I had actually already seen that video, but it was early on before I encountered the issue on which this post is based, and I had conveniently forgotten about it ;)

    Cheers
     
  12. jeffman1

    jeffman1

    Joined:
    Oct 21, 2013
    Posts:
    36
    I know this is an old thread Gregoryl but I am using the camera setup from third person youtube video above but want trajectory aiming from an object I already have set out in front of the player. similar to how you throw grenades in gears of war. Except I like the aiming in youtube video because it takes any object in the script code. I have the throwing code setup and just need mostly the Aim Camera from the above video to slow down because it moves really fast around the player sometimes and does not stop. Note: not using the exact same code from the above video but the StarterAssets Third Person prefab as I want a transition to help me recode for shooting arrows and bullets later in Third Person but I have a sling shot that operates similar to the above and using the main code. I also have some aiming refinements to ask about for an aiming object to just rotate with the player because it is either to the left or right of the player right now. If you think you can add z moving as well too go for it but I am mostly using this in the first level of my game (I've only dealt with X and Y).
     
  13. jeffman1

    jeffman1

    Joined:
    Oct 21, 2013
    Posts:
    36
    Follow Code for the player and followTransform (not much changed from StarterAssets code. Note: commenting out the _targetRotation and Aimrotation has no effect on the final result):
    Code (CSharp):
    1.     // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    2.             // if there is a move input rotate player when the player is moving
    3.             if (_input.move != Vector2.zero)
    4.             {
    5.                 _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + _mainCamera.transform.eulerAngles.y;
    6.                 float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity, RotationSmoothTime);
    7.  
    8.              
    9.                     if (_input.move.x == 0 && _input.move.y == 0)
    10.                     {
    11.  
    12.                     if (_input.startlooking == true)
    13.                     {
    14.                         _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + AimCamera.transform.eulerAngles.y;
    15.                         float Aimrotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity, RotationSmoothTime);
    16.  
    17.  
    18.                         //Set the player rotation based on the look transform
    19.                         transform.rotation = Quaternion.Euler(0.0f, Aimrotation, 0.0f);
    20.  
    21.                         //reset the y rotation of the look transform
    22.                         followTransform.transform.localEulerAngles = new Vector3(angles.x, 0, 0);
    23.                         //need to clamp x rotation of player so camera does not go too far or have the camera rotate with player
    24.                     }
    25.                 }
    26.                 else
    27.                 {
    28.              
    29.                     // rotate to face input direction relative to camera position
    30.                     transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
    31.                     //reset the y rotation of the look transform
    32.                     followTransform.transform.localEulerAngles = new Vector3(angles.x, 0, 0);
    33.                 }
    34.             }
    If you need pictures of my camera setup it is the same as the youtube video but I'll post it too. Also, I'll post the code for the aiming object which is used and changing its position (I hope to have it always offset a little to the right shoulder of the player).
     
    Last edited: Jan 17, 2022
  14. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    @jeffman1 It's very difficult for me to understand from your description what you're trying to do and what's going wrong. It would be better if you could make a simple test project with only the camera and aiming related code. Then I could look at it and perhaps offer some help.
     
  15. jeffman1

    jeffman1

    Joined:
    Oct 21, 2013
    Posts:
    36
    Can I message the code to you? I have some code from templates mixed in with some code I made myself. I noticed it has something to do with the cinemachine follow. I am only trying to use a gameobject target instead of a camera reticule target that you use in the youtube tutorial which is my end goal. I want the same as the youtube tutorial but is it possible to use a external game object? I have a simple flat square or plain I wish to use for targeting.
     
  16. jeffman1

    jeffman1

    Joined:
    Oct 21, 2013
    Posts:
    36
    It turns out I was close. I just needed to point at the CameraRoot object for the Main camera (included with the started Assets PlayerArmature prefab) then for the aim Camera use the FollowTarget Object like in the youtube video. I knew I could not be too far off. Anyways, it rotates around the player when not aiming like in the ThirdPerson. When Aiming, now it still follows the around the players shoulder. Hope this helps someone trying to figure out how it works. I might need to update position of the Target object a little better which is where the object ends similar to gears of war grenades or cannons with arced projectiles in mobile games which require a targeting end object which acts as a reticle to where said object is fired.
     
    Last edited: Jan 25, 2022
  17. jeffman1

    jeffman1

    Joined:
    Oct 21, 2013
    Posts:
    36
    Here's the camera setup and you only need the prefab from StarterAssets. This is for advanced aiming that requires a gameobject. At least people can take the same code in your tutorial and modify it. I do not think the 3rdpersonaim extension will work though as it requires a reticule attached to a canvas. This is for targeting that requires arcs and needs a lower to the ground targeting system and targeting system that is movable.
     

    Attached Files:

  18. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    If you still need my help, please make a simple & minimal test project that clearly shows the issue you're having. You can DM it to me. Include precise repro steps so I don't have to spend hours hunting for it.