Search Unity

Blending player object from gameplay to cutscene and back again

Discussion in 'Timeline' started by Roy-Massaad1, Aug 26, 2017.

  1. Roy-Massaad1

    Roy-Massaad1

    Joined:
    Jun 30, 2015
    Posts:
    16
    Hi guys

    What is the correct approach to blend from gameplay seamlessly into a cut-scene using the timeline ?


    My question is mainly to the devs as i am finding some ambiguity about this after going through unity conference videos and the online docs (preview doc, official doc, playable apis, forum, unity unite on youtube)

    I am checking playables and understood that they allow me to create custom tracks to animate extra things im interested in like lights without creating default animation tracks with clips for them. ok i understood that and downloaded the asset and wizard from the store.

    Now supposing i want to blend my script controlled player car/box (the script controls the world location either direction or with a animator state machine) into a cutscene seamlessly, how would i go about doing that ?

    I want to smoothly overide what the 'gamplay' animation of the play box including any script and animator state machines it has running into the cut-scene clip.

    Im not talking here about cameras, im checked cinemachine it will blend nicely the camera i tried that, i am talking about the player object itself, of type generic not humanoid in the import settings.

    I tried updating to the latest beta, set extrapolation to none, enable/disable root motion and still cant get a smooth transition from gameplay to cutscene. it just snaps. i did find a reference to a bug on this in another thread. https://forum.unity3d.com/threads/issue-with-mixing-animator-and-timeline-animations.468955/

    Im currently considering testing and scripting a custom playable that exposes a transform reference and "cinematic" bool to decide how how to blend clips based on whether the bool is cinematic or not. Still thinking about it and i have no idea if it will even resolve my problem when tested.

    So my question is the the unity devs, again, very specific, not about cinemachine or other stuff..

    What are the steps you yourself designed and will use we you need to take to blend a player controlled object like a car/box with its own animator state machine (generic) into a cutscene anition smoothly on the timeline ?

    Just point us in the right direction

    Big heart & thanks
     
  2. Roy-Massaad1

    Roy-Massaad1

    Joined:
    Jun 30, 2015
    Posts:
    16
    ps: do excuse any typo errors in the first post
     
  3. Andy-Touch

    Andy-Touch

    A Moon Shaped Bool Unity Legend

    Joined:
    May 5, 2014
    Posts:
    1,485
    What do you mean by 'it just snaps'?

    Can you give more context of your car/box example and what you want to sequence in the cinematic?
     
  4. knocking

    knocking

    Joined:
    Apr 10, 2014
    Posts:
    26
    Hi Andy,

    While we are working on finding a workflow for this, meanwhile I thought i'd try my luck in trying to explain what Roy was asking earlier. Perhaps you can help us with certain tips and guidelines.

    Basically imagine we have a flying ship with generic animation and a state machine setup that drives all the hierarchy movements on this ship. The ship is in gameplay mode and the player has control over it - but at a certain point it needs to perform a a pre animated action for a cut-scene. But it needs to blend seamlessly from the gameplay mode into the cut-scene mode and then when the cut scene ends it blends back to gameplay mode again... This also needs to obviously blend not just the root of the object but the entire hierarchy as well, for instance the wings movements etc.. (Basically blending with the state machine).

    If you follow this link I've uploaded a simple pre-animated sequence to try to explain what we want to achieve in game.



    How can we achieve this via the timeline?
     
    Last edited: Aug 27, 2017
    Roy-Massaad1 likes this.
  5. m-i-e-c-z

    m-i-e-c-z

    Joined:
    Nov 10, 2010
    Posts:
    27
    Hi,
    I was struggling with the same challenge and I wrote two simple scripts, that will automatically blend character's animation from the one that is set in Timeline (last frame) and current played animation (from the Animator graph).

    The first script should be attached to the same game object as the PlayableDirector. It checks when the director stops playing and starts AutoBlend() functions on the characters.

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.Playables;
    6.  
    7. //Attach this script to the game object containg the PlayableDirector component
    8.  
    9. public class PlayableDirectorAutoBlendOut : MonoBehaviour {
    10.  
    11.     //a reference to the playable director
    12.     public PlayableDirector director;
    13.  
    14.     //if set to true, a "Reset" trigger will be called on all animated characters
    15.     public bool resetCharacters = true;
    16.     public bool unparentCharacters = true;
    17.     //blend out duration
    18.     public float blendCharactersTime = 0.5f;
    19.  
    20.     //a reference to all characters (AutoBlendOut components) that need the blending
    21.     public AutoBlendOut[] autoBlends;
    22.     public PlayableDirector[] directorsToStop;
    23.     //normalized director playback time
    24.     float time;
    25.  
    26.     private void Awake()
    27.     {
    28.         if (director == null)
    29.         {
    30.             director = GetComponent<PlayableDirector>();
    31.         }
    32.         if (director != null)
    33.         {
    34.             //set the extraplotaion mode to Hold - we will still stop the director, but it ensures the last frame of the animation
    35.             //stays long enough to be captured by the AutoBlendOut scripts
    36.             director.extrapolationMode = DirectorWrapMode.Hold;
    37.         }
    38.     }
    39.  
    40.     bool _directorsStopped = false;
    41.  
    42.     private void Update()
    43.     {
    44.         //check if the director is currently playing
    45.         if (director != null && director.state == PlayState.Playing)
    46.         {
    47.             //if so - check the normalized time of the director
    48.             time = (float)(director.time / director.duration);
    49.  
    50.             //stops any additional directors at the start of the Timeline
    51.  
    52.             if (time >= 0.1f && !_directorsStopped)
    53.             {
    54.                 for (int i = 0; i < directorsToStop.Length; i++)
    55.                 {
    56.                     directorsToStop[i].Stop();
    57.                 }
    58.                 _directorsStopped = true;
    59.             }
    60.             if (time >= 1f)
    61.             {
    62.                 //if the director is done playing, start the autoblend scripts
    63.                 for (int i = 0; i < autoBlends.Length; i++)
    64.                 {
    65.                     autoBlends[i].AutoBlend(resetCharacters, blendCharactersTime);
    66.                    
    67.                     //automatically unparents characters at the end of the timeline
    68.                     if (unparentCharacters)
    69.                     {
    70.                         autoBlends[i].transform.parent = null;
    71.                     }
    72.                    
    73.                 }
    74.                 //and finally stop the director
    75.                 director.Stop();
    76.             }
    77.         }
    78.     }
    79. }
    80.  
    81.  
    The second script is attached to all characters that need this auto blending feature. You need to pass them to the autoBlends[] array of the PlayableDirectorAutoBlendOut component.

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. [System.Serializable]
    6. //A simple class to store the local position and local rotation of a bone (transform) and the reference to that bone
    7. public class BlendedTransform
    8. {
    9.     public Transform targetTransform;
    10.     public Vector3 position;
    11.     public Quaternion rotation;
    12. }
    13.  
    14. public class AutoBlendOut : MonoBehaviour {
    15.  
    16.     //the reference to the rig game object (the bones should be children of that transform)
    17.     public Transform myRig;
    18.  
    19.     //a list containing all the child game objects of the myRig transform
    20.     List<BlendedTransform> allmyBones = new List<BlendedTransform>();
    21.  
    22.     //a reference to the animator component
    23.     Animator anim;
    24.  
    25.     //the blend weight. When 0 the Animator animation is being played, when 1 the last frame set by the AutoBlend function is played
    26.     float weight = 0f;
    27.    
    28.     //blend duration
    29.     float _blendOutTime = 0.5f;
    30.  
    31.     //update mode set for the Animator component (we need to override it to "Normal" for the blend)
    32.     AnimatorUpdateMode _animUpdateMode;
    33.     private void Awake()
    34.     {
    35.         //getting the reference to the Animator
    36.         anim = GetComponent<Animator>();
    37.        
    38.         if (myRig == null)
    39.         {
    40.             myRig = transform;
    41.         }
    42.     }
    43.  
    44.     //call this function to start the blending process - it takes saves the current bones' positions and rotations (a reference frame)
    45.     public void AutoBlend (bool reset, float blendTime) {
    46.         _animUpdateMode = anim.updateMode;
    47.        
    48.         //override animation update mode to Normal (only then the blend looks good)
    49.         anim.updateMode = AnimatorUpdateMode.Normal;
    50.  
    51.         Transform[] myBones = myRig.GetComponentsInChildren<Transform>();
    52.         _blendOutTime = blendTime;
    53.         for (int i = 0; i < myBones.Length; i++)
    54.         {
    55.             if (myBones[i] != myRig)
    56.             {
    57.                 BlendedTransform bt = new BlendedTransform();
    58.                 bt.targetTransform = myBones[i];
    59.                 bt.position = myBones[i].localPosition;
    60.                 bt.rotation = myBones[i].localRotation;
    61.                 allmyBones.Add(bt);
    62.             }
    63.         }
    64.  
    65.         //if you pass a reset bool, the "Reset" trigger will be additionally called on the animator
    66.  
    67.         if (reset)
    68.         {
    69.             anim.SetTrigger("Reset");
    70.         }
    71.  
    72.         //here we set the weight to 1f to start with the saved reference frame
    73.  
    74.         weight = 1f;
    75.         _blend = true;
    76.     }
    77.  
    78.     public void SetAnimatorUpdate(AnimatorUpdateMode animUpdate)
    79.     {
    80.         anim.updateMode = animUpdate;
    81.     }
    82.     bool _blend = false;
    83.     private void LateUpdate()
    84.     {
    85.         //we blend bones in the late update to override the Animator component
    86.         BlendBones();
    87.     }
    88.  
    89.     void BlendBones()
    90.     {
    91.         //we stop blending bones when the _blend parameter is false
    92.         if (!_blend)
    93.         {
    94.             return;
    95.         }
    96.         if (_blendOutTime > 0f && weight > 0f)
    97.         {
    98.             //continue to blend bones untill the weight reaches 0f
    99.             weight -= Time.deltaTime / _blendOutTime;
    100.             for (int i = 0; i < allmyBones.Count; i++)
    101.             {
    102.                 allmyBones[i].targetTransform.localRotation = Quaternion.Lerp(allmyBones[i].targetTransform.localRotation, allmyBones[i].rotation, Mathf.Max(0f, weight));
    103.                 allmyBones[i].targetTransform.localPosition = Vector3.Lerp(allmyBones[i].targetTransform.localPosition, allmyBones[i].position, Mathf.Max(0f, weight));
    104.             }
    105.         }
    106.         else
    107.         {
    108.             //stop blending bones
    109.             _blend = false;
    110.  
    111.             //reset the update mode of the Animator
    112.             anim.updateMode = _animUpdateMode;
    113.         }
    114.     }
    115. }
    116.  
    117.  
    Hope it helps!
     
    Last edited: Mar 9, 2018