From 485af3610f3c32aa3d19e29d26e428650323cbd8 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Thu, 5 Jan 2023 16:51:43 +0000 Subject: [PATCH] Typewriter text widget can now insert pauses between sentences in a single block --- .../Private/StevesUI/TypewriterTextWidget.cpp | 26 ++++++++++++++++++- .../Public/StevesUI/TypewriterTextWidget.h | 13 +++++++--- doc/TypewriterText.md | 1 + 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index ef1723a..97a00fb 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -82,6 +82,7 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed) MaxLetterIndex = 0; NumberOfLines = 0; CombinedTextHeight = 0; + CurrentPlaySpeed = Speed; Segments.Empty(); CachedSegmentText.Empty(); @@ -110,7 +111,7 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed) FTimerDelegate Delegate; Delegate.BindUObject(this, &ThisClass::PlayNextLetter); - TimerManager.SetTimer(LetterTimer, Delegate, LetterPlayTime/Speed, true); + TimerManager.SetTimer(LetterTimer, Delegate, LetterPlayTime/CurrentPlaySpeed, true); SetVisibility(ESlateVisibility::SelfHitTestInvisible); } @@ -139,6 +140,14 @@ void UTypewriterTextWidget::PlayNextLetter() CalculateWrappedString(); } + // Incorporate pauses as a multiple of play timer (may not be exact but close enough) + if (PauseTime > 0) + { + PauseTime -= LetterPlayTime/CurrentPlaySpeed; + if (PauseTime > 0) + return; + } + FString WrappedString = CalculateSegments(); // TODO: How do we keep indexing of text i18n-friendly? @@ -169,6 +178,11 @@ void UTypewriterTextWidget::PlayNextLetter() } } +bool UTypewriterTextWidget::IsSentenceTerminator(TCHAR Letter) +{ + return Letter == '.' || Letter == '!' || Letter == '?'; +} + void UTypewriterTextWidget::CalculateWrappedString() { // Rich Text views give you: @@ -300,6 +314,16 @@ FString UTypewriterTextWidget::CalculateSegments() Result += Segment.Text.Mid(0, LettersLeft); + // Add pause for sentence ends + if (Result.Len() > 0 && + IsSentenceTerminator(Result[Result.Len() - 1]) && + CurrentLetterIndex < MaxLetterIndex - 1) // Don't pause on the last letter, that's the end pause's job + { + // Look ahead to make sure we only pause on LAST sentence terminator in a chain of them + if (LettersLeft == Segment.Text.Len() || !IsSentenceTerminator(Segment.Text[LettersLeft])) + PauseTime = PauseTimeAtSentenceTerminators; + } + if (!Segment.RunInfo.Name.IsEmpty()) { Result += TEXT(""); diff --git a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h index 3baf640..2abdabd 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h +++ b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h @@ -61,15 +61,19 @@ public: UPROPERTY(BlueprintReadOnly, meta = (BindWidget)) URichTextBlockForTypewriter* LineText; - // The amount of time between printing individual letters (for the "typewriter" effect). + /// The amount of time between printing individual letters (for the "typewriter" effect). UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter") float LetterPlayTime = 0.025f; - // The amount of time to wait after finishing the line before actually marking it completed. - // This helps prevent accidentally progressing dialogue on short lines. + /// The amount of time to wait after finishing the line before actually marking it completed. + /// This helps prevent accidentally progressing dialogue on short lines. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter") float EndHoldTime = 0.15f; + /// How long to pause at sentence terminators ('.', '!', '?') before proceeding (ignored if at end of text) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter") + float PauseTimeAtSentenceTerminators = 0.5f; + /// Set Text immediately UFUNCTION(BlueprintCallable) void SetText(const FText& InText); @@ -100,6 +104,7 @@ protected: private: void PlayNextLetter(); + static bool IsSentenceTerminator(TCHAR Letter); void CalculateWrappedString(); FString CalculateSegments(); @@ -130,4 +135,6 @@ private: uint32 bHasFinishedPlaying : 1; FTimerHandle LetterTimer; + float CurrentPlaySpeed = 1; + float PauseTime = 0; }; \ No newline at end of file diff --git a/doc/TypewriterText.md b/doc/TypewriterText.md index bb0025d..68dd680 100644 --- a/doc/TypewriterText.md +++ b/doc/TypewriterText.md @@ -14,6 +14,7 @@ skip to the end of the text if needed. height container, the height is correct before anything is played * Supports decorators like inline images * Respects explicit line breaks in your text +* Pauses on sentence terminators within a multi-sentence block (configurable) ## Usage