Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to get camera to go back to original position smoothly after recoiling?

Discussion in 'Scripting' started by milmalho, Jun 23, 2020.

  1. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    When my sniper is scoped in, it smoothly recoils up and to the side, but when coming back to the original position it "snaps" back. Can't seem to figure out why the first half of the recoil is smooth but second half isn't. I've seen to use Lerp but couldn't figure out how to get that to work. Here's a vid showing what I mean: https://imgur.com/a/AnmEKbD

    Code (CSharp):
    1.  
    2. public class MouseLook : MonoSingleton<MouseLook>
    3. {
    4.     public float mouseSensitivity;
    5.     float scopedSensitivity = 20f;
    6.  
    7.     public Transform playerBody;
    8.  
    9.     private float xRotation = 0f;
    10.     float yRotation = 0f;
    11.  
    12.     float sideRecoil = 0f;
    13.     float upRecoil = 0f;
    14.     public float recoilSpeed = 20f;
    15.  
    16.     // Start is called before the first frame update
    17.     void Start()
    18.     {
    19.         //removes cursor from view
    20.         Cursor.lockState = CursorLockMode.Locked;
    21.         mouseSensitivity = 100f;
    22.     }
    23.  
    24.     // Update is called once per frame
    25.     void Update()
    26.     {
    27.         if (Gun.isScoped)
    28.             mouseSensitivity = scopedSensitivity;
    29.         else
    30.             mouseSensitivity = 100f;
    31.        
    32.         //set the mouse x&y movement
    33.         float mouseX = upRecoil + Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
    34.         float mouseY = sideRecoil + Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;
    35.            
    36.         sideRecoil -= recoilSpeed * Time.deltaTime;
    37.         upRecoil -= recoilSpeed * Time.deltaTime;
    38.          
    39.         if (sideRecoil < 0)
    40.             sideRecoil = 0;
    41.         if (upRecoil < 0)
    42.             upRecoil = 0;
    43.            
    44.         //decrease xRotation based on mouseY
    45.         xRotation -= mouseY;
    46.  
    47.         if (PlayerMovement.isSitting)
    48.         {
    49.             //restricts the player from turning their head too far up/down
    50.             xRotation = Mathf.Clamp(xRotation, -35, 35);
    51.         }
    52.         else
    53.         {
    54.             xRotation = Mathf.Clamp(xRotation, -60f, 60f);
    55.         }
    56.  
    57.         transform.localRotation = Quaternion.Euler(xRotation, yRotation, 0f);
    58.         //move player left/right when mouse is going across x axis
    59.         playerBody.Rotate(Vector3.up * mouseX);
    60.        
    61.        
    62.     }
    63.     public IEnumerator Recoil(float up, float side)
    64.     {
    65.         sideRecoil += side;
    66.         upRecoil += up;
    67.         yield return new WaitForSeconds(.25f);
    68.         sideRecoil -= side * 2;
    69.         upRecoil -= up * 2;
    70.     }
    71. }
    72.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,515
    It will probably be easiest to just use an AnimationCurve for side and up recoil axes, then decide how long you want the entire recoil sequence to take, and during the time recoil is happening, directly feed the values from the curves into your side and up recoil offsets. Don't use coroutines, just use a single float that is nonzero during the time that a recoil is happening, and use that float (scaled to match the domain of your AnimationCurves).

    Then you can use the same code on all your guns, just with different curve data for different recoil behavior.
     
  3. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Oh interesting. Not gonna lie, I don't really understand what you mean lol I'm pretty much a beginner. But, I'm gonna look more into AnimationCurve and see if I can do (and understand) what you're suggesting.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,515
    I love your attitude.

    Let me drop you a few more nuggets about using it in your context:

    - make two public AnimationCurve variables (side and up recoil)
    - go edit them in your script instance (there are preset curves you can set)
    - when you use them:
    ---- smoothly transition a float (the input value) from 0 to the upper limit (usually 1) of your curve
    ---- call the .Evaluate() method on each curve to get its value at that point
    ---- use the value you get back from Evaluate() to offset your recoil.

    They're basically a great editable lookup curve.
     
  5. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Damn I'm still so confused, YouTube vids might have confused me more. The vids are either using Lerping with a start point and end point (which I don't want) or using only one AnimationCurve. I'm gonna keep experimenting and see if I can get this to work.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,515
    This was kinda what I had in mind in my description above. Feel free to use whatever you like.

    Complete package attached below.

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class RecoilOnClick : MonoBehaviour
    4. {
    5.     [Header( "Make the curve domains from 0 to 1.")]
    6.     [Header( "Output value is degrees of deflection.")]
    7.     [Header( "Adjust time interval separately below.")]
    8.     public AnimationCurve RecoilUp;
    9.     public AnimationCurve RecoilRight;
    10.  
    11.     [Header( "How long is entire recoil sequence?")]
    12.     public float TimeInterval = 0.25f;
    13.  
    14.     [Header( "Which object is having its .localRotation driven.")]
    15.     public Transform RecoilPivot;
    16.  
    17.     float recoiling;
    18.  
    19.     void DriveRecoil(float fraction)
    20.     {
    21.         float up = RecoilUp.Evaluate(fraction);
    22.         float right = RecoilRight.Evaluate(fraction);
    23.  
    24.         // special number to FORCE you to have zero output when fraction is zero,
    25.         // to keep you from making borked curves and wondering WTF.
    26.         if (fraction == 0)
    27.         {
    28.             up = 0;
    29.             right = 0;
    30.         }
    31.  
    32.         up = -up;
    33.  
    34.         RecoilPivot.localRotation = Quaternion.Euler(up, right, 0);
    35.     }
    36.  
    37.     void Update ()
    38.     {
    39.         if (recoiling == 0)
    40.         {
    41.             if (Input.GetMouseButtonDown(0))
    42.             {
    43.                 recoiling = Time.deltaTime;
    44.             }
    45.         }
    46.  
    47.         if (recoiling > 0)
    48.         {
    49.             float fraction = recoiling / TimeInterval;
    50.  
    51.             recoiling += Time.deltaTime;
    52.             if (recoiling > TimeInterval)
    53.             {
    54.                 recoiling = 0;
    55.                 fraction = 0;            // return to time = 0
    56.             }
    57.  
    58.             DriveRecoil(fraction);
    59.         }
    60.     }
    61. }
     

    Attached Files:

  7. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Thank you so much for helping me out with this. I'm trying to get this to work but might be assigning the wrong items in the inspector. My main camera was the object that would recoil but when assigning it, it causes the bullet to not be accurate and doesn't recoil. Also what do you mean by make the curve domains from 0 to 1. I couldn't find anything about curve domains on the documentation. I also tried assigning other objects (gun and weapon holder) but it wouldn't recoil
     

    Attached Files:

  8. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Hold the phone. We may have something working. Gonna keep messing around will post an update
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,515
    Domain just refers to the input range of the curve. Make it from 0 to 1 because the script assumes that for simplicity.

    Look closely at how I parented things in the scene: I have the player, the look point, the recoil pivot and then the camera below that. When you break stuff apart into hierarchies it gives you opportunities for additional unfettered control at each stage of things. In this point the look point would be moved to how high your eyeballs are from the ground, the pivot is the recoiling, and th camera, well, that's the camera.

    You can also insert a Debug.Break() statement in when the fire happens, which puts the editor into pause mode, and then you can single-step it. You can also go to Edit -> ProjectSettings -> Time to set the timeScale = 0.1 or even 0.01 to see everything happen veeeeeeery sloooooowly so you can see what's going on.
     
  10. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Oh my it looks beautiful. I changed the object recoiling to be my camera holder and made the curves have a keyframe in the middle set at a value of 2.5. Thank you so much this is so cool lol. For some reason it won't let me attach the video here but here's it working: https://imgur.com/a/gJ9z3ft
     
    PraetorBlue likes this.
  11. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Yeah I saw that later and realized your RecoilPivot was my camera holder and it started working. Thank you again
     
  12. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,515
    You're welcome! Glad to hear it.

    Just want to ask: are you using source control? You seem serious enough that I ask because I use source control for all my projects, and it's a great way to not have things mysteriously break that you cannot repair. I use git with Unity and it's very slick. There's lots of tutorials out there for setting it up. It's like a savegame system for development. As long as you commit frequently, you'll have plenty of options for recovering from experiments gone wrong.
     
  13. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    I do yes! I use bitbucket/sourcetree and it has saved me more times than I can count lol
     
    Kurt-Dekker and PraetorBlue like this.
  14. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Alright something super strange happens. When you first open unity, the recoil doesn't work. I have to remove the script from the object and reattach it for it to work. Any idea why that's happening? I've never had this happen before with a script.
     
  15. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,515
    Sometimes prefabs and/or scenes can become corrupt if you change the type of a public inspector variable without changing its name. For instance, if you have:

    Code (csharp):
    1. public int zabo = 123;
    And later decide,

    Code (csharp):
    1. public string zabo = "123";
    You likely will have to reconstruct at least the script instance where that is used (ie., remove and add it).
     
  16. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    So would I Destroy(GetComponent<RecoilOnClick>()) and then AddComponent? Wouldn't I then need to assign the recoil up, recoil right, etc. in script?
     
  17. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Also realizing none of the variable names in this script are the same in any other
     
  18. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,515
    I'm just saying the symptom you describe sounds like a symptom I've seen when changing a public field type without changing its name. Fix is to rebuild that part of the prefab or scene. But I don't know for sure this is your issue.

    Make a trivial replication of it in a fresh scene with a fresh prefab, see wtf is happening. Or rip-tear-shred your current scene to rebuild it and then revert in source control if it doesn't fix!
     
  19. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Ok will try that
     
  20. milmalho

    milmalho

    Joined:
    Feb 16, 2020
    Posts:
    18
    Update: got it working. I integrated the code into my gun script with some changes and that seemed to fix it!
     
    Kurt-Dekker likes this.