当前位置: 首页 > news >正文

Unity 避免Text组件每行开头不是字符和空格,适配不同分辨率

using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Text组件首行标点符号优化排版工具
/// 自动检测并处理行首标点符号,避免出现句首标点的尴尬排版
/// </summary>
public class TextSymbolWrap : MonoBehaviour
{
public Text textCom;
private string origStr;
private string replaceStr;
private string finalReplaceStr;

/// 标记不换行的空格(换行空格Unicode编码为/u0020,不换行的/u00A0)
public static readonly string Non_breaking_space = "\u00A0";

/// 用于匹配标点符号,为了不破坏富文本标签,所以只匹配指定的符号
private readonly string strPunctuation = @"[,。??!;]";

/// 用于存储text组件中的内容
private System.Text.StringBuilder TempText = null;

/// 用于存储text生成器中的内容
private IList<UILineInfo> 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.GetComponent<Text>();
}
}

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;
}
}
}

http://www.jsqmd.com/news/881055/

相关文章:

  • 2026年4月线束设备公司口碑推荐,线束设备/剥线机/端子机,线束设备实力厂家哪家靠谱 - 品牌推荐师
  • 告别SSH断连焦虑:手把手教你用Screen在Linux后台挂起任务(含源码编译避坑)
  • 给客户打电话经常被挂?电话号码企业认证来帮忙
  • 【Linux:文件】Linux 动静态库详解::制作、使用、原理与实战
  • Codex入门19-数据库操作(解放双手:用自然语言写SQL、建表和数据迁移)
  • Deep Clustering of Tabular Data by Weighted Gaussian Distribution Learning——基于加权高斯分布学习的表格数据深度聚类
  • qemu和gcc编译
  • 从单用户到团队协作:给你的Ubuntu服务器配置多用户SSH访问权限(附sudo权限管理)
  • AI agent案例汇总:基于 LangGraph 的智能对话 Agent 实现
  • 文章三:Elasticsearch 集群恢复和索引分布
  • 2026年当前,上海别墅大宅新风系统可靠服务商深度解析 - 2026年企业推荐榜
  • 机器学习数据集详解,公开免费数据集获取渠道汇总
  • Try和expect的正确使用方式
  • 连锁董事网络指标数据(2001-2024)
  • 2026电工杯数学建模竞赛A题论文、代码、数据
  • 数据结构:线性表和顺序表
  • 2026槽式电缆桥架优质推荐指南:网格电缆桥架、铝合金走线架、不锈钢电缆桥架、北京电缆桥架厂家、托盘式电缆桥架选择指南 - 优质品牌商家
  • Claude Code 在安装vscode插件时遇到的问题。
  • 告别图形界面!5个CUPS命令行技巧,让你在Linux终端高效管理打印机
  • 2026微型舵机优质推荐榜:小型舵机/尾翼用方扁舵机/工业舵机/德晟舵机/数字舵机/无人机舵机/无刷舵机/最小的舵机/选择指南 - 优质品牌商家
  • 2026电工杯数学建模竞赛A题论文、代码、数据(改进)
  • # 网页设计学习感悟
  • 朝晖玻璃钢:玻璃钢保温水箱/玻璃钢消防水箱/玻璃钢罐化粪池/碳钢水箱/立式不锈钢水箱/组合式玻璃钢水箱/雨水一体化提升泵站/选择指南 - 优质品牌商家
  • 别再手动下载DLL了!用Windows自带工具SFC/SCANNOW一键修复kernel32.dll错误
  • 别再让系统‘无家可归’:给已用满空间的Win10 SSD无损创建EFI引导分区指南
  • 2026年紫外线杀菌除藻灯优质厂家深度解析:聚焦技术、产能与服务三角 - 2026年企业推荐榜
  • Titanic数据集分析避坑指南:新手常犯的3个错误及如何修正
  • ubuntu2026.04部署k8s1.36版本的傻瓜式教程(注:运行时为docker,网络插件为calico)
  • 一文讲清楚规则、Skill、MCP
  • 2026泛塞封密封圈优质品牌推荐:聚四氟乙烯密封圈/铁氟龙密封圈/高分子材料密封圈/O型圈/PEEK密封圈/PU密封圈/选择指南 - 优质品牌商家