From 4053a67c665a71e2ffb9de0fff843d8cc3fecbeb Mon Sep 17 00:00:00 2001 From: Josh Guerrero Date: Mon, 29 Jul 2024 10:34:46 -0700 Subject: [PATCH 1/2] Implemented DismissOptions method in DialogueViewBase --- Runtime/DialogueRunner.cs | 74 ++++++++++++++++++++++++++----- Runtime/Views/DialogueViewBase.cs | 46 +++++++++++++++++++ 2 files changed, 110 insertions(+), 10 deletions(-) diff --git a/Runtime/DialogueRunner.cs b/Runtime/DialogueRunner.cs index 6b385f7e..e8f61d2e 100644 --- a/Runtime/DialogueRunner.cs +++ b/Runtime/DialogueRunner.cs @@ -608,8 +608,6 @@ public void SetDialogueViews(DialogueViewBase[] views) /// private readonly HashSet ActiveDialogueViews = new HashSet(); - Action selectAction; - /// /// The underlying object that executes Yarn instructions /// and provides lines, options and commands. @@ -741,7 +739,6 @@ Dialogue CreateDialogueInstance() PrepareForLinesHandler = PrepareForLines }; - selectAction = SelectedOption; return dialogue; } @@ -803,7 +800,7 @@ void HandleOptionsInternal() IsAvailable = options.Options[i].IsAvailable, }; } - + // Don't allow selecting options on the same frame that we // provide them IsOptionSelectionAllowed = false; @@ -812,7 +809,8 @@ void HandleOptionsInternal() { if (dialogueView == null || dialogueView.isActiveAndEnabled == false) continue; - dialogueView.RunOptions(optionSet, selectAction); + dialogueView.RunOptions(optionSet, + selectedOptionIndex => DialogueViewCompletedOptions(selectedOptionIndex, dialogueView)); } IsOptionSelectionAllowed = true; @@ -1015,10 +1013,6 @@ void InterruptLine() /// Action{int})"/> is in the middle of being called. void SelectedOption(int optionIndex) { - if (IsOptionSelectionAllowed == false) { - throw new InvalidOperationException("Selecting an option on the same frame that options are provided is not allowed. Wait at least one frame before selecting an option."); - } - // Mark that this is the currently selected option in the // Dialogue Dialogue.SetSelectedOption(optionIndex); @@ -1041,7 +1035,7 @@ void SelectedOption(int optionIndex) { ContinueDialogue(); } - + } private static IEnumerator WaitForYieldInstruction(YieldInstruction yieldInstruction, Action onSuccessfulDispatch) @@ -1087,6 +1081,16 @@ private void DialogueViewCompletedInterrupt(DialogueViewBase dialogueView) } } + private void DialogueViewCompletedOptions(int selectedOptionIndex, DialogueViewBase dialogueView) + { + if (IsOptionSelectionAllowed == false) + { + throw new InvalidOperationException("Selecting an option on the same frame that options are provided is not allowed. Wait at least one frame before selecting an option."); + } + + DismissOptionsFromViews(selectedOptionIndex, dialogueViews); + } + void ContinueDialogue(bool dontRestart = false) { if (dontRestart == true) @@ -1176,6 +1180,56 @@ private void DialogueViewCompletedDismissal(DialogueViewBase dialogueView) ContinueDialogue(); } } + + private void DismissOptionsFromViews(int selectedOptionIndex, IEnumerable dialogueViews) + { + ActiveDialogueViews.Clear(); + + foreach (var dialogueView in dialogueViews) + { + // Skip any dialogueView that is null or not enabled + if (dialogueView == null || dialogueView.isActiveAndEnabled == false) + { + continue; + } + + // we do this in two passes - first by adding each + // dialogueView into ActiveDialogueViews, then by asking + // them to dismiss the line - because calling + // view.DismissLine might immediately call its completion + // handler (which means that we'd be repeatedly returning + // to zero active dialogue views, which means + // DialogueViewCompletedDismissal will mark the line as + // entirely done) + ActiveDialogueViews.Add(dialogueView); + } + + foreach (var dialogueView in dialogueViews) + { + if (dialogueView == null || dialogueView.isActiveAndEnabled == false) + { + continue; + } + + dialogueView.DismissOptions(selectedOptionIndex, + () => DialogueViewCompletedOptionsDismissal(selectedOptionIndex, dialogueView)); + } + } + + private void DialogueViewCompletedOptionsDismissal(int optionIndex, DialogueViewBase dialogueView) + { + // A dialogue view just completed dismissing its options. Remove + // it from the set of active views. + ActiveDialogueViews.Remove(dialogueView); + + // Have all of the views completed dismissal? + if (ActiveDialogueViews.Count == 0) + { + // Then we're ready to continue to the next piece of + // content. + SelectedOption(optionIndex); + } + } #endregion /// diff --git a/Runtime/Views/DialogueViewBase.cs b/Runtime/Views/DialogueViewBase.cs index 2cfb8d3b..15de8157 100644 --- a/Runtime/Views/DialogueViewBase.cs +++ b/Runtime/Views/DialogueViewBase.cs @@ -297,6 +297,52 @@ public virtual void RunOptions(DialogueOption[] dialogueOptions, Action onO // The default implementation does nothing. } + /// + /// Called by the to signal that the view + /// should dismiss its current options from display, and clean up. + /// + /// + /// + /// This method is called when a Dialogue View attached to a Dialogue + /// Runner reports that an option has been selected. When this occurs, + /// the Dialogue Runner calls on all + /// Dialogue Views to tell them to clear their current options from + /// display. + /// Depending on how the Dialogue View presents options, + /// "dismissing" them may mean different things. For example, a + /// Dialogue View that presents on-screen text might fade the text away, + /// or a Dialogue View that presents voice-over dialogue may not need to + /// do anything at all (because audio finished playing when the line(s) + /// finished presenting.) + /// + /// + /// Dismissing the options can take time, but should ideally be as fast as + /// possible, because the user will be waiting for the next piece of + /// content to appear. + /// + /// When the options have finished dismissing, this method calls + /// onDismissalComplete to indicate that the dismissal is complete. When + /// all Dialogue Views on a Dialogue Runner have finished dismissing, + /// the Dialogue Runner moves on to the next piece of content. + /// + /// + /// The default implementation of this method immediately calls the + /// method (that is, it reports + /// that it has finished dismissing the options the moment that it receives + /// it), and otherwise does nothing. + /// + /// + /// The index of the option that was + /// selected by the user. + /// The method that should be called + /// when the view has finished dismissing the line. + public virtual void DismissOptions(int selectedOptionIndex, Action onDismissalComplete) + { + // The default implementation does nothing, and immediately calls + // onDismissalComplete. + onDismissalComplete?.Invoke(); + } + /// /// Called by the to signal that the /// dialogue has ended, and no more lines will be delivered. From 1eb26aa3da0b5e46b1e05ec782f174c92629ebf9 Mon Sep 17 00:00:00 2001 From: Josh Guerrero Date: Mon, 29 Jul 2024 10:43:26 -0700 Subject: [PATCH 2/2] Updated changelog, contributors list --- CHANGELOG.md | 3 +++ CONTRIBUTORS.md | 1 + 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dfaf396..1e855e2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added +- Added DismissOptions method to DialogueViewBase + - Inheriting classes can implement this method to respond to option selections from other Dialogue Views + ### Changed - Fixed an issue where, on Windows, projects would fail to automatically update when a file that belonged to them was created or edited. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index da277057..aa63bc63 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -17,3 +17,4 @@ The following people have contributed to the development of Yarn Spinner. If you * 2023: Mitch Zais * 2023: Thomas Ingram (https://vertx.xyz) * 2023: Isaac Berman (https://github.com/bermanisaac) +* 2024: Josh Guerrero (https://github.com/joshgrrro)