Subtitle Panel Yield To Menu Panel
-
- Posts: 222
- Joined: Wed Jan 22, 2020 10:48 pm
Subtitle Panel Yield To Menu Panel
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.
Re: Subtitle Panel Yield To Menu Panel
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.
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.
-
- Posts: 222
- Joined: Wed Jan 22, 2020 10:48 pm
Re: Subtitle Panel Yield To Menu Panel
Oh fantastic! How lucky
I set everything to "Until Superseded," and here's what I added to "ShowResponses:"
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?
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?
-
- Posts: 222
- Joined: Wed Jan 22, 2020 10:48 pm
Re: Subtitle Panel Yield To Menu Panel
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?
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?
Re: Subtitle Panel Yield To Menu Panel
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).
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).
-
- Posts: 222
- Joined: Wed Jan 22, 2020 10:48 pm
Re: Subtitle Panel Yield To Menu Panel
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
Sequencer Message to Sync Audio to Panel Open
Thanks so much for all the help!
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;
}
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;
}
Re: Subtitle Panel Yield To Menu Panel
Thanks for sharing! I think that's going to be helpful for a lot of people.
-
- Posts: 222
- Joined: Wed Jan 22, 2020 10:48 pm
Re: Subtitle Panel Yield To Menu Panel
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:
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;
}
}
Re: Subtitle Panel Yield To Menu Panel
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?
-
- Posts: 222
- Joined: Wed Jan 22, 2020 10:48 pm
Re: Subtitle Panel Yield To Menu Panel
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
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.
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);
Code: Select all
// if (routine != null) DialogueManager.instance.StopCoroutine(routine); // Safety valve