Pause between dialogs and skip button with typewriter
Posted: Thu Aug 22, 2019 8:07 am
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
CustomUISubtitlePanel - Inherits from the StandardUISubtitlePanel and attached to all panels
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();
}
}