unity text 文本符号显示问题与打字机效果的结合
public class UITextType : MonoBehaviour
{
public delegate void OnComplete();
[SerializeField] float defaultSpeed = 0.05f;
private Text label;
private string finalText = string.Empty;
private Coroutine typeTextCoroutine;
private static readonly string[] uguiSymbols = { "b", "i" };
private static readonly string[] uguiCloseSymbols = { "b", "i", "size", "color" };
private OnComplete OnCompleteCallback;
private void InitText()
{
if (label == null) label = GetComponent<Text>();
}
public void Awake()
{
InitText();
}
public void SetText(string text, float speed = -1)
{
InitText();
defaultSpeed = speed > 0 ? speed : defaultSpeed;
finalText = ReplaceSpeed(text);
label.text = "";
if (typeTextCoroutine != null)
{
StopCoroutine(typeTextCoroutine);
typeTextCoroutine = null;
}
typeTextCoroutine = StartCoroutine(InnerTypeText(text));
}
public void SkipTypeText()
{
if (typeTextCoroutine != null)
{
StopCoroutine(typeTextCoroutine);
typeTextCoroutine = null;
}
label.text = finalText;
OnCompleteCallback?.Invoke();
}
public IEnumerator InnerTypeText(string text)
{
string currentText = "";
int length = text.Length;
float speed = defaultSpeed;
bool tagOpened = false;
string tagType = "";
for (int i = 0; i < length; i++)
{
currentText = "";
//匹配speed
if (text[i] == '[' && i + 6 < length && text.Substring(i, 7).Equals("[speed="))
{
string parseSpeed = "";
for (int j = i + 7; j < length; j++)
{
if (text[j] == ']')
{
break;
}
parseSpeed += text[j];
}
if (!float.TryParse(parseSpeed, out speed))
{
speed = defaultSpeed;
}
i += 8 + parseSpeed.Length - 1;
continue;
}
bool symbolDetected = false;
//匹配 <i> 或 <b>
for (int j = 0; j < uguiSymbols.Length; j++)
{
string symbol = string.Format("<{0}>", uguiSymbols[j]);
if (text[i] == '<' && i + 1 + uguiSymbols[j].Length < length && text.Substring(i, 2 + uguiSymbols[j].Length).Equals(symbol))
{
currentText += symbol;
i += (2 + uguiSymbols[j].Length) - 1;
symbolDetected = true;
tagOpened = true;
tagType = uguiSymbols[j];
break;
}
}
//匹配富文本color格式
if (text[i] == '<' && i + 1 + 15 < length && text.Substring(i, 2 + 6).Equals("<color=#") && text[i + 16] == '>')
{
currentText += text.Substring(i, 2 + 6 + 8);
i += (2 + 14) - 1;
symbolDetected = true;
tagOpened = true;
tagType = "color";
}
//匹配富文本size格式
if (text[i] == '<' && i + 5 < length && text.Substring(i, 6).Equals("<size="))
{
string parseSize = "";
for (var j = i + 6; j < length; j++)
{
if (text[j] == '>')
{
break;
}
parseSize += text[j];
}
if (int.TryParse(parseSize, out _))
{
currentText += text.Substring(i, 7 + parseSize.Length);
i += (7 + parseSize.Length) - 1;
symbolDetected = true;
tagOpened = true;
tagType = "size";
}
}
//匹配富文本结束 </i> </b> </size> </color>
for (int j = 0; j < uguiCloseSymbols.Length; j++)
{
string symbol = string.Format("</{0}>", uguiCloseSymbols[j]);
if (text[i] == '<' && i + 2 + uguiCloseSymbols[j].Length < length && text.Substring(i, 3 + uguiCloseSymbols[j].Length).Equals(symbol))
{
currentText += symbol;
i += (3 + uguiCloseSymbols[j].Length) - 1;
symbolDetected = true;
tagOpened = false;
break;
}
}
if (symbolDetected) continue;
currentText += text[i];
label.text += currentText + (tagOpened ? string.Format("</{0}>", tagType) : "");
yield return new WaitForSeconds(speed);
}
typeTextCoroutine = null;
OnCompleteCallback?.Invoke();
}
private string ReplaceSpeed(string text)
{
return Regex.Replace(text, @"\[speed=\d+(\.\d+)?\]", "");
}
public bool IsSkippable()
{
return typeTextCoroutine != null;
}
public void SetOnComplete(OnComplete onComplete)
{
OnCompleteCallback = onComplete;
}
}
public static class UITypeTextUtility
{
public static void TypeText(this Text label, string text, float speed = 0.05f, UITextType.OnComplete onComplete = null)
{
if (!label.TryGetComponent<UITextType>(out var typeText))
{
typeText = label.gameObject.AddComponent<UITextType>();
}
typeText.SetText(text, speed);
typeText.SetOnComplete(onComplete);
}
public static bool IsSkippable(this Text label)
{
if (!label.TryGetComponent<UITextType>(out var typeText))
{
typeText = label.gameObject.AddComponent<UITextType>();
}
return typeText.IsSkippable();
}
public static void SkipTypeText(this Text label)
{
if (!label.TryGetComponent<UITextType>(out var typeText))
{
typeText = label.gameObject.AddComponent<UITextType>();
}
typeText.SkipTypeText();
}
}