问题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 = "";

            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();
        }
    }
03-11 07:26