using System.Collections;using System.Collections.Generic;using System.Text.RegularExpressions;using UnityEngine;using UnityEngine.UI;/// summary/// Text组件首行标点符号优化排版工具/// 自动检测并处理行首标点符号避免出现句首标点的尴尬排版/// /summarypublic class TextSymbolWrap : MonoBehaviour{public Text textCom;private string origStr;private string replaceStr;private string finalReplaceStr;/// 标记不换行的空格换行空格Unicode编码为/u0020不换行的/u00A0public static readonly string Non_breaking_space \u00A0;/// 用于匹配标点符号为了不破坏富文本标签所以只匹配指定的符号private readonly string strPunctuation [。?];/// 用于存储text组件中的内容private System.Text.StringBuilder TempText null;/// 用于存储text生成器中的内容private IListUILineInfo TextLine;private int screenWidth 0;private int screenHeight 0;//在替换后的文本最后面添加一个看不到的字符以用于辨别当前输入的文本是不是原始文本private string endString ;private bool isReplacing false;private void OnEnable(){isReplacing false;CheckTextComponent();CheckScreenSizeChange();ReplaceTextFun();}void Update(){// 当屏幕分辨率发生变化时恢复原文本并重新计算防止排版错乱if (CheckScreenSizeChange()){if (textCom ! null !string.IsNullOrEmpty(origStr)){textCom.text origStr;replaceStr ;finalReplaceStr ;}}CheckReplaceText();}private bool CheckScreenSizeChange(){if (Screen.width ! screenWidth || Screen.height ! screenHeight){screenWidth Screen.width;screenHeight Screen.height;return true;}return false;}private void CheckTextComponent(){if (textCom null){textCom this.gameObject.GetComponentText();}}private void CheckReplaceText(){if (textCom null || !CheckTextIsChange()) return;ReplaceTextFun();}private void ReplaceTextFun(){if (isReplacing) return;replaceStr ;finalReplaceStr ;StartCoroutine(ClearUpPunctuationMode, textCom);}private bool CheckTextIsChange(){if (textCom null) return false;return !string.Equals(textCom.text, finalReplaceStr);}IEnumerator ClearUpPunctuationMode(Text _component){isReplacing true;// 不能立刻进行计算要等渲染完上一帧才计算故延迟60毫秒约两帧多yield return new WaitForSeconds(0.06f);if (string.IsNullOrEmpty(_component.text)){isReplacing false;}else{string tempTxt _component.text;bool isOrigStr false;// 如果结尾没有特指标记字符就认为是业务刚赋的值即原始字符串if (tempTxt[tempTxt.Length - 1].ToString() ! endString){origStr tempTxt;isOrigStr true;}TextLine _component.cachedTextGenerator.lines;int ChangeIndex -1;TempText new System.Text.StringBuilder(_component.text);// 从1开始遍历只看第二行及以后的首字符for (int i 1; i TextLine.Count; i){UILineInfo lineInfo TextLine[i];int startCharIdx lineInfo.startCharIdx;if (TempText.Length startCharIdx) continue;bool IsPunctuation Regex.IsMatch(TempText[startCharIdx].ToString(), strPunctuation);// 将换行空格改成不换行空格后如果首字符是不换行空格同样需要调整if (TempText[startCharIdx].ToString() Non_breaking_space){IsPunctuation true;}if (IsPunctuation){ChangeIndex startCharIdx;// 回退操作判断提前一个字符后当前首字符是否仍是标点while (IsPunctuation){ChangeIndex - 1;if (ChangeIndex 0) break;IsPunctuation Regex.IsMatch(TempText[ChangeIndex].ToString(), strPunctuation);if (TempText[ChangeIndex].ToString() Non_breaking_space){IsPunctuation true;}}if (ChangeIndex 0) continue;// 在合适的位置手动插入换行符if (TempText[ChangeIndex - 1] ! \n)TempText.Insert(ChangeIndex, \n);}}replaceStr TempText.ToString();// 如果最终排版有改动if (!string.Equals(tempTxt, replaceStr)){if (isOrigStr){replaceStr endString;}_component.text replaceStr;}else{// 计算后结果一致证明当前文本排版已合法记录状态防止死循环重复验证finalReplaceStr replaceStr;}isReplacing false;}}}
Unity 避免Text组件每行开头不是字符和空格,适配不同分辨率
发布时间:2026/5/25 3:12:07
using System.Collections;using System.Collections.Generic;using System.Text.RegularExpressions;using UnityEngine;using UnityEngine.UI;/// summary/// Text组件首行标点符号优化排版工具/// 自动检测并处理行首标点符号避免出现句首标点的尴尬排版/// /summarypublic class TextSymbolWrap : MonoBehaviour{public Text textCom;private string origStr;private string replaceStr;private string finalReplaceStr;/// 标记不换行的空格换行空格Unicode编码为/u0020不换行的/u00A0public static readonly string Non_breaking_space \u00A0;/// 用于匹配标点符号为了不破坏富文本标签所以只匹配指定的符号private readonly string strPunctuation [。?];/// 用于存储text组件中的内容private System.Text.StringBuilder TempText null;/// 用于存储text生成器中的内容private IListUILineInfo TextLine;private int screenWidth 0;private int screenHeight 0;//在替换后的文本最后面添加一个看不到的字符以用于辨别当前输入的文本是不是原始文本private string endString ;private bool isReplacing false;private void OnEnable(){isReplacing false;CheckTextComponent();CheckScreenSizeChange();ReplaceTextFun();}void Update(){// 当屏幕分辨率发生变化时恢复原文本并重新计算防止排版错乱if (CheckScreenSizeChange()){if (textCom ! null !string.IsNullOrEmpty(origStr)){textCom.text origStr;replaceStr ;finalReplaceStr ;}}CheckReplaceText();}private bool CheckScreenSizeChange(){if (Screen.width ! screenWidth || Screen.height ! screenHeight){screenWidth Screen.width;screenHeight Screen.height;return true;}return false;}private void CheckTextComponent(){if (textCom null){textCom this.gameObject.GetComponentText();}}private void CheckReplaceText(){if (textCom null || !CheckTextIsChange()) return;ReplaceTextFun();}private void ReplaceTextFun(){if (isReplacing) return;replaceStr ;finalReplaceStr ;StartCoroutine(ClearUpPunctuationMode, textCom);}private bool CheckTextIsChange(){if (textCom null) return false;return !string.Equals(textCom.text, finalReplaceStr);}IEnumerator ClearUpPunctuationMode(Text _component){isReplacing true;// 不能立刻进行计算要等渲染完上一帧才计算故延迟60毫秒约两帧多yield return new WaitForSeconds(0.06f);if (string.IsNullOrEmpty(_component.text)){isReplacing false;}else{string tempTxt _component.text;bool isOrigStr false;// 如果结尾没有特指标记字符就认为是业务刚赋的值即原始字符串if (tempTxt[tempTxt.Length - 1].ToString() ! endString){origStr tempTxt;isOrigStr true;}TextLine _component.cachedTextGenerator.lines;int ChangeIndex -1;TempText new System.Text.StringBuilder(_component.text);// 从1开始遍历只看第二行及以后的首字符for (int i 1; i TextLine.Count; i){UILineInfo lineInfo TextLine[i];int startCharIdx lineInfo.startCharIdx;if (TempText.Length startCharIdx) continue;bool IsPunctuation Regex.IsMatch(TempText[startCharIdx].ToString(), strPunctuation);// 将换行空格改成不换行空格后如果首字符是不换行空格同样需要调整if (TempText[startCharIdx].ToString() Non_breaking_space){IsPunctuation true;}if (IsPunctuation){ChangeIndex startCharIdx;// 回退操作判断提前一个字符后当前首字符是否仍是标点while (IsPunctuation){ChangeIndex - 1;if (ChangeIndex 0) break;IsPunctuation Regex.IsMatch(TempText[ChangeIndex].ToString(), strPunctuation);if (TempText[ChangeIndex].ToString() Non_breaking_space){IsPunctuation true;}}if (ChangeIndex 0) continue;// 在合适的位置手动插入换行符if (TempText[ChangeIndex - 1] ! \n)TempText.Insert(ChangeIndex, \n);}}replaceStr TempText.ToString();// 如果最终排版有改动if (!string.Equals(tempTxt, replaceStr)){if (isOrigStr){replaceStr endString;}_component.text replaceStr;}else{// 计算后结果一致证明当前文本排版已合法记录状态防止死循环重复验证finalReplaceStr replaceStr;}isReplacing false;}}}