发布时间:2023-04-28 10:30
ps:后续我的笔记更新将更新在我的B站账号的专栏中(TenderRain-),此CSDN将不再更新,已有的内容会同步到B站专栏中,CSDN上也不会删除
ps:本篇内容应该属于半转载半原创,因为空格换行和首字符不出现标点。都是查询资料后我借用别人的成果再进行的整理的。但是发布时转载和原创只能选择一个因此我就选择其一了。
先来说下错误换行的问题,Unity中的Text组件有多行文本时会错误的出现提前换行,导致一行文本后面有大块空白区域
错误换行解决方法:原文地址:https://www.cnblogs.com/leoin2012/p/7162099.html
我这边还是说下原因
当字符串中带有半角空格,且半角空格后面的字符串内容超过文本剩余显示宽度时,Text组件会将后面的整段文字做换行。这个并不是bug,而是Text组件按照拉丁西语的分词习惯做line break,半角空格相当于分隔符,分隔空格前后的内容,并视之为单词。这种分词规则在西语中是正确的,但用在中文就水土不服了:整段的中文内容,粗暴地按半角空格分成了3部分,第一行空格后面的大段文字被判定为一个单词,剩余宽度无法显示,就被整个换到了第二行。
解决方案原理:我们平时用的Space键的空格,是换行空格(Breaking Space,Unicode编码为\\u0020)空格前后都运行自动换行,与之对应的是不换行空格(Non-breaking space,Unicode编码为\\u00A0)显示和换行空格一样,主要是禁止他自动换行。Breaking Space的存在让西语得以分隔单词,从而正确地分词排版,但放在中文里是多余的存在,中文没有单词概念,不需要做分隔。
知道Breaking Space和Non-breaking space后解决就方便主要把文本中的所有Breaking Space换成Non-breaking Space。
(我的代码是把换行问题和每行首字符不出现标点符号编写在一起的,因此等讲完怎么解决每行首字符出现标点的问题后再贴上我的代码)
下面来说下首字符出现标点符号的解决方案:
原因还是因为unity中的text分词规则对中文并不友好所导致的。
解决原理很简单,如果碰到首字符是标点符号,那么就提前一个字符或者多个字符让他换行就可以了。
如果对中文排版要求比较高,就要考虑自己动手做Text的布局实现了。
ps:以下是我整合解决空格换行和首字符标点符号而整理的脚本。没有做特别的优化,比如首字符的标点为成对标点符号的左边符号时也会判断为首字符是符号然后提前一个字符或多个字符进行换行。如果需要优化文本显示布局,请自己后续优化。
另外说下此文本是继承unity中自带的Text来实现的因此使用时将Text对象上原来的text组件移除,在挂上这个脚本,和原来的text一样的用法。
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(Text))]
public class TextFit : Text{
/// 标记不换行的空格(换行空格Unicode编码为/u0020,不换行的/u00A0)
public static readonly string Non_breaking_space = \"\\u00A0\";
/// 用于匹配标点符号(正则表达式)
private readonly string strPunctuation = @\"\\p{P}\";
/// 用于存储text组件中的内容
private System.Text.StringBuilder TempText = null;
/// 用于存储text生成器中的内容
private IList<UILineInfo> TextLine;
//protected Text mytext;//Text组件
protected override void Awake ()
{
base.Awake();
this.RegisterDirtyVerticesCallback(OnTextChange);
}
//解决换行空格问题
public void OnTextChange()
{
if (this.text.Contains(\" \"))
{
this.text = this.text.Replace(\" \", Non_breaking_space);
}
}
protected override void OnPopulateMesh(VertexHelper toFill)
{
base.OnPopulateMesh(toFill);
StartCoroutine(ClearUpPunctuationMode(this));
}
//解决首字符出现标点问题
IEnumerator ClearUpPunctuationMode(Text _component)
{
//必须等当前帧跑完后,不然如果在运行text显示的时候对显示内容进行修改会报错
yield return new WaitForEndOfFrame();
//清除上一次添加的换行符号
_component.text = _component.text.Replace(\"\\n\", string.Empty);
TextLine = _component.cachedTextGenerator.lines;
//需要改变的字符序号
int ChangeIndex = -1;
TempText = new System.Text.StringBuilder(_component.text);
for (int i = 1; i < TextLine.Count; i++)
{
//首位是否有标点
bool IsPunctuation= Regex.IsMatch(TempText[TextLine[i].startCharIdx].ToString(), strPunctuation);
//因为将换行空格都改成不换行空格后需要另外判断下如果首字符是不换行空格那么还是需要调整换行字符的下标
if(TempText[TextLine[i].startCharIdx].ToString()==Non_breaking_space)
{
IsPunctuation=true;
}
//没有标点就跳过本次循环
if(!IsPunctuation)
{
continue;
}
else
{
//有标点时保存当前下标
ChangeIndex=TextLine[i].startCharIdx;
//下面这个循环是为了判断当已经提前一个字符后当前这个的首字符还是标点时做的继续提前字符的处理
while(IsPunctuation)
{
ChangeIndex = 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\");
}
}
//Debug.Log(TextLine.Count);
_component.text = TempText.ToString();
}
}