问题1:unity text显示文本时,符号可能显示在某行的开头的位置
问题2:打字机效果没有适配问题1的脚本
解决方法:
问题1:通过遍历text组件每一行数据(第二行开始),如果是符号,就在它之前的字符前添加换行符
问题2:适配上述脚本
脚本1 解决文本符号显示问题
TextSymbolFit.cs
public class TextSymbolFit : Text
{
/// <summary>
/// 用于匹配标点符号
/// </summary>
private readonly string strRegex = @"\p{P}";
/// <summary>
/// 用于存储text组件中的内容
/// </summary>
private System.Text.StringBuilder MExplainText = null;
/// <summary>
/// 用于存储text生成器中的内容
/// </summary>
private IList<UILineInfo> MExpalinTextLine;
protected override void OnPopulateMesh(VertexHelper toFill)
{
base.OnPopulateMesh(toFill);
StartCoroutine(MClearUpExplainMode(this, text));
}
private IEnumerator MClearUpExplainMode(Text _component, string _text)
{
_component.text = _text;
yield return new WaitForEndOfFrame();
MExplainText = new System.Text.StringBuilder(_component.text);
MExpalinTextLine = _component.cachedTextGenerator.lines;
int mChangeIndex;
// 从第二行开始进行检测
for (int i = 1; i < MExpalinTextLine.Count; i++)
{
try
{
if (MExpalinTextLine[i].startCharIdx >= _component.text.Length) continue;
//首位是否有标点
bool match = Regex.IsMatch(MExplainText.ToString()[MExpalinTextLine[i].startCharIdx].ToString(), strRegex);
if (match)
{
mChangeIndex = MExpalinTextLine[i].startCharIdx - 1;
// 解决联系多个都是标点符号的问题
for (int j = MExpalinTextLine[i].startCharIdx - 1; j > 0; j--)
{
match = Regex.IsMatch(MExplainText.ToString()[j].ToString(), strRegex);
if (match)
{
mChangeIndex--;
}
else
{
break;
}
}
MExplainText.Insert(mChangeIndex, "\n");
}
}
catch (Exception e)
{
Debug.LogException(e);
}
}
_component.text = MExplainText.ToString();
}
}
脚本2,适配TextSymbolFit脚本
UITextType.cs
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 = "";
bool symbolDetected = false;
for (int i = 0; i < length; i++)
{
//匹配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;
}
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);
if (tagOpened)
{
if (label.text[..^tagLabel.Length] != currentText)
{
currentText = label.text[..^tagLabel.Length];
}
}
else
{
if (label.text != currentText)
{
currentText = label.text;
}
}
}
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();
}
}