Commit 4243b1b4 authored by Gerard Gascón's avatar Gerard Gascón
Browse files

feature: Added a simple tools simplified text typer

parent 69b2edf1
Loading
Loading
Loading
Loading
+118 −0
Original line number Diff line number Diff line
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<DialogueCommand> 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<DialogueCommand> 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
+11 −0
Original line number Diff line number Diff line
fileFormatVersion: 2
guid: dd35e3193d336f24785ac649187905f8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 
+94 −0
Original line number Diff line number Diff line
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 = "<p:(?<pause>" + REMAINDER_REGEX + ")>";
		private static readonly Regex PauseRegex = new(PAUSE_REGEX_STRING);
		private const string SPEED_REGEX_STRING = "<sp:(?<speed>" + REMAINDER_REGEX + ")>";
		private static readonly Regex SpeedRegex = new(SPEED_REGEX_STRING);

		private static readonly Dictionary<string, float> PauseDictionary = new() {
			{ "tiny", .1f },
			{ "short", .25f },
			{ "normal", 0.666f },
			{ "long", 1f },
			{ "read", 2f },
		};

		public static List<DialogueCommand> ProcessInputString(string message, out string processedMessage) {
			List<DialogueCommand> result = new();
			processedMessage = message;

			processedMessage = HandlePauseTags(processedMessage, result);
			processedMessage = HandleSpeedTags(processedMessage, result);

			return result;
		}

		private static string HandleSpeedTags(string processedMessage, List<DialogueCommand> 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<DialogueCommand> 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
+11 −0
Original line number Diff line number Diff line
fileFormatVersion: 2
guid: 7cb18af2a0fb6af4abf80654e793de64
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: