Animations interrupted when moving to a new dialogue node
-
- Posts: 195
- Joined: Mon Jul 01, 2019 1:21 pm
Animations interrupted when moving to a new dialogue node
Hey Tony, just a question about using the Sequence field to play animations. I noticed that when I click through the dialogue nodes fast, the animation doesn't play in full, but seems to skip to its last frame. So for example, I have the following sequence command which controls a Stat bar.
AnimatorFloat(StatLevel, [var=Stat], StatBar, 0.75)
When playing normally, it plays smoothly between the start value and end value, but when clicking through fast, it jumps straight to the end value.
Is there a way to tell DS to play the animation in full despite moving to the next node?
AnimatorFloat(StatLevel, [var=Stat], StatBar, 0.75)
When playing normally, it plays smoothly between the start value and end value, but when clicking through fast, it jumps straight to the end value.
Is there a way to tell DS to play the animation in full despite moving to the next node?
Re: Animations interrupted when moving to a new dialogue node
Hi,
Yes, you can write a custom sequencer command to do that. Most of the built-in commands attempt to put the command in its final state when you skip ahead, instead of leaving it halfway done. However, some commands, like Audio() [versus AudioWait()] let the action continue even if the player skips ahead.
Yes, you can write a custom sequencer command to do that. Most of the built-in commands attempt to put the command in its final state when you skip ahead, instead of leaving it halfway done. However, some commands, like Audio() [versus AudioWait()] let the action continue even if the player skips ahead.
-
- Posts: 195
- Joined: Mon Jul 01, 2019 1:21 pm
Re: Animations interrupted when moving to a new dialogue node
Thanks, that sounds good. So just to check - using a custom sequencer, it will be possible to have the animations not be interrupted by advancing to the next node, but still have them play in full (as in reach their final state)? It's not gonna leave them halfway done if I do it that way?
Re: Animations interrupted when moving to a new dialogue node
It depends. For example, AnimatorPlay() starts playing an animator state but doesn't make it jump to the end if you skip ahead. So if one node has this Sequence:
And the next node has this Sequence:
Then as soon as the player skips to the next node it will immediately switch to the Idle animation even if Dance isn't done yet.
But let's say you write a custom sequencer command named UninterrupibleAnimatorFloat that you can use like this:
This command should do it:
SequencerCommandUninterruptibleAnimatorFloat.cs
Code: Select all
AnimatorPlay(Dance)
Code: Select all
AnimatorPlay(Idle)
But let's say you write a custom sequencer command named UninterrupibleAnimatorFloat that you can use like this:
Code: Select all
UninterruptibleAnimatorFloat(StatLevel, [var=Stat], StatBar, 0.75)
SequencerCommandUninterruptibleAnimatorFloat.cs
Code: Select all
using System.Collections;
using UnityEngine;
namespace PixelCrushers.DialogueSystem.SequencerCommands
{
[AddComponentMenu("")] // Hide from menu.
public class SequencerCommandUninterruptibleAnimatorFloat : SequencerCommand
{
public void Start()
{
// Get the values of the parameters:
string animatorParameter = GetParameter(0);
int animatorParameterHash = Animator.StringToHash(animatorParameter);
float targetValue = GetParameterAsFloat(1, 1);
Transform subject = GetSubject(2);
float duration = GetParameterAsFloat(3, 0);
// Check the parameters:
if (subject == null)
{
if (DialogueDebug.logWarnings) Debug.LogWarning(string.Format("{0}: Sequencer: AnimatorFloat(): subject '{1}' wasn't found.", new System.Object[] { DialogueDebug.Prefix, GetParameter(2) }));
}
else
{
var animator = subject.GetComponentInChildren<Animator>();
if (animator == null)
{
if (DialogueDebug.logWarnings) Debug.LogWarning(string.Format("{0}: Sequencer: AnimatorFloat(): no Animator found on '{1}'.", new System.Object[] { DialogueDebug.Prefix, subject.name }));
}
else
{
if (DialogueDebug.logInfo) Debug.Log(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}: Sequencer: AnimatorFloat({1}, {2}, {3}, {4})", new System.Object[] { DialogueDebug.Prefix, animatorParameter, targetValue, subject, duration }));
DialogueManager.instance.StartCoroutine(LerpAnimatorFloat(animator, animatorParameterHash, targetValue, duration));
}
}
Stop();
}
// This method is static so it can run independently of the sequencer command.
// it runs through the Dialogue Manager GameObject's DialogueSystemController component.
private static IEnumerator LerpAnimatorFloat(Animator animator, int animatorParameterHash, float targetValue, float duration)
{
var originalValue = animator.GetFloat(animatorParameterHash);
var startTime = DialogueTime.time;
var elapsed = 0f;
while (elapsed < duration)
{
elapsed = (DialogueTime.time - startTime) / duration;
float current = Mathf.Lerp(originalValue, targetValue, elapsed / duration);
if (animator != null) animator.SetFloat(animatorParameterHash, current);
yield return null;
}
if (animator != null) animator.SetFloat(animatorParameterHash, targetValue);
}
}
}
-
- Posts: 195
- Joined: Mon Jul 01, 2019 1:21 pm
Re: Animations interrupted when moving to a new dialogue node
That works perfectly, thanks so much Tony (:
Re: Animations interrupted when moving to a new dialogue node
It sounds like you're experiencing an issue where the animation doesn't play fully when you click through the dialogue nodes quickly. This behavior is expected because the Dialogue System (DS) typically moves to the next node as soon as it receives input.
To ensure that the animation plays in full before moving to the next node, you can use a coroutine to control the flow of the dialogue. Here's an example of how you can modify your code to achieve this:
csharp
Copy code
using UnityEngine;
using System.Collections;
using PixelCrushers.DialogueSystem;
public class YourDialogueClass : MonoBehaviour
{
private bool isAnimating = false;
// Your code to initiate the dialogue goes here.
private IEnumerator PlayAnimationCoroutine(string stat, GameObject statBar, float duration)
{
// Check if an animation is already playing.
if (isAnimating)
{
// Wait until the previous animation finishes before starting a new one.
yield return new WaitUntil(() => !isAnimating);
}
// Set the flag to indicate that an animation is playing.
isAnimating = true;
// Play the animation using the Sequence field.
DialogueLua.SetVariable("Stat", stat);
DialogueManager.PlaySequence("AnimatorFloat(StatLevel, [var=Stat], " + statBar.name + ", " + duration + ")");
// Wait for the animation to complete.
yield return new WaitForSeconds(duration);
// Reset the flag to indicate that the animation has finished.
isAnimating = false;
// Move to the next node in the dialogue.
DialogueManager.DialogueContinue();
}
public void PlayAnimation(string stat, GameObject statBar, float duration)
{
StartCoroutine(PlayAnimationCoroutine(stat, statBar, duration));
}
}
In this example, we introduce a boolean flag called isAnimating to keep track of whether an animation is currently playing. When the PlayAnimationCoroutine is called, it first checks if an animation is already playing. If so, it waits until the previous animation finishes before starting a new one.
The animation is played using DialogueManager.PlaySequence as before. After the animation, the flag is reset, and then DialogueManager.DialogueContinue is called to move to the next node in the dialogue.
By using coroutines and the yield return statements, we ensure that the animation completes before proceeding to the next dialogue node, regardless of how fast you click through the dialogue.
Remember to attach this script to a GameObject in your scene and call the PlayAnimation method whenever you want to play an animation during the dialogue.
To ensure that the animation plays in full before moving to the next node, you can use a coroutine to control the flow of the dialogue. Here's an example of how you can modify your code to achieve this:
csharp
Copy code
using UnityEngine;
using System.Collections;
using PixelCrushers.DialogueSystem;
public class YourDialogueClass : MonoBehaviour
{
private bool isAnimating = false;
// Your code to initiate the dialogue goes here.
private IEnumerator PlayAnimationCoroutine(string stat, GameObject statBar, float duration)
{
// Check if an animation is already playing.
if (isAnimating)
{
// Wait until the previous animation finishes before starting a new one.
yield return new WaitUntil(() => !isAnimating);
}
// Set the flag to indicate that an animation is playing.
isAnimating = true;
// Play the animation using the Sequence field.
DialogueLua.SetVariable("Stat", stat);
DialogueManager.PlaySequence("AnimatorFloat(StatLevel, [var=Stat], " + statBar.name + ", " + duration + ")");
// Wait for the animation to complete.
yield return new WaitForSeconds(duration);
// Reset the flag to indicate that the animation has finished.
isAnimating = false;
// Move to the next node in the dialogue.
DialogueManager.DialogueContinue();
}
public void PlayAnimation(string stat, GameObject statBar, float duration)
{
StartCoroutine(PlayAnimationCoroutine(stat, statBar, duration));
}
}
In this example, we introduce a boolean flag called isAnimating to keep track of whether an animation is currently playing. When the PlayAnimationCoroutine is called, it first checks if an animation is already playing. If so, it waits until the previous animation finishes before starting a new one.
The animation is played using DialogueManager.PlaySequence as before. After the animation, the flag is reset, and then DialogueManager.DialogueContinue is called to move to the next node in the dialogue.
By using coroutines and the yield return statements, we ensure that the animation completes before proceeding to the next dialogue node, regardless of how fast you click through the dialogue.
Remember to attach this script to a GameObject in your scene and call the PlayAnimation method whenever you want to play an animation during the dialogue.
Re: Animations interrupted when moving to a new dialogue node
Thanks for sharing your code and explanation!