Search Unity

Animating the Camera

Discussion in 'Animation' started by shakozzz, Mar 23, 2017.

  1. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    Hi all,
    I'm trying to get my camera to rotate on the X-axis as my player(a bird) dives down and rotate back to its original position as the player floats back up. To achieve this I created 2 animations; one for rotating the camera up(diving) and one for rotating it back down(floating). So whenever I'm diving and need to float again, the floating animation needs to pick up from the same coordinates where the diving animation left off. So say the animation when left to play uninterrupted moves the camera's rotation on the X from 50 to 20. If the player decided to abort diving and start floating anywhere in the middle, say at 30, the floating animation needs to start from 30 back to 50. Hope this is making sense so far.
    I've tried achieving this with parenting where I place the camera object as a child of an empty object and place the animation on the parent object. This still forces my animations to start from set positions though. This seems like an obvious thing that people would want to implement in their games, however I haven't found anything that comes close to a dedicated animation solution from unity here in the forums and that's where I hope you guys could help me out.

    Own thoughts:
    I'm thinking I could change the X values of the initial keyframes in each animation in c# so that the X value of the initial keyframe in each animation is equal to the final X value reached by the previous animation. This would force the new animation to start from the same position where the one before it was interrupted. I'm sure, however, that my problem could be solved purely in without code.

    floating animation

    diving animation
     
    Last edited: Mar 23, 2017
  2. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    Can you post a screen shot of the animation window for this animation? It seems like there is something setting the transform.position as well as the transform.rotation. You should be able to remove one of those properties. Additionally, you might be able to achieve the effect you want using Transform.LookAt in a simple script on your camera.
     
  3. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    Check out the edit. Do they give you any insights?
     
  4. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    Okay...I think I have a much better understanding of what you are trying to accomplish now. Seeing the animation curves let me really visualize what you were describing quite a bit. I got confused when you said the animations were starting from "set positions" rather than "set rotations".

    All in all, it sounds like you could actually pull off the desired effect with a with two clips that are only a single frame each. One which has the base rotation of the camera and one that has the maximum dive angle of the camera. Those two clips can be placed in a 1D BlendTree within your Animator and controlled by a single float parameter. The result would be something like this:

    You would just need your code to accomplish your desired speed of the blend. If you set up an AnimationCurve as a field in the component driving the blend, you can use AnimationCurve.Evaluate() to get a nice smooth movement in and out of the dive along a curve. This will allow you to track the progress of the dive between 0 and 1 at all times. When the player aborts early you are just reducing from the current value back to 0. Hope that helps you out.

    Cheers,
    TrickyHandz
     
    theANMATOR2b likes this.
  5. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    Thanks for the input so far. I've managed to create the blend tree, placed the animations in it (one keyframe each) and and created the float parameter. Where I'm lost, however, is what to write in code. What I have so far is a bool variable diving which is true as long as the player is pressing the space bar. I'm guessing somehow I need to link this bool variable to the blend tree so it triggers the animations when it's true. This is what I have so far in unity:
    upload_2017-3-23_22-51-15.png
    Edit 1: I must've done something wrong, since when changing DivePercentage my camera doesn't move at all like yours.
    Edit 2: I've changed my animations so that now they contain 2 keyframes each (I must've misunderstood when you said 1 frame each?) so edit 1 is now irrelevant. Still trying to figure out how to manipulate DivePercentage through c#, though. Here are the new animation curves:
    CamRotDown.png CamRotUp.png
     
    Last edited: Mar 24, 2017
  6. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    You'll have to bear with me on a bit of a wait. I will gladly put an example together for you but it won't be until later tonight. I can put together a script and a video explaining the whole technique.

    Cheers,
    TrickyHandz
     
  7. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    That's totally fine, take your time.
     
    TrickyHandz likes this.
  8. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    I really wish I could have gotten this up earlier, but my screen capture decided it didn't want to cooperate with me. So, here is a video where I set up an entire system for this from scratch...live coding and mistakes included.



    You can download a complete UnityPackage with everything from the video here: BlendTree Camera Animation
    I think this should really help you get the type of effect you were looking for. The code in the package is fully commented and you should be able to do a lot of customization or build a whole new system on the same principles. Have fun!

    Cheers,
    TrickyHandz
     
    theANMATOR2b and shakozzz like this.
  9. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    First of all, thanks for the effort you've put into this. I really do appreciate it.
    So I've managed to get it working as you showed in the video. The problem is, though, when replacing
    Code (CSharp):
    1. if (Input.GetKeyDown(KeyCode.Space))
    with
    Code (CSharp):
    1. if (birdScript.diving)
    birdScript.diving being a bool variable which reacts to a button I have on the screen, the magic doesn't happen anymore even though the bool has the correct values. I need to do this since I'm building a mobile app and I check for the aforementioned button press in another script and set the bool accordingly. Any ideas for why this is so?

    Edit 1: So it turns out it's because of how Input.GetKeyDown works. This is from the docs:
    In other words, when I press space, the if statement checks out and the coroutine is started. On the iteration of update we don't enter the if statement again since space is still being pressed and
    Input.GetKeyDown doesn't return true anymore (till it's released and pressed again, that is). With my bool however the if statement kept checking out on every iteration of update, thus continuously restarting the coroutine and not giving it a chance to get any work done. I got around this by changing the if statement's condition so that I only enter the if statement if the bool value has changed since the last coroutine start. This is what my update looks like now:
    Code (CSharp):
    1. //...
    2. private bool lastState; //prevents re-entering coroutine if birdScript.diving hasn't changed since last frame
    3.  
    4. void Start()
    5. {
    6.         //...
    7.         lastState = birdScript.diving;
    8. }
    9.  
    10. private void Update () {
    11.         if (birdScript.diving != lastState)
    12.         {
    13.             if (birdScript.diving)
    14.             {
    15.                 Debug.Log("hi");
    16.                 if (currentBlend != null)
    17.                 {
    18.                     StopCoroutine(currentBlend);
    19.                 }
    20.                 currentBlend = DiveBlend(0f, enterDiveDuration, enterDiveCurve);
    21.                 StartCoroutine(currentBlend);
    22.             }
    23.             else if (!birdScript.diving)
    24.             {
    25.                 Debug.Log("bye");
    26.                 if (currentBlend != null)
    27.                 {
    28.                     StopCoroutine(currentBlend);
    29.                 }
    30.                 currentBlend = DiveBlend(1f, exitDiveDuration, exitDiveCurve);
    31.                 StartCoroutine(currentBlend);
    32.             }
    33.             lastState = birdScript.diving;
    34.         }      
    35.     }
    Thanks once again for the help. Marked as solved!
     
    Last edited: Mar 26, 2017
  10. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    What I would recommend in this case is actually allowing the UI button to call methods for entering and exiting the dive. I've put together a short video of those modifications.



    Here are the two methods that I mention in the video. When you put these in, remember to remove the Update() method entirely so you don't have per-frame code happening that isn't needed.

    Code (csharp):
    1.     /// <summary>
    2.     /// Used to start the camera's
    3.     /// diving behavior
    4.     /// </summary>
    5.     public void EnterDive()
    6.     {
    7.         if (currentBlend != null)
    8.         {
    9.             StopCoroutine(currentBlend);
    10.         }
    11.         currentBlend = DiveBlend(1f, enterDiveDuration, enterDiveCurve);
    12.         StartCoroutine(currentBlend);
    13.     }
    14.  
    15.     /// <summary>
    16.     /// Used to end the camera's dive
    17.     /// and return to the starting
    18.     /// rotation.
    19.     /// </summary>
    20.     public void ExitDive()
    21.     {
    22.         if (currentBlend != null)
    23.         {
    24.             StopCoroutine(currentBlend);
    25.         }
    26.         currentBlend = DiveBlend(0f, exitDiveDuration, exitDiveCurve);
    27.         StartCoroutine(currentBlend);
    28.     }
    I think that should do it.

    Cheers,
    TrickyHandz
     
    shakozzz likes this.
  11. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    Hadn't seen this while posting my edit. Yes, this would be exactly what I need.
     
    TrickyHandz likes this.