feature: Added a simple tools simplified text typer
This commit is contained in:
parent
69b2edf1e6
commit
4243b1b401
4 changed files with 234 additions and 0 deletions
118
Assets/Scripts/Messaging/TextTyper.cs
Normal file
118
Assets/Scripts/Messaging/TextTyper.cs
Normal file
|
@ -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<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;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/Messaging/TextTyper.cs.meta
Normal file
11
Assets/Scripts/Messaging/TextTyper.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dd35e3193d336f24785ac649187905f8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
94
Assets/Scripts/Messaging/TextUtility.cs
Normal file
94
Assets/Scripts/Messaging/TextUtility.cs
Normal file
|
@ -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 = "<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,
|
||||
}
|
||||
}
|
11
Assets/Scripts/Messaging/TextUtility.cs.meta
Normal file
11
Assets/Scripts/Messaging/TextUtility.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7cb18af2a0fb6af4abf80654e793de64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue