Page 1 of 1

Pause between dialogs and skip button with typewriter

Posted: Thu Aug 22, 2019 8:07 am
by nicmar
I posted in some other threads about this, but since I never got the code from them to really work, I've been trying for several hours to make my own solution.

It doesn't work really well, and I'd love some feedback on how to improve it.

I'm using a Basic Standard Dialogue for "radio messages" talking in a box, and Standard UI Subtitle Panel for speech bubbles. Here's what I want to do:

1) Show a message using the typewriter, when it's complete, wait for X seconds, then hide the messagebox, wait for Y seconds, then show the next message.
2) If the next message is from the same Actor, don't hide the messagebox in between. I might want this differently depending on the situation, so if this could be a setting/sequence in a conversation, that would be great.
3) If I click the skip-button when it's still typing, have it show all text, and while waiting for X seconds, I can click again, to skip to the end

X is maybe 1 second and Y is maybe 0.5 seconds. I just don't like the next bubble coming up while the first one is closing. It gives a little stress to the conversation.

Some things I've noticed:
- GetDefaultSubtitleDuration from ConversationView doesn't count any PauseCharacters, causing the Typewriter not to finish before it closes. I removed PauseDurations in my tests, so that wouldn't affect what's happening.
- It seems like the timer in the Coroutine DelayHideSubtitle never finishes, and that the dialogue continues anyway. It seems like dialogue continue without caring about the StandardUISubtitlePanel HidePanel() method?
- I read about the methods in viewtopic.php?f=3&t=2251&start=10#p13820 but I don't think it's very user friendly to have a pause node between each, but is there any downsite to use the SetDialoguePanel method, as the default sequence, so it's on every conversation?

Maybe all of this work is pointless, and I should just focus on the SetDialoguePanel method instead.

Well, here's my code, if you have any feedback on it, but right now it just feels extremely complex compared to the SetDialoguePanel method. :)


CustomSkipButton - Placed on the DialogueManager

Code: Select all

using System;
using UnityEngine;
using System.Collections;
using PixelCrushers.DialogueSystem;
using Rewired;
using BlueGoo.Utils;

public class CustomSkipButton : MonoBehaviour {
	// Default values set before changing them

	[Header("Normal values")]
	[SerializeField] private float minSubtitleSeconds = 2;

	[SerializeField] private float subtitleCharsPerSec = 50;
	[SerializeField] private float typewriterCharsPerSec = 50;
	[SerializeField] private float fullPauseDuration = 1;
	[SerializeField] private float quarterPauseDuration = 0.25f;

	[Header("Fast forward values")]
	[SerializeField] private float skipMinSubtitleSeconds = 1;

	[SerializeField] private float skipSubtitleCharsPerSec = 100;
	[SerializeField] private float skipTypewriterCharsPerSec = 100;
	[SerializeField] private float skipFullPauseDuration = 0.5f;
	[SerializeField] private float skipQuarterPauseDuration = 0f;

	[SerializeField] private float durationToWaitWhenComplete = 2f;

	private StandardDialogueUI dialogueUI;
	private UnityUITypewriterEffect typewriter;

	private bool skipping;
	private Coroutine skipDialogueCoroutine;

	private void Awake() {
		dialogueUI = GetComponentInChildren<StandardDialogueUI>();
		typewriter = dialogueUI.conversationUIElements.defaultNPCSubtitlePanel.subtitleText.gameObject.GetComponent<UnityUITypewriterEffect>();
		if (typewriter == null) Debug.LogError("UnityUITypewriterEffect component missing");
	}

	public void SkipDialogue() {
		// If pressed twice, don't wait for typewriter to finish or its delay
		if (skipping) {
			Continue();
		}

		skipDialogueCoroutine = StartCoroutine(nameof(SkipDialogueSequence));
	}

	private void Continue() {
		skipping = false;
		dialogueUI.OnContinue();
		AbortSkipDialogueCoroutine();
	}

	private void AbortSkipDialogueCoroutine() {
		if (skipDialogueCoroutine != null) StopCoroutine(skipDialogueCoroutine);
	}
	
	IEnumerator SkipDialogueSequence() {
		skipping = true;

		FastForwardTypewriter();

		var timeout = Time.time + durationToWaitWhenComplete;

		while (skipping && Time.time < timeout) {
			yield return null;
		}

		if (skipping) {
			// Reset typewriter and close the dialogue (Only if it hasn't been closed already)
			ResetTypewriter();
			Continue();
		}
	}

	void FastForwardTypewriter() {
		Debug.Log(Time.time.ToString("F1") + ": <color=yellow>FastForward Typewriter (Wait 2 sec)</color>");

		// Set values to fast forward:
		typewriter.charactersPerSecond = skipTypewriterCharsPerSec;
		typewriter.fullPauseDuration = skipFullPauseDuration;
		typewriter.quarterPauseDuration = skipQuarterPauseDuration;
		DialogueManager.DisplaySettings.subtitleSettings.subtitleCharsPerSecond = skipSubtitleCharsPerSec;
		DialogueManager.DisplaySettings.subtitleSettings.minSubtitleSeconds = skipMinSubtitleSeconds;
		// This seemed to caused the dialog to never continue, so i disabled it
		//DialogueManager.DisplaySettings.subtitleSettings.continueButton = DisplaySettings.SubtitleSettings.ContinueButtonMode.Never;
	}

	void ResetTypewriter() {
		// Set original values:
		typewriter.charactersPerSecond = typewriterCharsPerSec;
		typewriter.fullPauseDuration = fullPauseDuration;
		typewriter.quarterPauseDuration = quarterPauseDuration;
		DialogueManager.DisplaySettings.subtitleSettings.subtitleCharsPerSecond = subtitleCharsPerSec;
		DialogueManager.DisplaySettings.subtitleSettings.minSubtitleSeconds = minSubtitleSeconds;
		//DialogueManager.DisplaySettings.subtitleSettings.continueButton = DisplaySettings.SubtitleSettings.ContinueButtonMode.Always;
	}

	void OnConversationLine(Subtitle subtitle) {
		if (subtitle.dialogueEntry.DialogueText.Length == 0) return;
		ResetTypewriter();
	}


	void OnConversationEnd(Transform actor) {
		ResetTypewriter();
	}

}

CustomUISubtitlePanel - Inherits from the StandardUISubtitlePanel and attached to all panels

Code: Select all

using UnityEngine;
using PixelCrushers;
using PixelCrushers.DialogueSystem;
using System.Collections;

public class CustomUISubtitlePanel : StandardUISubtitlePanel {
	// Loosely based on QueueSubtitleDialogueUI from https://www.pixelcrushers.com/phpbb/viewtopic.php?f=3&t=2251#p12369

	public float hideSubtitleDelay = 1;

	private StandardUISubtitlePanel currentVisiblePanel = null;
	private bool showContinueButton = false;

	public override void ShowSubtitle(Subtitle subtitle) {
		base.ShowSubtitle(subtitle);
		/*
		// Here I was trying to figure out some code to 
		// 1) Find who the owner of this subtitle is talking to
		// 2) Get the StandardUISubtitlePanel from that person
		// 3) Compare it to the current open panel, and if it's the same hide, and then reopen it with the new dialogue (?)
		*/
	}

	public override void HideSubtitle(Subtitle subtitle) {
		// Ignore the START-node
		if (subtitle.dialogueEntry.DialogueText.Length == 0) return;

		StartCoroutine(DelayHideSubtitle(subtitle));
	}

	IEnumerator DelayHideSubtitle(Subtitle subtitle) {
		// Play subtitle close animation
		animatorMonitor.SetTrigger(hideAnimationTrigger, OnHidden, true);

		var timeout = Time.time + hideSubtitleDelay;

		while (Time.time < timeout) {
			Debug.Log(timeout - Time.time);
			yield return null;
		}

		base.HideSubtitle(subtitle);
	}

	public override void Close() {
		currentVisiblePanel = null;
		base.Close();
	}
}

Re: Pause between dialogs and skip button with typewriter

Posted: Thu Aug 22, 2019 8:33 am
by nicmar
I took a good long look at the Fade Between Example, and it seemed to work with both the "overhead" dialog and the bubbles with fading.

However, when I set Subtitle panel to "Use Bark UI" it didn't fade out between.

So perhaps something is messed up in my custom dialogs, and I will retry with your dialogs and then go from there. Hmm :)

Re: Pause between dialogs and skip button with typewriter

Posted: Thu Aug 22, 2019 8:54 am
by Tony Li
I haven't had a chance yet to look at your script, but I wanted to provide answers to these questions first:
nicmar wrote: Thu Aug 22, 2019 8:07 am- GetDefaultSubtitleDuration from ConversationView doesn't count any PauseCharacters, causing the Typewriter not to finish before it closes. I removed PauseDurations in my tests, so that wouldn't affect what's happening.
Run the string through UITools.StripRPGMakerCodes() first:

Code: Select all

float duration = conversationView.GetDefaultSubtitleDuration(UITools.StripRPGMakerCodes(s));
nicmar wrote: Thu Aug 22, 2019 8:07 am- It seems like the timer in the Coroutine DelayHideSubtitle never finishes, and that the dialogue continues anyway. It seems like dialogue continue without caring about the StandardUISubtitlePanel HidePanel() method?
That's correct. As soon as it tells the panel to hide, it allows the next subtitle to play.
nicmar wrote: Thu Aug 22, 2019 8:07 am- I read about the methods in viewtopic.php?f=3&t=2251&start=10#p13820 but I don't think it's very user friendly to have a pause node between each, but is there any downsite to use the SetDialoguePanel method, as the default sequence, so it's on every conversation?
You can set a Default Sequence for a single conversation. In the Dialogue Editor, inspect the conversation and click on blank canvas space to inspect the conversation's properties. In the inspector, tick Override Display Settings > Sequence Settings.

Re: Pause between dialogs and skip button with typewriter

Posted: Thu Aug 22, 2019 9:49 am
by nicmar
I'm not sure if I explained what I meant clearly. I found this when I tried to debug my code, and the subtitle closing prematurely. If you try to set a slow typewriter speed, a rather short text, and use pause characters and a pause delay, the typewriter may not finish, before the dialogue closes.

I'm not sure if pause characters are internally converted to RPGCodes, but take this example:

"Hello! This is a text! How fun!"
It's 31 characters including the !. With 30 chars/second, all characters would show in about a second, but using 1 second pause with the !, it should take 4 seconds instead. But the dialog closes after 2 seconds, which is the minSubtitleDuration.

With 10 chars/second, it would play in about 3 seconds plus 3 seconds for the pause. That's 6 seconds to type it all out, and the GetDefaultSubtitleDuration doesn't take the pause characters into account, so it will close after 3 seconds.

So the GetDefaultSubtitleDuration from ConversationView would use either the MinSubtitleDuration or the calculated, whichever is longer. And in case the text is short, with many pause characters, it might not display on the typewriter under 2 seconds, and the subtitle will close.

I'm not sure stripping the RPG codes would help this, I think the duration of all pause characters should be added to the total time.

Code: Select all

        /// <returns>A duration based on the text length and the Dialogue Manager's Subtitle Settings > Min Subtitle Seconds and Subtitle Chars Per Second.</returns>
        public float GetDefaultSubtitleDuration(string text)
        {
            if (overrideGetDefaultSubtitleDuration != null) return overrideGetDefaultSubtitleDuration(text);
            int numCharacters = string.IsNullOrEmpty(text) ? 0 : Tools.StripRichTextCodes(text).Length;
            Debug.Log("GetDefaultSubtitleDuration: <color=magenta>" + Mathf.Max(settings.GetMinSubtitleSeconds(), numCharacters / Mathf.Max(1, settings.GetSubtitleCharsPerSecond())) + "</color>"); 
            return Mathf.Max(settings.GetMinSubtitleSeconds(), numCharacters / Mathf.Max(1, settings.GetSubtitleCharsPerSecond()));
        }
I'm sorry if I am confused somehow, and it doesn't work like this, but this is my experience when testing with slow typewriter speeds.

Thanks! :)

Re: Pause between dialogs and skip button with typewriter

Posted: Thu Aug 22, 2019 10:07 am
by Tony Li
That's in 2.2.0. However, it assumes the default values for \. and \, (1 second and 0.25 second) since it doesn't know ahead of time which typewriter component it may need to use.

Re: Pause between dialogs and skip button with typewriter

Posted: Thu Aug 22, 2019 10:14 am
by nicmar
Cool, looking forward to test that out.