From f47178a49efebdf6935dcd83884cff3711da8169 Mon Sep 17 00:00:00 2001 From: Edward Rowe Date: Sun, 12 Jul 2020 23:09:17 -0400 Subject: [PATCH] Add support for printing sprite tags (fixes Issue #35) --- .../TextTyper/Examples/TextTyperTester.cs | 1 + Assets/RedBlueGames/TextTyper/RichTextTag.cs | 12 ++-- .../Tests/Editor/TextTagParserTests.cs | 12 ++++ .../RedBlueGames/TextTyper/TextTagParser.cs | 14 ++++ Assets/RedBlueGames/TextTyper/TextTyper.cs | 69 ++++++++++++------- 5 files changed, 78 insertions(+), 30 deletions(-) diff --git a/Assets/RedBlueGames/TextTyper/Examples/TextTyperTester.cs b/Assets/RedBlueGames/TextTyper/Examples/TextTyperTester.cs index 9acae5a..54bbae8 100644 --- a/Assets/RedBlueGames/TextTyper/Examples/TextTyperTester.cs +++ b/Assets/RedBlueGames/TextTyper/Examples/TextTyperTester.cs @@ -45,6 +45,7 @@ public void Start() dialogueLines.Enqueue("Hello! My name is... NPC. Got it, bub?"); dialogueLines.Enqueue("You can use uGUI text tag and color tag like this."); dialogueLines.Enqueue("bold text test bold text test"); + dialogueLines.Enqueue("Sprites!Isn't that neat?"); dialogueLines.Enqueue("You can size 40 and size 20"); dialogueLines.Enqueue("You can color tag like this."); dialogueLines.Enqueue("Sample Shake Animations: Light Rotation, Light Position, Full Shake\nSample Curve Animations: Slow Sine, Bounce Bounce, Crazy Flip"); diff --git a/Assets/RedBlueGames/TextTyper/RichTextTag.cs b/Assets/RedBlueGames/TextTyper/RichTextTag.cs index 573f497..7789fbe 100644 --- a/Assets/RedBlueGames/TextTyper/RichTextTag.cs +++ b/Assets/RedBlueGames/TextTyper/RichTextTag.cs @@ -13,7 +13,7 @@ public class RichTextTag private const char OpeningNodeDelimeter = '<'; private const char CloseNodeDelimeter = '>'; private const char EndTagDelimeter = '/'; - private const string ParameterDelimeter = "="; + private const char ParameterDelimeter = '='; /// /// Initializes a new instance of the class. @@ -54,12 +54,10 @@ public string TagType var tagType = this.TagText.Substring(1, this.TagText.Length - 2); tagType = tagType.TrimStart(EndTagDelimeter); - // Strip Parameter - var parameterDelimeterIndex = tagType.IndexOf(ParameterDelimeter); - if (parameterDelimeterIndex > 0) - { - tagType = tagType.Substring(0, parameterDelimeterIndex); - } + var tagEndDelimeters = new char[] { ' ', ParameterDelimeter }; + var delimeterIndex = tagType.IndexOfAny(tagEndDelimeters); + var tagEndIndex = delimeterIndex > 0 ? delimeterIndex : tagType.Length; + tagType = tagType.Substring(0, tagEndIndex); return tagType; } diff --git a/Assets/RedBlueGames/TextTyper/Tests/Editor/TextTagParserTests.cs b/Assets/RedBlueGames/TextTyper/Tests/Editor/TextTagParserTests.cs index b6939ff..cdab71b 100644 --- a/Assets/RedBlueGames/TextTyper/Tests/Editor/TextTagParserTests.cs +++ b/Assets/RedBlueGames/TextTyper/Tests/Editor/TextTagParserTests.cs @@ -96,5 +96,17 @@ public void RemoveUnityTags_AllUnityTags_ReturnsNoTags() Assert.AreEqual(expectedText, generatedText); } + + [Test] + public void RemoveUnityTags_SpriteTagWithValue_ReturnsTaglessText() + { + var builder = new System.Text.StringBuilder(); + var textToType = "This string has a sprite."; + var generatedText = TextTagParser.RemoveUnityTags(textToType); + + var expectedText = "This string has a sprite."; + + Assert.AreEqual(expectedText, generatedText); + } } } \ No newline at end of file diff --git a/Assets/RedBlueGames/TextTyper/TextTagParser.cs b/Assets/RedBlueGames/TextTyper/TextTagParser.cs index 80b5845..b6ef146 100644 --- a/Assets/RedBlueGames/TextTyper/TextTagParser.cs +++ b/Assets/RedBlueGames/TextTyper/TextTagParser.cs @@ -167,6 +167,20 @@ public bool IsTag } } + /// + /// Gets a value indicating this Symbol represents a Sprite, which is treated + /// as a visible character by TextMeshPro. + /// See Issue #35 for details. + /// + /// + public bool IsReplacedWithSprite + { + get + { + return this.IsTag && this.Tag.TagType == "sprite"; + } + } + public float GetFloatParameter(float defaultValue = 0f) { if (!this.IsTag) diff --git a/Assets/RedBlueGames/TextTyper/TextTyper.cs b/Assets/RedBlueGames/TextTyper/TextTyper.cs index dea3945..bc3c3c9 100644 --- a/Assets/RedBlueGames/TextTyper/TextTyper.cs +++ b/Assets/RedBlueGames/TextTyper/TextTyper.cs @@ -57,7 +57,7 @@ public sealed class TextTyper : MonoBehaviour private TextMeshProUGUI textComponent; private float defaultPrintDelay; - private List characterPrintDelays; + private List charactersToType; private List animations; private Coroutine typeTextCoroutine; @@ -127,7 +127,7 @@ public void TypeText(string text, float printDelay = -1) } this.defaultPrintDelay = printDelay > 0 ? printDelay : PrintDelaySetting; - this.ProcessCustomTags(text); + this.ProcessTags(text); this.typeTextCoroutine = this.StartCoroutine(this.TypeTextCharByChar(text)); } @@ -166,31 +166,24 @@ private void CleanupCoroutine() private IEnumerator TypeTextCharByChar(string text) { - string taglessText = TextTagParser.RemoveAllTags(text); - int totalPrintedChars = taglessText.Length; - - int currPrintedChars = 1; this.TextComponent.text = TextTagParser.RemoveCustomTags(text); - do + for (int numPrintedCharacters = 0; numPrintedCharacters < this.charactersToType.Count; ++numPrintedCharacters) { - this.TextComponent.maxVisibleCharacters = currPrintedChars; + this.TextComponent.maxVisibleCharacters = numPrintedCharacters + 1; this.UpdateMeshAndAnims(); - this.OnCharacterPrinted(taglessText[currPrintedChars - 1].ToString()); + var printedChar = this.charactersToType[numPrintedCharacters]; + this.OnCharacterPrinted(printedChar.ToString()); - var delay = this.characterPrintDelays[currPrintedChars - 1]; if (this.useUnscaledTime) { - yield return new WaitForSecondsRealtime(delay); + yield return new WaitForSecondsRealtime(printedChar.Delay); } else { - yield return new WaitForSeconds(delay); + yield return new WaitForSeconds(printedChar.Delay); } - - ++currPrintedChars; } - while (currPrintedChars <= totalPrintedChars); this.typeTextCoroutine = null; this.OnTypewritingComplete(); @@ -219,11 +212,10 @@ private void UpdateMeshAndAnims() /// the appropriate TextAnimation components /// /// Full text string with tags - private void ProcessCustomTags(string text) + private void ProcessTags(string text) { - this.characterPrintDelays = new List(text.Length); + this.charactersToType = new List(); this.animations = new List(); - var textAsSymbolList = TextTagParser.CreateSymbolListFromText(text); int printedCharCount = 0; @@ -232,7 +224,8 @@ private void ProcessCustomTags(string text) float nextDelay = this.defaultPrintDelay; foreach (var symbol in textAsSymbolList) { - if (symbol.IsTag) + // Sprite prints a character so we need to throw it out and treat it like a character + if (symbol.IsTag && !symbol.IsReplacedWithSprite) { // TODO - Verification that custom tags are not nested, b/c that will not be handled gracefully if (symbol.Tag.TagType == TextTagParser.CustomTags.Delay) @@ -281,7 +274,7 @@ private void ProcessCustomTags(string text) } else { - // Unrecognized CustomTag Type. Should we error here? + // Tag type is likely a Unity tag, but it might be something we don't know... could error if unrecognized. } } @@ -289,14 +282,23 @@ private void ProcessCustomTags(string text) { printedCharCount++; - if (punctutationCharacters.Contains(symbol.Character)) + TypableCharacter characterToType = new TypableCharacter (); + if (symbol.IsTag && symbol.IsReplacedWithSprite) { - this.characterPrintDelays.Add(nextDelay * PunctuationDelayMultiplier); + characterToType.IsSprite = true; } else { - this.characterPrintDelays.Add(nextDelay); + characterToType.Char = symbol.Character; + } + + characterToType.Delay = nextDelay; + if (punctutationCharacters.Contains(symbol.Character)) + { + characterToType.Delay *= PunctuationDelayMultiplier; } + + this.charactersToType.Add(characterToType); } } } @@ -334,5 +336,26 @@ private void OnTypewritingComplete() public class CharacterPrintedEvent : UnityEvent { } + + /// + /// This class represents a printed character moment, which should correspond with a + /// delay in the text typer. It became necessary to make this a class when I had + /// to account for Sprite tags which are replaced by a sprite that counts as a "visble" + /// character. These sprites would not be in the Text string stripped of tags, + /// so this allows us to track and print them with a delay. + /// + private class TypableCharacter + { + public char Char { get; set; } + + public float Delay { get; set; } + + public bool IsSprite { get; set; } + + public override string ToString() + { + return this.IsSprite ? "Sprite" : Char.ToString(); + } + } } }