Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question 3rd person aiming camera always snaps to a certain direction when entering aiming state.

Discussion in 'Cinemachine' started by Beanasaur, Sep 26, 2022.

  1. Beanasaur

    Beanasaur

    Joined:
    Jan 21, 2020
    Posts:
    9
    Hi

    I'm fairly new to Unity and coding in general, so I apologise in advance if this is a bit of an obvious question.

    I'm making a 3rd person game that will feature melee and ranged combat. I would like to have a freelook camera, a lock on camera and an aiming camera for projectiles. I've managed to implement the aiming camera mostly but have noticed weird behaviour upon entering the PlayerAimCamera.

    I have a empty rig prefab which houses my character and a state driven camera, the state driven camera has 3 children, one of which is the PlayerAimCamera.
    upload_2022-9-26_17-56-29.png
    which is set up with the following settings:

    upload_2022-9-26_18-0-14.png

    I've been learning from a course that uses a state machine to separate out my code into different states, so I have a script called PlayerAimingState which is entered when pressing the aim button.

    The issue I'm having is that when I press the aim button, the camera spins and makes the character face a certain direction in the world, its always the same world direction regardless of where the character is stood or where the character is looking. I've tried to find where this value is set but not had much luck.

    Any help you can offer is appreciated.

    Unfortunately I had trouble uploading a video of my issue. Code Below:

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using Cinemachine;
    6. using System;
    7.  
    8. public class PlayerAimingState : PlayerBaseState
    9. {    
    10.     private readonly int AimingBlendTreeHash = Animator.StringToHash("AimingBlendTree");
    11.     private readonly int AimingForwardHash = Animator.StringToHash("AimingForward");
    12.     private readonly int AimingRightHash = Animator.StringToHash("AimingRight");
    13.  
    14.    
    15.  
    16.     public PlayerAimingState(PlayerStateMachine stateMachine) : base(stateMachine)  {  }
    17.  
    18.     public Camera mainCamera;
    19.  
    20.     // cinemachine
    21.     private float _cinemachineTargetYaw;
    22.     private float _cinemachineTargetPitch;
    23.  
    24.     private bool IsCurrentDeviceMouse;
    25.  
    26.  
    27.     public override void Enter()
    28.     {
    29.         //private Transform mainCameratransform = mainCamera.transform;
    30.         stateMachine.Animator.Play(AimingBlendTreeHash);
    31.  
    32.         Cursor.lockState = CursorLockMode.Locked;
    33.         //stateMachine.LockCameraPosition = true;
    34.  
    35.     }
    36.  
    37.     public override void Tick(float deltaTime)
    38.     {
    39.         if(!stateMachine.InputReader.IsAiming)
    40.         {
    41.             stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
    42.             return;
    43.         }
    44.         AimCameraRotate();
    45.  
    46.         FaceAndAim();
    47.  
    48.         Vector3 movement = CalculateMovement();
    49.        
    50.         Move(movement * stateMachine.AimingMovementSpeed, deltaTime);
    51.  
    52.         UpdateAnimator(deltaTime);
    53.                
    54.                      
    55.  
    56.     }
    57.  
    58.    
    59.  
    60.     public override void Exit()
    61.     {
    62.         Cursor.lockState = CursorLockMode.None;
    63.     }
    64.    
    65.     private void AimCameraRotate()
    66.     {
    67.        
    68.         // if there is an input and camera position is not fixed
    69.         if (stateMachine.InputReader.look.sqrMagnitude >= 0.01f && !stateMachine.LockCameraPosition)
    70.         {
    71.             //Don't multiply mouse input by Time.deltaTime;
    72.             float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime;
    73.  
    74.             _cinemachineTargetYaw += stateMachine.InputReader.look.x * deltaTimeMultiplier * stateMachine.AimSensitivity;
    75.             _cinemachineTargetPitch += stateMachine.InputReader.look.y * deltaTimeMultiplier * stateMachine.AimSensitivity;
    76.         }
    77.  
    78.         // clamp our rotations so our values are limited 360 degrees
    79.         _cinemachineTargetYaw = ClampAngle(_cinemachineTargetYaw, float.MinValue, float.MaxValue);
    80.         _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, stateMachine.BottomClamp, stateMachine.TopClamp);
    81.  
    82.         // Cinemachine will follow this target
    83.         stateMachine.transform.rotation = Quaternion.Euler(_cinemachineTargetPitch + stateMachine.CameraAngleOverride,
    84.         _cinemachineTargetYaw, 0.0f);              
    85.        
    86.     }
    87.  
    88.     private void FaceAndAim()
    89.     {
    90.         Vector3 mouseWorldPosition = Vector3.zero;
    91.  
    92.         Debug.Log("Initial Mouse Position" + mouseWorldPosition);
    93.  
    94.         Vector2 screenCentrePoint = new Vector2(Screen.width / 2f, Screen.height / 2f);
    95.         Ray ray = Camera.main.ScreenPointToRay(screenCentrePoint);
    96.         if (Physics.Raycast(ray, out RaycastHit raycastHit, 999f, stateMachine.AimColliderLayerMask))
    97.         {
    98.             stateMachine.debugTransform.position = raycastHit.point;
    99.             mouseWorldPosition = raycastHit.point;
    100.  
    101.             Debug.Log("Mouse Position after Raycast hit point" + mouseWorldPosition);
    102.         }
    103.  
    104.        
    105.         Vector3 worldAimTarget = mouseWorldPosition;
    106.  
    107.         worldAimTarget.y = stateMachine.transform.position.y;
    108.  
    109.         Vector3 aimDirection = (worldAimTarget - stateMachine.transform.position).normalized;
    110.  
    111.         stateMachine.transform.forward = Vector3.Lerp(stateMachine.transform.forward, aimDirection, Time.deltaTime * 20f);
    112.     }
    113.  
    114.     private Vector3 CalculateMovement()
    115.     {
    116.         Vector3 forward = stateMachine.MainCameraTransform.forward;
    117.         Vector3 right = stateMachine.MainCameraTransform.right;
    118.  
    119.         forward.y = 0f;
    120.         right.y = 0f;
    121.  
    122.         forward.Normalize();
    123.         right.Normalize();
    124.  
    125.         return forward * stateMachine.InputReader.MovementValue.y + right * stateMachine.InputReader.MovementValue.x;
    126.     }
    127.  
    128.     private void UpdateAnimator(float deltaTime)
    129.     {
    130.         if(stateMachine.InputReader.MovementValue.y == 0)
    131.         {
    132.             stateMachine.Animator.SetFloat(AimingForwardHash, 0, 0.1f, deltaTime);
    133.         }
    134.         else
    135.         {
    136.             float value = stateMachine.InputReader.MovementValue.y > 0 ? 1f : -1f;
    137.             stateMachine.Animator.SetFloat(AimingForwardHash, value, 0.1f, deltaTime);
    138.         }
    139.  
    140.         if(stateMachine.InputReader.MovementValue.x == 0)
    141.         {
    142.             stateMachine.Animator.SetFloat(AimingRightHash, 0, 0.1f, deltaTime);
    143.         }
    144.         else
    145.         {
    146.             float value = stateMachine.InputReader.MovementValue.x > 0 ? 1f : -1f;
    147.             stateMachine.Animator.SetFloat(AimingRightHash, value, 0.1f, deltaTime);
    148.         }
    149.        
    150.     }
    151.  
    152.     private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
    153.     {
    154.         if (lfAngle < -360f) lfAngle += 360f;
    155.         if (lfAngle > 360f) lfAngle -= 360f;
    156.         return Mathf.Clamp(lfAngle, lfMin, lfMax);
    157.     }
    158.  
    159.  
    160. }
     
  2. gaborkb

    gaborkb

    Unity Technologies

    Joined:
    Nov 7, 2019
    Posts:
    856
    Why do you have a Cinemachine Input Provider on this vcam? 3rdPersonFollow does not accept input. It controls the camera based on the Follow target's transform.

    This could be the reason why you experience a snap. Your other cameras are controlled by user input, but this one depends on the Follow target.

    What is the behaviour you are trying to get when you switch to this camera?
     
    Last edited: Sep 26, 2022
  3. Beanasaur

    Beanasaur

    Joined:
    Jan 21, 2020
    Posts:
    9
    This was just left there from me messing around trying to figure out the issue.

    upload_2022-9-28_13-11-56.png

    I've removed that component and the behaviour is the same.

    What I would like the camera to do is aim towards where the camera is currently facing in the FreeLookState or at the very least the direction the player is currently facing in said state.

    Instead it is just reverting to a world direction upon entering the camera / state.

    Freelookstate code below:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerFreeLookState : PlayerBaseState
    6. {
    7.     private readonly int FreeLookSpeedHash = Animator.StringToHash("FreeLookSpeed");
    8.     private readonly int FreeLookBlendTreeHash = Animator.StringToHash("FreeLookBlendTree");
    9.  
    10.     private const float AnimatorDampTime = 0.1f;
    11.  
    12.     private const float CrossFadeDuration = 0.1f;
    13.        
    14.     public PlayerFreeLookState(PlayerStateMachine stateMachine) : base(stateMachine) { }
    15.  
    16.     public override void Enter()
    17.     {
    18.         stateMachine.InputReader.TargetEvent += OnTarget;
    19.        
    20.         stateMachine.Animator.CrossFadeInFixedTime(FreeLookBlendTreeHash, CrossFadeDuration);
    21.  
    22.     }
    23.  
    24.     public override void Tick(float deltaTime)
    25.     {
    26.         if(stateMachine.InputReader.IsAttacking)
    27.         {
    28.             stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0));
    29.             return;
    30.         }
    31.  
    32.         if(stateMachine.InputReader.IsAiming)
    33.         {
    34.             stateMachine.SwitchState(new PlayerAimingState(stateMachine));
    35.             return;
    36.         }
    37.  
    38.         Vector3 movement = CalculateMovement();
    39.  
    40.         Move(movement * stateMachine.FreeLookMovementSpeed, deltaTime);
    41.        
    42.         if (stateMachine.InputReader.MovementValue == Vector2.zero)
    43.         {
    44.             stateMachine.Animator.SetFloat(FreeLookSpeedHash, 0, AnimatorDampTime, deltaTime);
    45.             return;
    46.         }
    47.  
    48.         stateMachine.Animator.SetFloat(FreeLookSpeedHash, 1, AnimatorDampTime, deltaTime);
    49.         FaceMovementDirection(movement, deltaTime);
    50.     }
    51.  
    52.     public override void Exit()
    53.     {
    54.         stateMachine.InputReader.TargetEvent -= OnTarget;
    55.     }
    56.  
    57.     private void OnTarget()
    58.     {
    59.         if (!stateMachine.Targeter.SelectTarget()) { return; }
    60.         stateMachine.SwitchState(new PlayerTargetingState(stateMachine));
    61.     }
    62.  
    63.     private Vector3 CalculateMovement()
    64.     {
    65.         Vector3 forward = stateMachine.MainCameraTransform.forward;
    66.         Vector3 right = stateMachine.MainCameraTransform.right;
    67.  
    68.         forward.y = 0f;
    69.         right.y = 0f;
    70.  
    71.         forward.Normalize();
    72.         right.Normalize();
    73.  
    74.         return forward * stateMachine.InputReader.MovementValue.y + right * stateMachine.InputReader.MovementValue.x;
    75.     }
    76.  
    77.     private void FaceMovementDirection(Vector3 movement, float deltaTime)
    78.     {
    79.         stateMachine.transform.rotation = Quaternion.Lerp(
    80.             stateMachine.transform.rotation,
    81.             Quaternion.LookRotation(movement),
    82.             deltaTime * stateMachine.RotationDamping);
    83.     }
    84.  
    85. }
    86.  
     
  4. gaborkb

    gaborkb

    Unity Technologies

    Joined:
    Nov 7, 2019
    Posts:
    856
    Could you send me a couple of drawings (e.g. topdown) of the transitions you'd like. Showing the target's and the camera's orientation, position?

    I'll try to guide you through a use-case :)
     
  5. Beanasaur

    Beanasaur

    Joined:
    Jan 21, 2020
    Posts:
    9
    I'm not really sure how I would draw it to be honest, basically the freelook camera follows the player and allows the player to look around using the mouse or right stick. When the aim button is pressed the camera is switched to an over the shoulder view e.g. aiming in Resident Evil 4. This works but the issue is that when it transitions from the freelook camera to the aiming camera it seems to always snap to the original transform of the aiming camera in the game world rather than aiming relative to the position of the player, this happens only when entering the new state and allows me to aim and move normally afterwards, but then when leaving and re-entering the behaviour will be the same.

    Is there a way I can just send you my project? It's not that big about 4.4GB.
     
  6. Beanasaur

    Beanasaur

    Joined:
    Jan 21, 2020
    Posts:
    9
    I've uploaded a clip of the issue.
     

    Attached Files:

  7. gaborkb

    gaborkb

    Unity Technologies

    Joined:
    Nov 7, 2019
    Posts:
    856
    You can send me your project in a direct message. You can delete the library folder before sending it to me, that should make it significantly smaller. I'll have a look ;)

    This makes it clear what the issue is. This is what I understood from your description too. :) This should not happen, I'll check your project and see why this happens.
     
  8. Beanasaur

    Beanasaur

    Joined:
    Jan 21, 2020
    Posts:
    9
    I'm having trouble uploading it to a direct conversation, when Library is removed and compressed its about 1.3 gb. do you have any suggestions?

    I've also tried compressing it to 100mb chunks but they were still too large, what is the largest file size I can send?
     
    Last edited: Oct 18, 2022
  9. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,268
    Have you tried setting InheritPosition on your vcams?

    upload_2022-10-18_10-2-3.png
     
  10. gaborkb

    gaborkb

    Unity Technologies

    Joined:
    Nov 7, 2019
    Posts:
    856
    You can share it on Google Drive or similar, for example.
     
  11. Beanasaur

    Beanasaur

    Joined:
    Jan 21, 2020
    Posts:
    9
    I tried this but sadly it made no difference.
     
  12. Beanasaur

    Beanasaur

    Joined:
    Jan 21, 2020
    Posts:
    9
    I sent you a link.
     
    gaborkb likes this.
  13. gaborkb

    gaborkb

    Unity Technologies

    Joined:
    Nov 7, 2019
    Posts:
    856
    The problem is not with Cinemachine here. Third Person Follow positions itself based on the Follow Targets position and orientation. When you start aiming, the character is set to orientation 0,0,0. So it is not the camera that snaps, but the player, and then Cinemachine reacts to this behaviour.

    To change this behaviour, you'll need to change AimCameraRotate function in your script PlayerAimingState.cs.

    The problem is that you check for look input. When there is none (e.g. when you switch to aim camera), stateMachine.transform.rotation is going to be set to identity (0,0,0). Making your character's orientation snap to 0,0,0.
     
  14. Beanasaur

    Beanasaur

    Joined:
    Jan 21, 2020
    Posts:
    9
    Ok Thanks.

    Forgive my ignorance but I still don't fully understand the issue, I've been following online tutorials to try and learn how to do this and I clearly don't understand this code very well.

    Do you have any suggestions on how to better achieve the desired outcome or a suggestion of any materials I can look at to help me learn it.
     
  15. gaborkb

    gaborkb

    Unity Technologies

    Joined:
    Nov 7, 2019
    Posts:
    856
    This might be helpful:
     
    Gregoryl, Beanasaur and tsukimi like this.
  16. Beanasaur

    Beanasaur

    Joined:
    Jan 21, 2020
    Posts:
    9
    Thank you very much, I had looked at this before but after what we had discussed I downloaded the project and reviewed the code. It really helped this time and I managed to resolve my issue.

    Thanks Again!
     
    gaborkb and Gregoryl like this.