Subtitle Panel Yield To Menu Panel

Announcements, support questions, and discussion for the Dialogue System.
VoodooDetective
Posts: 222
Joined: Wed Jan 22, 2020 10:48 pm

Subtitle Panel Yield To Menu Panel

Post by VoodooDetective »

What do you think the best way is to have the subtitle panels disappear whenever the menu panel wants to come out? Ideally, each panel would wait for the other to finish disappearing before appearing itself.
User avatar
Tony Li
Posts: 21987
Joined: Thu Jul 18, 2013 1:27 pm

Re: Subtitle Panel Yield To Menu Panel

Post by Tony Li »

Hi,

In version 2.2.14, I added Wait To Close checkboxes to StandardUISubtitlePanel and StandardUIMenuPanel to do just that. Try setting the subtitle panels' Visibility to Only During Content. The only catch is that if two lines in a row use the same subtitle panel, the subtitle panel will hide after the first line and then re-show to show the second line. If you don't want that to happen, set their Visibility to Until Superceded or one of the Always options, and configure the menu panel to hide them. You can configure the menu panel's OnOpen() UnityEvent to call the subtitle panels' Close() methods, or you can subclass StandardUIMenuPanel and override ShowResponses to close the subtitle panels.
VoodooDetective
Posts: 222
Joined: Wed Jan 22, 2020 10:48 pm

Re: Subtitle Panel Yield To Menu Panel

Post by VoodooDetective »

Oh fantastic! How lucky :)

I set everything to "Until Superseded," and here's what I added to "ShowResponses:"

Code: Select all

        public override void ShowResponses(Subtitle subtitle, Response[] responses, Transform target)
        {
            // Close Open Subtitles
            StandardDialogueUI dialogueUI = ((StandardDialogueUI)DialogueManager.instance.dialogueUI);
            StandardUIDialogueControls dialogueControls = (StandardUIDialogueControls)dialogueUI.dialogueControls;
            foreach (StandardUISubtitlePanel subtitlePanel in dialogueControls.subtitlePanels)
            {
                switch (subtitlePanel.panelState)
                {
                    case PanelState.Open:
                    case PanelState.Opening:
                        subtitlePanel.Close();
                        break;
                }
            }
            base.ShowResponses(subtitle, responses, target);
        }
 

I'm running into two strange behaviors after those checkboxes were checked (so without my code change):

- The dialogue audio plays a long time before the subtitle panel opens and I'm getting "Game object with animator is inactive" for my portrait animators for the first node of every character's dialogue (whenever whose speaking changes).
- When I reopen the same conversation, the audio plays immediately like before, only now there's a 5-10 second delay before the subtitle panel opens. (So this is really a variation of the symptoms from my first bullet.)

Could those checkboxes have introduced a regression there?
VoodooDetective
Posts: 222
Joined: Wed Jan 22, 2020 10:48 pm

Re: Subtitle Panel Yield To Menu Panel

Post by VoodooDetective »

OK, trying to summarize here:

8 Second Delay Issue
Looking at the code, I see an 8 second "safe guard" timeout, so that's got to be that delay I noticed. Looks like the NPC Panel never has OnHidden called.

I think the DialoguePanel deactivates before the NPC panel has a chance to declare itself Closed. So if you have "Deactivate on Hidden" for the Dialogue Panel, I guess that represents an edge case that wasn't accounted for for the panel states maybe?

Sequences Run Right Away
The sequences are being run before the Subtitle panel has opened completely. If I have a 5 second animation to bring in the subtitle menu, then the audio starts immediately, and 5 seconds later the typing begins.

"Game object with animator is inactive"
I'm still not 100% sure why this is happening. I'm guessing because the sequence runs immediately, and maybe the portrait animator isn't setup until the animation finishes or something?
User avatar
Tony Li
Posts: 21987
Joined: Thu Jul 18, 2013 1:27 pm

Re: Subtitle Panel Yield To Menu Panel

Post by Tony Li »

Hi,
8 Second Delay Issue
Can you make the dialogue panel's animation last the same duration or a little longer? This way the dialogue panel be able to stay active while its subtitle panels are hiding.

Alternatively, you can subclass and override StandardDialogueUI.Close and wait until AreAnyPanelsClosing() is false. In fact, I think I'll add that as an option in the next version and make it the default.

Sequences Run Right Away
That's correct. Sequences run as soon as the conversation gets to that dialogue entry node. Sequences kind of run in parallel to dialogue UI operations. The typewriter effect, on the other hand, is part of the dialogue UI, so it can be configured to wait until the panel is done showing. You could delay your sequencer commands, for example setting the Dialogue Manager's Default Sequence AudioWait(entrytag)@5 instead of AudioWait(entrytag). Or even set it to listen for a sequencer command -- AudioWait(entrytag)@Message(SubtitlePanelAppeared) -- and send that message when your subtitle panel is visible (e.g., when panelState is PanelState.Open).
VoodooDetective
Posts: 222
Joined: Wed Jan 22, 2020 10:48 pm

Re: Subtitle Panel Yield To Menu Panel

Post by VoodooDetective »

Fantastic! That fixed it for me. The animator issue was my own fault.

For anyone else looking into this, here's the code I have to signal the subtitle panel is open. You have to wait to the end of the frame to fire it:

Dialogue Panel Close Before Sub-Panel State Updated Fix

Code: Select all

        public override void Close()
        {
            // Wait for sub-panels to close before closing
            StartCoroutine(CloseRoutine(base.Close));
        }

        private IEnumerator CloseRoutine(System.Action closeLambda)
        {
            float safeguardTime = Time.realtimeSinceStartup + 8f;
            while (AreAnyPanelsClosing() && Time.realtimeSinceStartup < safeguardTime)
            {
                yield return null;
            }
            closeLambda?.Invoke();
        }

        private bool AreAnyPanelsClosing()
        {
            StandardDialogueUI dialogueUI = ((StandardDialogueUI)DialogueManager.instance.dialogueUI);
            StandardUIDialogueControls dialogueControls = (StandardUIDialogueControls)dialogueUI.dialogueControls;
            foreach (var panel in dialogueControls.subtitlePanels)
            {
                if (panel != null && panel.panelState == UIPanel.PanelState.Closing) return true;
            }
            foreach (var panel in dialogueControls.menuPanels)
            {
                if (panel != null && panel.panelState == UIPanel.PanelState.Closing) return true;
            }
            return false;
        }
Sequencer Message to Sync Audio to Panel Open

Code: Select all

        // Constants
        private const string OpenSequencerMessage = "SubtitleOpened";

        // Properties
        private Coroutine routine;

        protected override void OnDisable()
        {
            base.OnDisable();
            if (routine != null) DialogueManager.instance.StopCoroutine(routine); // Safety valve
        }

        public override void ShowSubtitle(Subtitle subtitle)
        {
            base.ShowSubtitle(subtitle);
            routine = DialogueManager.instance.StartCoroutine(SequencerMessageOnOpen());
        }

        private IEnumerator SequencerMessageOnOpen()
        {
            while (!hasFocus && panelState != PanelState.Open)
            {
                yield return null;
            }
            yield return new WaitForEndOfFrame();
            Sequencer.Message(OpenSequencerMessage);
            yield break;
        }
Thanks so much for all the help!
User avatar
Tony Li
Posts: 21987
Joined: Thu Jul 18, 2013 1:27 pm

Re: Subtitle Panel Yield To Menu Panel

Post by Tony Li »

Thanks for sharing! I think that's going to be helpful for a lot of people.
VoodooDetective
Posts: 222
Joined: Wed Jan 22, 2020 10:48 pm

Re: Subtitle Panel Yield To Menu Panel

Post by VoodooDetective »

As it turns out, this isn't a terribly robust solution. If the player tries to open another conversation while this one is closing, it causes issues.

This solution works only if you prevent the player from starting conversations while this is still "closing."


Ahh, another workaround:

Code: Select all

    public class CUSTOMDialoguePanel : UIPanel
    {
        // Properties
        private Coroutine routine;

        public override void Open()
        {
            // Stop trying to close if we got a request to open
            if (routine != null) StopCoroutine(routine);
            base.Open();
        }

        public override void Close()
        {
            // Wait for sub-panels to close before closing
            // TODO: wait to see how this resolves: https://www.pixelcrushers.com/phpbb/viewtopic.php?f=3&t=3917&p=21721#p21721
            routine = StartCoroutine(CloseRoutine(base.Close));
        }

        private IEnumerator CloseRoutine(System.Action closeLambda)
        {
            float safeguardTime = Time.realtimeSinceStartup + 8f;
            while (AreAnyPanels(UIPanel.PanelState.Closing) && Time.realtimeSinceStartup < safeguardTime)
            {
                yield return null;
            }
            closeLambda?.Invoke();
        }

        private bool AreAnyPanels(params UIPanel.PanelState[] panelStates)
        {
            StandardDialogueUI dialogueUI = ((StandardDialogueUI)DialogueManager.instance.dialogueUI);
            StandardUIDialogueControls dialogueControls = (StandardUIDialogueControls)dialogueUI.dialogueControls;
            foreach (var panel in dialogueControls.subtitlePanels)
            {
                if (panel != null)
                {
                    foreach (UIPanel.PanelState state in panelStates)
                    {
                        if (panel.panelState == state) return true;
                    }
                }
            }
            foreach (var panel in dialogueControls.menuPanels)
            {
                foreach (UIPanel.PanelState state in panelStates)
                {
                    if (panel.panelState == state) return true;
                }
            }
            return false;
        }
    }

Code: Select all

    public class CUSTOMSubtitlePanel : StandardUISubtitlePanel
    {
        // Constants
        private const string OpenSequencerMessage = "SubtitleOpened";

        // Properties
        private Coroutine routine;

        protected override void OnDisable()
        {
            base.OnDisable();
            // if (routine != null) DialogueManager.instance.StopCoroutine(routine); // Safety valve
        }

        public override void ShowSubtitle(Subtitle subtitle)
        {
            routine = DialogueManager.instance.StartCoroutine(SequencerMessageOnOpen());
            base.ShowSubtitle(subtitle);
        }

        private IEnumerator SequencerMessageOnOpen()
        {
            while (!hasFocus && panelState != PanelState.Open)
            {
                yield return null;
                // Debug.Log("waiting");
            }
            yield return new WaitForEndOfFrame();
            Sequencer.Message(OpenSequencerMessage);
            yield break;
        }
    }
User avatar
Tony Li
Posts: 21987
Joined: Thu Jul 18, 2013 1:27 pm

Re: Subtitle Panel Yield To Menu Panel

Post by Tony Li »

What version of the Dialogue System are you on? The most recent version has additional Wait For Close checkboxes on subtitle panels. And the upcoming version 2.2.15 has a Wait For Close checkbox on the Standard Dialogue UI component itself that waits for all subtitle panels and menu panels to close before closing itself. Would that address your needs?
VoodooDetective
Posts: 222
Joined: Wed Jan 22, 2020 10:48 pm

Re: Subtitle Panel Yield To Menu Panel

Post by VoodooDetective »

So if I'm understanding correctly, 2.2.15 will incorporate "Wait for Close to Open" into the DialoguePanel. What I've created here is "Wait for Close to Close" so it's a little different.


The two issues I ran into yesterday were:

1) The CUSTOMDialoguePanel needed to stop the "close" coroutine if Open() is called while it's waiting to close

Code: Select all

            // Stop trying to close if we got a request to open
            if (routine != null) StopCoroutine(routine);
2) The CUSTOMSubtitlePanel previously could cancel the sequencer message coroutine if OnDisable happened. But it's possible for OnDisable to be called after ShowSubtitle so I had to remove that safety valve.

Code: Select all

// if (routine != null) DialogueManager.instance.StopCoroutine(routine); // Safety valve
Post Reply