From 4243b1b401348d0dedcb0a3135fcc33cf594a607 Mon Sep 17 00:00:00 2001 From: Geri Date: Fri, 26 Jan 2024 21:31:57 +0100 Subject: [PATCH] feature: Added a simple tools simplified text typer --- Assets/Scripts/Messaging/TextTyper.cs | 118 +++++++++++++++++++ Assets/Scripts/Messaging/TextTyper.cs.meta | 11 ++ Assets/Scripts/Messaging/TextUtility.cs | 94 +++++++++++++++ Assets/Scripts/Messaging/TextUtility.cs.meta | 11 ++ 4 files changed, 234 insertions(+) create mode 100644 Assets/Scripts/Messaging/TextTyper.cs create mode 100644 Assets/Scripts/Messaging/TextTyper.cs.meta create mode 100644 Assets/Scripts/Messaging/TextUtility.cs create mode 100644 Assets/Scripts/Messaging/TextUtility.cs.meta diff --git a/Assets/Scripts/Messaging/TextTyper.cs b/Assets/Scripts/Messaging/TextTyper.cs new file mode 100644 index 0000000..f255451 --- /dev/null +++ b/Assets/Scripts/Messaging/TextTyper.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using TMPro; +using UnityEngine; + +namespace Messaging { + public class TextTyper { + private bool _textAnimating; + private bool _stopAnimating; + + private readonly TMP_Text _textBox; + + public TextTyper(TMP_Text textBox) { + _textBox = textBox; + } + + public IEnumerator AnimateTextIn(List commands, string processedMessage, Action onFinish) { + _textAnimating = true; + float secondsPerCharacter = 1f / 150f; + float timeOfLastCharacter = 0; + + TMP_TextInfo textInfo = _textBox.textInfo; + foreach (TMP_MeshInfo meshInfer in textInfo.meshInfo) { + if (meshInfer.vertices == null) continue; + for (int j = 0; j < meshInfer.vertices.Length; j++) { + meshInfer.vertices[j] = Vector3.zero; + } + } + + _textBox.text = processedMessage; + _textBox.ForceMeshUpdate(); + + Color32[][] originalColors = new Color32[textInfo.meshInfo.Length][]; + for (int i = 0; i < originalColors.Length; i++) { + Color32[] theColors = textInfo.meshInfo[i].colors32; + originalColors[i] = new Color32[theColors.Length]; + Array.Copy(theColors, originalColors[i], theColors.Length); + } + int charCount = textInfo.characterCount; + float[] charAnimStartTimes = new float[charCount]; + for (int i = 0; i < charCount; i++) { + charAnimStartTimes[i] = -1; + } + int visibleCharacterIndex = 0; + while (true) { + if (_stopAnimating) { + for (int i = visibleCharacterIndex; i < charCount; i++) { + charAnimStartTimes[i] = Time.unscaledTime; + } + visibleCharacterIndex = charCount; + FinishAnimating(onFinish); + } + if (ShouldShowNextCharacter(secondsPerCharacter, timeOfLastCharacter)) { + if (visibleCharacterIndex <= charCount) { + ExecuteCommandsForCurrentIndex(commands, visibleCharacterIndex, ref secondsPerCharacter, + ref timeOfLastCharacter); + if (visibleCharacterIndex < charCount && + ShouldShowNextCharacter(secondsPerCharacter, timeOfLastCharacter)) { + charAnimStartTimes[visibleCharacterIndex] = Time.unscaledTime; + visibleCharacterIndex++; + timeOfLastCharacter = Time.unscaledTime; + if (visibleCharacterIndex == charCount) { + FinishAnimating(onFinish); + } + } + } + } + + _textBox.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32); + for (int i = 0; i < textInfo.meshInfo.Length; i++) { + TMP_MeshInfo theInfo = textInfo.meshInfo[i]; + theInfo.mesh.vertices = theInfo.vertices; + _textBox.UpdateGeometry(theInfo.mesh, i); + } + yield return null; + } + } + + private static void ExecuteCommandsForCurrentIndex(List commands, int visableCharacterIndex, + ref float secondsPerCharacter, ref float timeOfLastCharacter) { + for (int i = 0; i < commands.Count; i++) { + DialogueCommand command = commands[i]; + if (command.Position != visableCharacterIndex) continue; + switch (command.Type) { + case DialogueCommandType.Pause: + timeOfLastCharacter = Time.unscaledTime + command.FloatValue; + break; + case DialogueCommandType.TextSpeedChange: + secondsPerCharacter = 1f / command.FloatValue; + break; + } + commands.RemoveAt(i); + i--; + } + } + + private void FinishAnimating(Action onFinish) { + _textAnimating = false; + _stopAnimating = false; + onFinish?.Invoke(); + } + + private static bool ShouldShowNextCharacter(float secondsPerCharacter, float timeOfLastCharacter) { + return (Time.unscaledTime - timeOfLastCharacter) > secondsPerCharacter; + } + + public void SkipToEndOfCurrentMessage() { + if (_textAnimating) { + _stopAnimating = true; + } + } + + public bool IsMessageAnimating() { + return _textAnimating; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Messaging/TextTyper.cs.meta b/Assets/Scripts/Messaging/TextTyper.cs.meta new file mode 100644 index 0000000..9783241 --- /dev/null +++ b/Assets/Scripts/Messaging/TextTyper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd35e3193d336f24785ac649187905f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Messaging/TextUtility.cs b/Assets/Scripts/Messaging/TextUtility.cs new file mode 100644 index 0000000..22bbd73 --- /dev/null +++ b/Assets/Scripts/Messaging/TextUtility.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; + +namespace Messaging { + public class DialogueUtility : MonoBehaviour { + private const string REMAINDER_REGEX = "(.*?((?=>)|(/|$)))"; + private const string PAUSE_REGEX_STRING = "" + REMAINDER_REGEX + ")>"; + private static readonly Regex PauseRegex = new(PAUSE_REGEX_STRING); + private const string SPEED_REGEX_STRING = "" + REMAINDER_REGEX + ")>"; + private static readonly Regex SpeedRegex = new(SPEED_REGEX_STRING); + + private static readonly Dictionary PauseDictionary = new() { + { "tiny", .1f }, + { "short", .25f }, + { "normal", 0.666f }, + { "long", 1f }, + { "read", 2f }, + }; + + public static List ProcessInputString(string message, out string processedMessage) { + List result = new(); + processedMessage = message; + + processedMessage = HandlePauseTags(processedMessage, result); + processedMessage = HandleSpeedTags(processedMessage, result); + + return result; + } + + private static string HandleSpeedTags(string processedMessage, List result) { + MatchCollection speedMatches = SpeedRegex.Matches(processedMessage); + foreach (Match match in speedMatches) { + string stringVal = match.Groups["speed"].Value; + if (!float.TryParse(stringVal, out float val)) { + val = 150f; + } + result.Add(new DialogueCommand { + Position = VisibleCharactersUpToIndex(processedMessage, match.Index), + Type = DialogueCommandType.TextSpeedChange, + FloatValue = val + }); + } + processedMessage = Regex.Replace(processedMessage, SPEED_REGEX_STRING, ""); + return processedMessage; + } + + private static string HandlePauseTags(string processedMessage, List result) { + MatchCollection pauseMatches = PauseRegex.Matches(processedMessage); + foreach (Match match in pauseMatches) { + string val = match.Groups["pause"].Value; + string pauseName = val; + Debug.Assert(PauseDictionary.ContainsKey(pauseName), "no pause registered for '" + pauseName + "'"); + result.Add(new DialogueCommand { + Position = VisibleCharactersUpToIndex(processedMessage, match.Index), + Type = DialogueCommandType.Pause, + FloatValue = PauseDictionary[pauseName] + }); + } + processedMessage = Regex.Replace(processedMessage, PAUSE_REGEX_STRING, ""); + return processedMessage; + } + + private static int VisibleCharactersUpToIndex(string message, int index) { + int result = 0; + bool insideBrackets = false; + for (int i = 0; i < index; i++) { + if (message[i] == '<') { + insideBrackets = true; + } else if (message[i] == '>') { + insideBrackets = false; + result--; + } + if (!insideBrackets) { + result++; + } else if (i + 6 < index && message.Substring(i, 6) == "sprite") { + result++; + } + } + return result; + } + } + + public struct DialogueCommand { + public int Position; + public DialogueCommandType Type; + public float FloatValue; + } + + public enum DialogueCommandType { + Pause, + TextSpeedChange, + } +} \ No newline at end of file diff --git a/Assets/Scripts/Messaging/TextUtility.cs.meta b/Assets/Scripts/Messaging/TextUtility.cs.meta new file mode 100644 index 0000000..7f41413 --- /dev/null +++ b/Assets/Scripts/Messaging/TextUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7cb18af2a0fb6af4abf80654e793de64 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: