OK great! So would your recommendation be to comment out this line in UIAnimatorMonitor:
Code: Select all
CheckAnimatorModeAndTimescale(triggerName);
in WaitForAnimation()?
EDIT:
Huh, even if I do that and it uses Normal time, I'm having that issue pop up. I'm sending the Sequencer message from a coroutine that starts in ShowSubtitle and waits for the panel to open:
Code: Select all
private IEnumerator OnPanelOpen(params Action[] onOpenCallbacks)
{
while (!hasFocus && panelState != PanelState.Open)
{
yield return null;
}
yield return waitForEndOfFrame;
foreach (Action action in onOpenCallbacks) action?.Invoke();
}
The sequencer message is one of the callbacks.
Edit 2:
Ahh, looks like there's a safety valve kind of thing in StandardUISubtitlePanel:
Code: Select all
protected IEnumerator FocusWhenOpen()
{
float timeout = Time.realtimeSinceStartup + 5f;
while (panelState != PanelState.Open && Time.realtimeSinceStartup < timeout)
{
yield return null;
}
m_focusWhenOpenCoroutine = null;
FocusNow();
}
That causes FocusNow() to be called even if the panel isn't open yet.
Edit 3:
AHHH, but even if I comment out the safety valve, the panel gets set to open before the animation has finished. Digging more.
Edit 4:
Ahhhhhh OK, so there's another place unscaled time is forced and also where there is another safety valve in UIAnimatorMonitor:
Code: Select all
private IEnumerator WaitForAnimation(string triggerName, System.Action callback, bool wait)
{
if (HasAnimator() && !string.IsNullOrEmpty(triggerName))
{
if (IsAnimatorValid())
{
// Run Animator and wait:
CheckAnimatorModeAndTimescale(triggerName);
m_animator.SetTrigger(triggerName);
currentTrigger = triggerName;
float timeout = Time.realtimeSinceStartup + MaxWaitDuration + 100000;
var goalHashID = Animator.StringToHash(triggerName);
var oldHashId = UIUtility.GetAnimatorNameHash(m_animator.GetCurrentAnimatorStateInfo(0));
var currentHashID = oldHashId;
if (wait)
{
while ((currentHashID != goalHashID) && (currentHashID == oldHashId) && (Time.realtimeSinceStartup < timeout))
{
yield return null;
currentHashID = IsAnimatorValid() ? UIUtility.GetAnimatorNameHash(m_animator.GetCurrentAnimatorStateInfo(0)) : 0;
}
if (Time.realtimeSinceStartup < timeout && IsAnimatorValid())
{
var clipLength = m_animator.GetCurrentAnimatorStateInfo(0).length;
if (Mathf.Approximately(0, Time.timeScale))
{
timeout = Time.realtimeSinceStartup + clipLength;
while (Time.realtimeSinceStartup < timeout)
{
yield return null;
}
}
else
{
yield return new WaitForSeconds(clipLength);
}
}
}
}
else if (m_animation != null && m_animation.enabled)
{
m_animation.Play(triggerName);
if (wait)
{
var clip = m_animation.GetClip(triggerName);
if (clip != null)
{
yield return new WaitForSeconds(clip.length);
}
}
}
}
currentTrigger = string.Empty;
m_coroutine = null;
if (callback != null) callback.Invoke();
}
If I comment out all the safety valves, the code that forces unscaled time, AND set all animators to normal, then the issue goes away. But that seems like a pretty heavy handed approach. Do you have any better ideas?
I wonder if this animation functionality could be abstracted out into an interface that could have this stuff as the default implementation, and then allow folks to implement their own. Like with a library like animancer or something. Still digging...
Edit 5:
OK, so you may have a better idea, but what would you think of:
Code: Select all
namespace PixelCrushers
{
public interface IUIAnimatorMonitor
{
string currentTrigger { get; }
void SetTrigger(string triggerName, System.Action callback, bool wait = true);
void CancelCurrentAnimation();
}
}
and then in UIPanel:
Code: Select all
public virtual IUIAnimatorMonitor animatorMonitor
{
get
{
if (m_animatorMonitor == null) m_animatorMonitor = new UIAnimatorMonitor(gameObject);
return m_animatorMonitor;
}
}
and then in UIAnimatorMonitor:
Code: Select all
public class UIAnimatorMonitor : IUIAnimatorMonitor
and then finally in StandardUISubtitlePanel:
Code: Select all
protected IEnumerator FocusWhenOpen()
{
// float timeout = Time.realtimeSinceStartup + 5f;
while (panelState != PanelState.Open)// && Time.realtimeSinceStartup < timeout)
{
yield return null;
}
m_focusWhenOpenCoroutine = null;
FocusNow();
}