Animation Duration

Announcements, support questions, and discussion for the Dialogue System.
pegassy
Posts: 135
Joined: Sat Mar 17, 2018 8:07 pm

Animation Duration

Post by pegassy »

Hello,

I am using the integration with Rogo Digital's Lipsync Pro. In a scene where the NPC is speaking with a lipsync and with a sequencer camera, I am also using AnimatorPlay(Talk, NPC) to play a talk animation. However, the animation is longer than the speech itself, and I am looking for a way to limit the duration of the talk animation to each speech, and crossfade to an idle towards the end of the speech. Without crossfade the transition to idle animation is very abrupt, and if I turn on the crossfade it will still wait until the talk animation is also over, which will linger into the next speech node.

I also tried to integrate the talk animations using the gesture component of Lipsync Pro. However, it also does not have a stop animation feature, and I run into the same problem where the animation transitions into the new node.

Is there an easy way to contain each talk animation within that speech node?
User avatar
Tony Li
Posts: 22057
Joined: Thu Jul 18, 2013 1:27 pm

Re: Animation Duration

Post by Tony Li »

Hi,

If you're comfortable with a little bit of scripting, I recommend making a copy of SequencerCommandLipSync.cs. Maybe call it SequencerCommandLipTalk.cs. If so, the sequencer command to use would be LipTalk() instead of LipSync().

The last line of code in the script's Start() method is:

Code: Select all

if (nowait) Stop();
You could add some code like this to transition to an idle state 0.5 seconds before the end of the audio clip:

Code: Select all

if (nowait) Stop();
yield return new WaitForSeconds(audioClip.length - 0.5f); // 0.5 seconds before the end...
subject.GetComponent<Animator>().CrossFade("Idle", 0.5f); // ...cross fade to Idle.
pegassy
Posts: 135
Joined: Sat Mar 17, 2018 8:07 pm

Re: Animation Duration

Post by pegassy »

I think I can handle that much coding :)

I greatly appreciate it. I hope to share the updated snippet of a conversation on twitter today.
User avatar
Tony Li
Posts: 22057
Joined: Thu Jul 18, 2013 1:27 pm

Re: Animation Duration

Post by Tony Li »

Sounds good. I'll be back online in about 2 hours if you have any questions about it.

What's your Twitter?
pegassy
Posts: 135
Joined: Sat Mar 17, 2018 8:07 pm

Re: Animation Duration

Post by pegassy »

Ok, so much for being able to handle "that much of coding". :)

I got the following compiler error and I am not sure if I actually did it wrong. I placed it inside the Start() method under "if (nowait) Stop();".

Assets/Pixel Crushers/Dialogue System/Third Party Support/LipSync Support/Scripts/SequencerCommandLipTalk.cs(26,21): error CS1624: The body of `PixelCrushers.DialogueSystem.SequencerCommands.SequencerCommandLipTalk.Start()' cannot be an iterator block because `void' is not an iterator interface type
User avatar
Tony Li
Posts: 22057
Joined: Thu Jul 18, 2013 1:27 pm

Re: Animation Duration

Post by Tony Li »

Sorry, I glossed over that. And even the explanation will sound like gibberish to non-programmers: to use the yield keyword, you need to make the Start method a coroutine by changing "void Start" to "IEnumerator Start", which also requires that you include "System.Collections" in the using statements at the top of the script.

In other words, add this line to the top of the script:

Code: Select all

using System.Collections;
And also change this line:

Code: Select all

        public void Start()
to this:

Code: Select all

        public IEnumerator Start()
Here's the whole script:
SequencerCommandLipTalk.cs

Code: Select all

using System.Collections;
using UnityEngine;
using RogoDigital.Lipsync;

namespace PixelCrushers.DialogueSystem.SequencerCommands
{

    public class SequencerCommandLipTalk : SequencerCommand
    {

        private LipSync lipSync = null;
        private AudioClip audioClip = null;
        private bool nowait = false;

        public IEnumerator Start()
        {
            var lipSyncAssetName = GetParameter(0);
            var lipSyncData = DialogueManager.LoadAsset(lipSyncAssetName, typeof(LipSyncData)) as LipSyncData;
            var subject = GetSubject(1, Sequencer.Speaker);
            lipSync = (subject == null) ? null : subject.GetComponentInChildren<LipSync>();
            nowait = string.Equals(GetParameter(2), "nowait", System.StringComparison.OrdinalIgnoreCase);
            if (lipSync == null)
            {
                if (DialogueDebug.LogWarnings) Debug.LogWarning("Dialogue System: Sequencer: LipSync(" + GetParameters() + "): LipSync subject not found");
                Stop();
            }
            else if (lipSyncData == null)
            {
                if (DialogueDebug.LogWarnings) Debug.LogWarning("Dialogue System: Sequencer: LipSync(" + GetParameters() + "): LipSync Data not found");
                Stop();
            }
            else
            {
                if (DialogueDebug.LogInfo) Debug.Log("Dialogue System: Sequencer: LipSync(" + lipSyncData + ", " + lipSync + (nowait ? ", nowait" : string.Empty) + ")");
                lipSync.Play(lipSyncData);
                audioClip = lipSyncData.clip;
                if (nowait) Stop();
                yield return new WaitForSeconds(audioClip.length - 0.5f); // 0.5 seconds before the end...
                subject.GetComponent<Animator>().CrossFade("Idle", 0.5f); // ...cross fade to Idle.                
            }
        }

        public void Update()
        {
            if (DialogueTime.IsPaused)
            {
                if (!lipSync.IsPaused) lipSync.Pause();
            }
            else
            {
                if (lipSync.IsPaused) lipSync.Resume();
                if (!lipSync.IsPlaying)
                {
                    Stop();
                }
            }
        }

        public void OnDestroy()
        {
            if (lipSync == null) return;
            if (nowait) return; // If nowait, don't manually stop.
            if (lipSync.audioSource != null && lipSync.audioSource.clip != audioClip) return; // If already playing a different clip, don't stop it.
            lipSync.Stop(true);
        }

        //// If you want to debug LipSync, uncomment the method below:
        //private void OnGUI()
        //{
        //    if (lipSync.IsPlaying) GUILayout.Label("LipSync.IsPlaying");
        //    if (lipSync.IsPaused) GUILayout.Label("LipSync.IsPaused");
        //}
    }
}
I should have also mentioned that this line:

Code: Select all

                subject.GetComponent<Animator>().CrossFade("Idle", 0.5f); // ...cross fade to Idle.
assumes that you want to play the animator state named "Idle". And of course it assumes that it should start crossfading into that state in the last 0.5 seconds.
pegassy
Posts: 135
Joined: Sat Mar 17, 2018 8:07 pm

Re: Animation Duration

Post by pegassy »

I appreciate taking the time to explain all of that and also prepare the script completely. I will implement it and check the results.

Now that I have taken the continue button out entirely (since given the voice-overs, it is not very necessary) and dialogues transitioning from one to the other, the animation continuing to the next one may also become less of an issue. I think I will set the delay to 0.1 seconds and ensure that animations do not transition to the next sequence.

Thank you so much.
User avatar
Tony Li
Posts: 22057
Joined: Thu Jul 18, 2013 1:27 pm

Re: Animation Duration

Post by Tony Li »

Glad to help! Strings of Ambivalence is looking better every day.
pegassy
Posts: 135
Joined: Sat Mar 17, 2018 8:07 pm

Re: Animation Duration

Post by pegassy »

Thank you Tony. I really enjoy working on SOA. DSU is adding a great deal to this enjoyment. Many of the things DSU enables are things that I could only dream of doing a while ago.

I just noticed that if I leave the dialogue skip key enabled, it seems to bypass some of the sequence commands, namely the ones that I designated to work at a certain @time after that node begins, or the 0.5 second before the end of the node delay in the new modified script. This may end up causing the the animation to continue playing even after UCC is supposed to take over, so the character will be playing talking animation while walking.

I connected the talking animations back to idle, so it eventually turns to normal. But I was wondering if there was a workaround for this.

Thank you.
User avatar
Tony Li
Posts: 22057
Joined: Thu Jul 18, 2013 1:27 pm

Re: Animation Duration

Post by Tony Li »

Hi,

Yup, add the special keyword "required" in front of any sequencer commands that are required to run even if the player skips early.
Post Reply