Typewriter text widget can now insert pauses between sentences in a single block

This commit is contained in:
Steve Streeting 2023-01-05 16:51:43 +00:00
parent 5829ce6cc4
commit 485af3610f
3 changed files with 36 additions and 4 deletions

View File

@ -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("</>");

View File

@ -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;
};

View File

@ -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