Unity中的Text內(nèi)容有空格導(dǎo)致?lián)Q行,以及讓每行首字符不出現(xiàn)標(biāo)點(diǎn)符號(hào)
ps:本篇內(nèi)容應(yīng)該屬于半轉(zhuǎn)載半原創(chuàng),因?yàn)榭崭駬Q行和首字符不出現(xiàn)標(biāo)點(diǎn)。都是查詢資料后我借用別人的成果再進(jìn)行的整理的。但是發(fā)布時(shí)轉(zhuǎn)載和原創(chuàng)只能選擇一個(gè)因此我就選擇其一了。
先來(lái)說(shuō)下錯(cuò)誤換行的問(wèn)題,Unity中的Text組件有多行文本時(shí)會(huì)錯(cuò)誤的出現(xiàn)提前換行,導(dǎo)致一行文本后面有大塊空白區(qū)域
錯(cuò)誤換行解決方法:原文地址:https://www.cnblogs.com/leoin2012/p/7162099.html
我這邊還是說(shuō)下原因
當(dāng)字符串中帶有半角空格,且半角空格后面的字符串內(nèi)容超過(guò)文本剩余顯示寬度時(shí),Text組件會(huì)將后面的整段文字做換行。這個(gè)并不是bug,而是Text組件按照拉丁西語(yǔ)的分詞習(xí)慣做line break,半角空格相當(dāng)于分隔符,分隔空格前后的內(nèi)容,并視之為單詞。這種分詞規(guī)則在西語(yǔ)中是正確的,但用在中文就水土不服了:整段的中文內(nèi)容,粗暴地按半角空格分成了3部分,第一行空格后面的大段文字被判定為一個(gè)單詞,剩余寬度無(wú)法顯示,就被整個(gè)換到了第二行。
解決方案原理:我們平時(shí)用的Space鍵的空格,是換行空格(Breaking Space,Unicode編碼為\u0020)空格前后都運(yùn)行自動(dòng)換行,與之對(duì)應(yīng)的是不換行空格(Non-breaking space,Unicode編碼為\u00A0)顯示和換行空格一樣,主要是禁止他自動(dòng)換行。Breaking Space的存在讓西語(yǔ)得以分隔單詞,從而正確地分詞排版,但放在中文里是多余的存在,中文沒(méi)有單詞概念,不需要做分隔。
知道Breaking Space和Non-breaking space后解決就方便主要把文本中的所有Breaking Space換成Non-breaking Space。
(我的代碼是把換行問(wèn)題和每行首字符不出現(xiàn)標(biāo)點(diǎn)符號(hào)編寫在一起的,因此等講完怎么解決每行首字符出現(xiàn)標(biāo)點(diǎn)的問(wèn)題后再貼上我的代碼)
下面來(lái)說(shuō)下首字符出現(xiàn)標(biāo)點(diǎn)符號(hào)的解決方案:
原因還是因?yàn)閡nity中的text分詞規(guī)則對(duì)中文并不友好所導(dǎo)致的。
解決原理很簡(jiǎn)單,如果碰到首字符是標(biāo)點(diǎn)符號(hào),那么就提前一個(gè)字符或者多個(gè)字符讓他換行就可以了。
如果對(duì)中文排版要求比較高,就要考慮自己動(dòng)手做Text的布局實(shí)現(xiàn)了。
ps:以下是我整合解決空格換行和首字符標(biāo)點(diǎn)符號(hào)而整理的腳本。沒(méi)有做特別的優(yōu)化,比如首字符的標(biāo)點(diǎn)為成對(duì)標(biāo)點(diǎn)符號(hào)的左邊符號(hào)時(shí)也會(huì)判斷為首字符是符號(hào)然后提前一個(gè)字符或多個(gè)字符進(jìn)行換行。如果需要優(yōu)化文本顯示布局,請(qǐng)自己后續(xù)優(yōu)化。
另外說(shuō)下此文本是繼承unity中自帶的Text來(lái)實(shí)現(xiàn)的因此使用時(shí)將Text對(duì)象上原來(lái)的text組件移除,在掛上這個(gè)腳本,和原來(lái)的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{
? ?/// 標(biāo)記不換行的空格(換行空格Unicode編碼為/u0020,不換行的/u00A0)
public static readonly string Non_breaking_space = "\u00A0";
? ?/// 用于匹配標(biāo)點(diǎn)符號(hào)(正則表達(dá)式)
? ?private readonly string strPunctuation = @"\p{P}";
? ?/// 用于存儲(chǔ)text組件中的內(nèi)容
? ?private System.Text.StringBuilder TempText = null;
? ?/// 用于存儲(chǔ)text生成器中的內(nèi)容
? ?private IList<UILineInfo> TextLine;
//protected Text mytext;//Text組件
? ?protected override void ?Awake ()
? ?{
base.Awake();
this.RegisterDirtyVerticesCallback(OnTextChange);
? ?}
//解決換行空格問(wèn)題
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)); ? ?
}
//解決首字符出現(xiàn)標(biāo)點(diǎn)問(wèn)題
IEnumerator ClearUpPunctuationMode(Text _component)
? ?{
? ? ? ?//必須等當(dāng)前幀跑完后,不然如果在運(yùn)行text顯示的時(shí)候?qū)︼@示內(nèi)容進(jìn)行修改會(huì)報(bào)錯(cuò)
? ? ? ?yield return new WaitForEndOfFrame();
? ? ? ?//清除上一次添加的換行符號(hào)
? ? ? ?_component.text = _component.text.Replace("\n", string.Empty);
? ? ? ?TextLine = _component.cachedTextGenerator.lines;
? ? ? ?//需要改變的字符序號(hào)
? ? ? ?int ChangeIndex = -1;
? ? ? ?TempText = new System.Text.StringBuilder(_component.text);
? ? ? ?for (int i = 1; i < TextLine.Count; i++)
? ? ? ?{
? ? ? ? ? ?//首位是否有標(biāo)點(diǎn)
? ? ? ? ? ?bool IsPunctuation= Regex.IsMatch(TempText[TextLine[i].startCharIdx].ToString(), strPunctuation); ? ?
? ? ? ? ? ?//因?yàn)閷Q行空格都改成不換行空格后需要另外判斷下如果首字符是不換行空格那么還是需要調(diào)整換行字符的下標(biāo)
? ? ? ? ? ?if(TempText[TextLine[i].startCharIdx].ToString()==Non_breaking_space)
? ? ? ? ? ?{
? ? ? ? ? ? ? ?IsPunctuation=true;
? ? ? ? ? ?}
? ? ? ? ? ?//沒(méi)有標(biāo)點(diǎn)就跳過(guò)本次循環(huán)
? ? ? ? ? ?if(!IsPunctuation)
? ? ? ? ? ?{ ? ? ? ? ?
? ? ? ? ? ? ? ?continue;
? ? ? ? ? ?}
? ? ? ? ? ?else
? ? ? ? ? ?{
? ? ? ? ? ? ? ?//有標(biāo)點(diǎn)時(shí)保存當(dāng)前下標(biāo)
? ? ? ? ? ? ? ?ChangeIndex=TextLine[i].startCharIdx;
? ? ? ? ? ? ? ?//下面這個(gè)循環(huán)是為了判斷當(dāng)已經(jīng)提前一個(gè)字符后當(dāng)前這個(gè)的首字符還是標(biāo)點(diǎn)時(shí)做的繼續(xù)提前字符的處理
? ? ? ? ? ? ? ?while(IsPunctuation)
? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ?ChangeIndex = ChangeIndex - 1;
if(ChangeIndex<0)break;
? ? ? ? ? ? ? ? ? ?IsPunctuation = Regex.IsMatch(TempText[ChangeIndex].ToString(), strPunctuation);
? ? ? ? ? ? ? ? ? ? //因?yàn)閷Q行空格都改成不換行空格后需要另外判斷下如果首字符是不換行空格那么還是需要調(diào)整換行字符的下標(biāo)
? ?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();
? ?}
}