最近项目中某些文字效果,使用TextMeshPro能更好、更快速的实现,看FairyGUI对TextMeshPro也有了支持,就决定使用了。项目中涉及多语言,但给的字体库不支持中文,只能使用备用字体库,看TMP中有个Fallback Font Assets(如图1-1),以为就可以这么愉快的搞定了,奈何FairyGUI并不支持这个特性,求助FairyGUI官网群也无果,只能自己解决了。

看了一下UGUI中对这一特性的支持,它就是额外创建了一个TMP_SubMeshUI使用备用字体绘制文本。

(1)参照这个思路,在FairyGUI中创建一个GSubTextField使用备用字体绘制文本,GSubTextField基本上就是GTextField的拷贝,只是对Setup_BeforeAdd做了一些改动,当创建GSubTextField时,完全用GTextField的各种属性设置GSubTextField。由于TMP_FontAsset中的fallbackFontAssets为私有属性无法访问,我直接在GSubTextField中手动设置了备用字体。代码如下:
public void Setup_BeforeAdd(TextFormat ptf, GTextField gtf)
{
SetXY(gtf.x, gtf.y);
SetSize(gtf.width, gtf.height, true);
minWidth = gtf.minWidth;
maxWidth = gtf.maxWidth;
minHeight = gtf.minHeight;
maxHeight = gtf.maxHeight;
SetScale(gtf.scaleX, gtf.scaleY);
this.skew = gtf.skew;
SetPivot(gtf.pivot.x, gtf.pivot.y, gtf.pivotAsAnchor);
this.alpha = gtf.alpha;
this.rotation = gtf.rotation;
this.visible = gtf.visible;
this.touchable = false;
this.grayed = gtf.grayed;
this.blendMode = gtf.blendMode;
TextFormat tf = _textField.textFormat;
tf.font = UIConfig.defaultFont;
tf.size = ptf.size;
tf.color = ptf.color;
this.align = gtf.align;
this.verticalAlign = gtf.verticalAlign;
tf.lineSpacing = ptf.lineSpacing;
tf.letterSpacing = ptf.letterSpacing;
_ubbEnabled = gtf.UBBEnabled;
this.autoSize = gtf.autoSize;
tf.underline = ptf.underline;
tf.italic = ptf.italic;
tf.bold = ptf.bold;
this.singleLine = gtf.singleLine;
tf.outlineColor = ptf.outlineColor;
tf.outline = ptf.outline;
tf.shadowColor = ptf.shadowColor;
tf.shadowOffset = new Vector2(ptf.shadowOffset.x, ptf.shadowOffset.y);
if(gtf.templateVars != null)
_templateVars = new Dictionary<string, string>();
tf.strikethrough = ptf.strikethrough;
tf.faceDilate = ptf.faceDilate;
tf.outlineSoftness = ptf.outlineSoftness;
tf.underlaySoftness = ptf.underlaySoftness;
_textField.textFormat = tf;
SetUnderlayDilate(ptf.underlayDilate);
}
(2)GTextField中实现创建GSubTextField的接口和刷新文字的接口:如下所示:
#if FAIRYGUI_TMPRO
public void CreateSubTextField()
{
if (_subGTextField == null)
{
_subGTextField = new GSubTextField();
_subGTextField.Setup_BeforeAdd(_textField.textFormat, this);
_subGTextField.relations.CopyFrom(this.relations);
if (parent!=null)
{
parent.AddChild(_subGTextField);
}
}
}
public void RefreshSubText()
{
if (_subGTextField != null)
{
if (_text.Equals(_subGTextField.text) == false)
{
_subGTextField.text = _text;
}
}
}
#endif
(3)FairyGUI中每次修改文本时都会调用TextField中的BuildLines2()重新构建字符,我们在这个接口中根据文本需不需要备用字体来决定是否需要创建GSubTextField及刷不刷新GSubTextField的文本,实现如下:
void BuildLines2()
{
float letterSpacing = _textFormat.letterSpacing * _fontSizeScale;
float lineSpacing = (_textFormat.lineSpacing - 1) * _fontSizeScale;
float rectWidth = _contentRect.width - GUTTER_X * 2;
float glyphWidth = 0, glyphHeight = 0, baseline = 0;
#if FAIRYGUI_TMPRO
bool isFallback;
bool fallback = false;
#endif
short wordLen = 0;
bool wordPossible = false;
float posx = 0;
TextFormat format = _textFormat;
_font.SetFormat(format, _fontSizeScale);
bool wrap = _wordWrap && !_singleLine;
if (_maxWidth > 0)
{
wrap = true;
rectWidth = _maxWidth - GUTTER_X * 2;
}
_textWidth = _textHeight = 0;
RequestText();
int elementCount = _elements.Count;
int elementIndex = 0;
HtmlElement element = null;
if (elementCount > 0)
element = _elements[elementIndex];
int textLength = _parsedText.Length;
LineInfo line = LineInfo.Borrow();
_lines.Add(line);
line.y = line.y2 = GUTTER_Y;
sLineChars.Clear();
for (int charIndex = 0; charIndex < textLength; charIndex++)
{
char ch = _parsedText[charIndex];
glyphWidth = glyphHeight = baseline = 0;
while (element != null && element.charIndex == charIndex)
{
if (element.type == HtmlElementType.Text)
{
format = element.format;
_font.SetFormat(format, _fontSizeScale);
}
else
{
IHtmlObject htmlObject = element.htmlObject;
if (_richTextField != null && htmlObject == null)
{
element.space = (int)(rectWidth - line.width - 4);
htmlObject = _richTextField.htmlPageContext.CreateObject(_richTextField, element);
element.htmlObject = htmlObject;
}
if (htmlObject != null)
{
glyphWidth = htmlObject.width + 2;
glyphHeight = htmlObject.height;
baseline = glyphHeight * IMAGE_BASELINE;
}
if (element.isEntity)
ch = '\0'; //indicate it is a place holder
}
elementIndex++;
if (elementIndex < elementCount)
element = _elements[elementIndex];
else
element = null;
}
if (ch == '\0' || ch == '\n')
{
wordPossible = false;
}
#if FAIRYGUI_TMPRO
else if (_font.GetGlyphWithFallback(ch == '\t' ? ' ' : ch, out glyphWidth, out glyphHeight, out baseline,out isFallback))
{
if ((isFallback == true)&&(this.gOwner!=null))
{
if(this.gOwner.GetType()==typeof(GTextField))
{
fallback = true;
if(isFirst == true)
{
isFirst = false;
((GTextField)gOwner).CreateSubTextField();
}
}
}
#else
else if (_font.GetGlyph(ch == '\t' ? ' ' : ch, out glyphWidth, out glyphHeight, out baseline))
{
#endif
if (ch == '\t')
glyphWidth *= 4;
if (wordPossible)
{
if (char.IsWhiteSpace(ch))
{
wordLen = 0;
}
else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z'
|| ch >= '0' && ch <= '9'
|| ch == '.' || ch == '"' || ch == '\''
|| format.specialStyle == TextFormat.SpecialStyle.Subscript
|| format.specialStyle == TextFormat.SpecialStyle.Superscript
|| _textDirection != RTLSupport.DirectionType.UNKNOW && RTLSupport.IsArabicLetter(ch))
{
wordLen++;
}
else
wordPossible = false;
}
else if (char.IsWhiteSpace(ch))
{
wordLen = 0;
wordPossible = true;
}
else if (format.specialStyle == TextFormat.SpecialStyle.Subscript
|| format.specialStyle == TextFormat.SpecialStyle.Superscript)
{
if (sLineChars.Count > 0)
{
wordLen = 2; //避免上标和下标折到下一行
wordPossible = true;
}
}
else
wordPossible = false;
}
else
wordPossible = false;
sLineChars.Add(new LineCharInfo() { width = glyphWidth, height = glyphHeight, baseline = baseline });
if (glyphWidth != 0)
{
if (posx != 0)
posx += letterSpacing;
posx += glyphWidth;
}
if (ch == '\n' && !_singleLine)
{
UpdateLineInfo(line, letterSpacing, sLineChars.Count);
LineInfo newLine = LineInfo.Borrow();
_lines.Add(newLine);
newLine.y = line.y + (line.height + lineSpacing);
if (newLine.y < GUTTER_Y) //lineSpacing maybe negative
newLine.y = GUTTER_Y;
newLine.y2 = newLine.y;
newLine.charIndex = line.charIndex + line.charCount;
sLineChars.Clear();
wordPossible = false;
posx = 0;
line = newLine;
}
else if (wrap && posx > rectWidth)
{
int lineCharCount = sLineChars.Count;
int toMoveChars;
if (wordPossible && wordLen < 20 && lineCharCount > 2) //if word had broken, move word to new line
{
toMoveChars = wordLen;
//we caculate the line width WITHOUT the tailing space
UpdateLineInfo(line, letterSpacing, lineCharCount - (toMoveChars + 1));
line.charCount++; //but keep it in this line.
}
else
{
toMoveChars = lineCharCount > 1 ? 1 : 0; //if only one char here, we cant move it to new line
UpdateLineInfo(line, letterSpacing, lineCharCount - toMoveChars);
}
LineInfo newLine = LineInfo.Borrow();
_lines.Add(newLine);
newLine.y = line.y + (line.height + lineSpacing);
if (newLine.y < GUTTER_Y)
newLine.y = GUTTER_Y;
newLine.y2 = newLine.y;
newLine.charIndex = line.charIndex + line.charCount;
posx = 0;
if (toMoveChars != 0)
{
for (int i = line.charCount; i < lineCharCount; i++)
{
LineCharInfo ci = sLineChars[i];
if (posx != 0)
posx += letterSpacing;
posx += ci.width;
}
sLineChars.RemoveRange(0, line.charCount);
}
else
sLineChars.Clear();
wordPossible = false;
line = newLine;
}
}
UpdateLineInfo(line, letterSpacing, sLineChars.Count);
if (_textWidth > 0)
_textWidth += GUTTER_X * 2;
_textHeight = line.y + line.height + GUTTER_Y;
_textWidth = Mathf.RoundToInt(_textWidth);
_textHeight = Mathf.RoundToInt(_textHeight);
#if FAIRYGUI_TMPRO
if ((fallback==true)||(fallback != lastNeedFallback))
{
lastNeedFallback = fallback;
((GTextField)gOwner).RefreshSubText();
}
#endif
}
另外在TMPFont中添加了一个接口以便判断文本是否需要备用字体,实现如下:
override public bool GetGlyphWithFallback(char ch, out float width, out float height, out float baseline,out bool isFallback)
{
isFallback = false;
_char = GetCharacterFromFontAssetWithoutFallback(ch, _style);
if (_char == null)
{
_char = GetCharacterFromFontAsset(ch, _style);
if(_char != null)
{
isFallback = true;
}
}
if(_char != null)
{
width = _char.glyph.metrics.horizontalAdvance * _boldMultiplier * _scale;
height = _lineHeight * _scale;
baseline = _ascent * _scale;
if (_format.specialStyle == TextFormat.SpecialStyle.Subscript)
{
height /= SupScale;
baseline /= SupScale;
}
else if (_format.specialStyle == TextFormat.SpecialStyle.Superscript)
{
height = height / SupScale + baseline * SupOffset;
baseline *= (SupOffset + 1 / SupScale);
}
height = Mathf.RoundToInt(height);
baseline = Mathf.RoundToInt(baseline);
return true;
}
width = 0;
height = 0;
baseline = 0;
return false;
}
TMP_Character GetCharacterFromFontAssetWithoutFallback(uint unicode, FontStyles fontStyle)
{
bool isAlternativeTypeface;
#pragma warning disable
TMP_FontAsset actualAsset;
#pragma warning restore
return TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, _fontAsset, false, fontStyle, _fontWeight,
out isAlternativeTypeface
//,out actualAsset //old TMP version need this line
);
}
(4)最后修改textField中的OnPopulateMesh,根据字符是否在当前字体中决定是否绘制字形用于渲染,修改如下:
public void OnPopulateMesh(VertexBuffer vb)
{
if (_textWidth == 0 && _lines.Count == 1)
{
if (_charPositions != null)
{
_charPositions.Clear();
_charPositions.Add(new CharPosition());
}
if (_richTextField != null)
_richTextField.RefreshObjects();
return;
}
float letterSpacing = _textFormat.letterSpacing * _fontSizeScale;
TextFormat format = _textFormat;
_font.SetFormat(format, _fontSizeScale);
_font.UpdateGraphics(graphics);
float rectWidth = _contentRect.width > 0 ? (_contentRect.width - GUTTER_X * 2) : 0;
float rectHeight = _contentRect.height > 0 ? Mathf.Max(_contentRect.height, _font.GetLineHeight(format.size)) : 0;
if (_charPositions != null)
_charPositions.Clear();
List<Vector3> vertList = vb.vertices;
List<Vector2> uvList = vb.uvs;
List<Vector2> uv2List = vb.uvs2;
List<Color32> colList = vb.colors;
HtmlLink currentLink = null;
float linkStartX = 0;
int linkStartLine = 0;
float posx = 0;
float indent_x;
bool clipping = !_input && _autoSize == AutoSizeType.None;
bool lineClipped;
AlignType lineAlign;
float glyphWidth, glyphHeight, baseline;
bool isFallback;
short vertCount;
float underlineStart;
float strikethroughStart;
int minFontSize;
int maxFontSize;
string rtlLine = null;
int elementIndex = 0;
int elementCount = _elements.Count;
HtmlElement element = null;
if (elementCount > 0)
element = _elements[elementIndex];
int lineCount = _lines.Count;
for (int i = 0; i < lineCount; ++i)
{
LineInfo line = _lines[i];
if (line.charCount == 0)
continue;
lineClipped = clipping && i != 0 && line.y + line.height > rectHeight;
lineAlign = format.align;
if (element != null && element.charIndex == line.charIndex)
lineAlign = element.format.align;
else
lineAlign = format.align;
if (_textDirection == RTLSupport.DirectionType.RTL)
{
if (lineAlign == AlignType.Center)
indent_x = (int)((rectWidth + line.width) / 2);
else if (lineAlign == AlignType.Right)
indent_x = rectWidth;
else
indent_x = line.width + GUTTER_X * 2;
if (indent_x > rectWidth)
indent_x = rectWidth;
posx = indent_x - GUTTER_X;
}
else
{
if (lineAlign == AlignType.Center)
indent_x = (int)((rectWidth - line.width) / 2);
else if (lineAlign == AlignType.Right)
indent_x = rectWidth - line.width;
else
indent_x = 0;
if (indent_x < 0)
indent_x = 0;
posx = GUTTER_X + indent_x;
}
int lineCharCount = line.charCount;
underlineStart = posx;
strikethroughStart = posx;
minFontSize = maxFontSize = format.size;
if (_textDirection != RTLSupport.DirectionType.UNKNOW)
{
rtlLine = _parsedText.Substring(line.charIndex, lineCharCount);
if (_textDirection == RTLSupport.DirectionType.RTL)
rtlLine = RTLSupport.ConvertLineR(rtlLine);
else
rtlLine = RTLSupport.ConvertLineL(rtlLine);
lineCharCount = rtlLine.Length;
}
for (int j = 0; j < lineCharCount; j++)
{
int charIndex = line.charIndex + j;
char ch = rtlLine != null ? rtlLine[j] : _parsedText[charIndex];
while (element != null && charIndex == element.charIndex)
{
if (element.type == HtmlElementType.Text)
{
vertCount = 0;
if (format.underline != element.format.underline)
{
if (format.underline)
{
if (!lineClipped)
{
float lineWidth;
if (_textDirection == RTLSupport.DirectionType.UNKNOW)
lineWidth = (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx) - underlineStart;
else
lineWidth = underlineStart - (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx);
if (lineWidth > 0)
vertCount += (short)_font.DrawLine(underlineStart < posx ? underlineStart : posx, -(line.y + line.baseline), lineWidth,
maxFontSize, 0, vertList, uvList, uv2List, colList);
}
maxFontSize = 0;
}
else
underlineStart = posx;
}
if (format.strikethrough != element.format.strikethrough)
{
if (format.strikethrough)
{
if (!lineClipped)
{
float lineWidth;
if (_textDirection == RTLSupport.DirectionType.UNKNOW)
lineWidth = (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx) - strikethroughStart;
else
lineWidth = strikethroughStart - (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx);
if (lineWidth > 0)
vertCount += (short)_font.DrawLine(strikethroughStart < posx ? strikethroughStart : posx, -(line.y + line.baseline), lineWidth,
minFontSize, 1, vertList, uvList, uv2List, colList);
}
minFontSize = int.MaxValue;
}
else
strikethroughStart = posx;
}
if (vertCount > 0 && _charPositions != null)
{
CharPosition cp = _charPositions[_charPositions.Count - 1];
cp.vertCount += vertCount;
_charPositions[_charPositions.Count - 1] = cp;
}
format = element.format;
minFontSize = Math.Min(minFontSize, format.size);
maxFontSize = Math.Max(maxFontSize, format.size);
_font.SetFormat(format, _fontSizeScale);
}
else if (element.type == HtmlElementType.Link)
{
currentLink = (HtmlLink)element.htmlObject;
if (currentLink != null)
{
element.position = Vector2.zero;
currentLink.SetPosition(0, 0);
linkStartX = posx;
linkStartLine = i;
}
}
else if (element.type == HtmlElementType.LinkEnd)
{
if (currentLink != null)
{
currentLink.SetArea(linkStartLine, linkStartX, i, posx);
currentLink = null;
}
}
else
{
IHtmlObject htmlObj = element.htmlObject;
if (htmlObj != null)
{
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= htmlObj.width - 2;
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.imgIndex = (short)(elementIndex + 1);
cp.offsetX = posx;
cp.width = (short)htmlObj.width;
_charPositions.Add(cp);
}
if (lineClipped || clipping && (posx < GUTTER_X || posx > GUTTER_X && posx + htmlObj.width > _contentRect.width - GUTTER_X))
element.status |= 1;
else
element.status &= 254;
element.position = new Vector2(posx + 1, line.y + line.baseline - htmlObj.height * IMAGE_BASELINE);
htmlObj.SetPosition(element.position.x, element.position.y);
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += htmlObj.width + letterSpacing + 2;
}
}
if (element.isEntity)
ch = '\0';
elementIndex++;
if (elementIndex < elementCount)
element = _elements[elementIndex];
else
element = null;
}
if (ch == '\0')
continue;
#if FAIRYGUI_TMPRO
if(_font.GetType() == typeof(TMPFont))
{
if (_font.GetGlyphWithFallback(ch == '\t' ? ' ' : ch, out glyphWidth, out glyphHeight, out baseline,out isFallback))
{
if (ch == '\t')
glyphWidth *= 4;
if (_textDirection == RTLSupport.DirectionType.RTL)
{
if (lineClipped || clipping && (rectWidth < 7 || posx != (indent_x - GUTTER_X)) && posx < GUTTER_X - 0.5f) //超出区域,剪裁
{
posx -= (letterSpacing + glyphWidth);
continue;
}
posx -= glyphWidth;
}
else
{
if (lineClipped || clipping && (rectWidth < 7 || posx != (GUTTER_X + indent_x)) && posx + glyphWidth > _contentRect.width - GUTTER_X + 0.5f) //超出区域,剪裁
{
posx += letterSpacing + glyphWidth;
continue;
}
}
if (isFallback == false)
{
vertCount = (short)_font.DrawGlyph(posx, -(line.y + line.baseline), vertList, uvList, uv2List, colList);
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.vertCount = vertCount;
cp.offsetX = posx;
cp.width = (short)glyphWidth;
_charPositions.Add(cp);
}
}
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += letterSpacing + glyphWidth;
}
else //if GetGlyph failed
{
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.offsetX = posx;
_charPositions.Add(cp);
}
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += letterSpacing;
}
}
else
{
if (_font.GetGlyph(ch == '\t' ? ' ' : ch, out glyphWidth, out glyphHeight, out baseline))
{
if (ch == '\t')
glyphWidth *= 4;
if (_textDirection == RTLSupport.DirectionType.RTL)
{
if (lineClipped || clipping && (rectWidth < 7 || posx != (indent_x - GUTTER_X)) && posx < GUTTER_X - 0.5f) //超出区域,剪裁
{
posx -= (letterSpacing + glyphWidth);
continue;
}
posx -= glyphWidth;
}
else
{
if (lineClipped || clipping && (rectWidth < 7 || posx != (GUTTER_X + indent_x)) && posx + glyphWidth > _contentRect.width - GUTTER_X + 0.5f) //超出区域,剪裁
{
posx += letterSpacing + glyphWidth;
continue;
}
}
vertCount = (short)_font.DrawGlyph(posx, -(line.y + line.baseline), vertList, uvList, uv2List, colList);
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.vertCount = vertCount;
cp.offsetX = posx;
cp.width = (short)glyphWidth;
_charPositions.Add(cp);
}
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += letterSpacing + glyphWidth;
}
else //if GetGlyph failed
{
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.offsetX = posx;
_charPositions.Add(cp);
}
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += letterSpacing;
}
}
#else
if (_font.GetGlyph(ch == '\t' ? ' ' : ch, out glyphWidth, out glyphHeight, out baseline))
{
Debug.Log("OnPopulateMesh GetGlyph:" + ch);
if (ch == '\t')
glyphWidth *= 4;
if (_textDirection == RTLSupport.DirectionType.RTL)
{
if (lineClipped || clipping && (rectWidth < 7 || posx != (indent_x - GUTTER_X)) && posx < GUTTER_X - 0.5f) //超出区域,剪裁
{
posx -= (letterSpacing + glyphWidth);
continue;
}
posx -= glyphWidth;
}
else
{
if (lineClipped || clipping && (rectWidth < 7 || posx != (GUTTER_X + indent_x)) && posx + glyphWidth > _contentRect.width - GUTTER_X + 0.5f) //超出区域,剪裁
{
posx += letterSpacing + glyphWidth;
continue;
}
}
vertCount = (short)_font.DrawGlyph(posx, -(line.y + line.baseline), vertList, uvList, uv2List, colList);
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.vertCount = vertCount;
cp.offsetX = posx;
cp.width = (short)glyphWidth;
_charPositions.Add(cp);
}
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += letterSpacing + glyphWidth;
}
else //if GetGlyph failed
{
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.offsetX = posx;
_charPositions.Add(cp);
}
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += letterSpacing;
}
#endif
}//text loop
if (!lineClipped)
{
vertCount = 0;
if (format.underline)
{
float lineWidth;
if (_textDirection == RTLSupport.DirectionType.UNKNOW)
lineWidth = (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx) - underlineStart;
else
lineWidth = underlineStart - (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx);
if (lineWidth > 0)
vertCount += (short)_font.DrawLine(underlineStart < posx ? underlineStart : posx, -(line.y + line.baseline), lineWidth,
maxFontSize, 0, vertList, uvList, uv2List, colList);
}
if (format.strikethrough)
{
float lineWidth;
if (_textDirection == RTLSupport.DirectionType.UNKNOW)
lineWidth = (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx) - strikethroughStart;
else
lineWidth = strikethroughStart - (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx);
if (lineWidth > 0)
vertCount += (short)_font.DrawLine(strikethroughStart < posx ? strikethroughStart : posx, -(line.y + line.baseline), lineWidth,
minFontSize, 1, vertList, uvList, uv2List, colList);
}
if (vertCount > 0 && _charPositions != null)
{
CharPosition cp = _charPositions[_charPositions.Count - 1];
cp.vertCount += vertCount;
_charPositions[_charPositions.Count - 1] = cp;
}
}
}//line loop
if (element != null && element.type == HtmlElementType.LinkEnd && currentLink != null)
currentLink.SetArea(linkStartLine, linkStartX, lineCount - 1, posx);
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)(lineCount - 1);
cp.charIndex = _charPositions.Count;
cp.offsetX = posx;
_charPositions.Add(cp);
}
int count = vertList.Count;
if (count > 65000)
{
Debug.LogWarning("Text is too large. A mesh may not have more than 65000 vertices.");
vertList.RemoveRange(65000, count - 65000);
colList.RemoveRange(65000, count - 65000);
uvList.RemoveRange(65000, count - 65000);
if (uv2List.Count > 0)
uv2List.RemoveRange(65000, count - 65000);
count = 65000;
}
if (_font.customOutline)
{
bool hasShadow = _textFormat.shadowOffset.x != 0 || _textFormat.shadowOffset.y != 0;
int allocCount = count;
int drawDirs = 0;
if (_textFormat.outline != 0)
{
drawDirs = UIConfig.enhancedTextOutlineEffect ? 8 : 4;
allocCount += count * drawDirs;
}
if (hasShadow)
allocCount += count;
if (allocCount > 65000)
{
Debug.LogWarning("Text is too large. Outline/shadow effect cannot be completed.");
allocCount = count;
}
if (allocCount != count)
{
VertexBuffer vb2 = VertexBuffer.Begin();
List<Vector3> vertList2 = vb2.vertices;
List<Color32> colList2 = vb2.colors;
Color32 col = _textFormat.outlineColor;
float outline = _textFormat.outline;
if (outline != 0)
{
for (int j = 0; j < drawDirs; j++)
{
for (int i = 0; i < count; i++)
{
Vector3 vert = vertList[i];
vertList2.Add(new Vector3(vert.x + STROKE_OFFSET[j * 2] * outline, vert.y + STROKE_OFFSET[j * 2 + 1] * outline, 0));
colList2.Add(col);
}
vb2.uvs.AddRange(uvList);
if (uv2List.Count > 0)
vb2.uvs2.AddRange(uv2List);
}
}
if (hasShadow)
{
col = _textFormat.shadowColor;
Vector2 offset = _textFormat.shadowOffset;
for (int i = 0; i < count; i++)
{
Vector3 vert = vertList[i];
vertList2.Add(new Vector3(vert.x + offset.x, vert.y - offset.y, 0));
colList2.Add(col);
}
vb2.uvs.AddRange(uvList);
if (uv2List.Count > 0)
vb2.uvs2.AddRange(uv2List);
}
vb.Insert(vb2);
vb2.End();
}
}
vb.AddTriangles();
if (_richTextField != null)
_richTextField.RefreshObjects();
}
目前还有很多支持不完善的地方,如果运行中动态修改GTextField的属性,需要将对应属性的变动同步到GSubTextField中,目前看只能在GTextField每个属性设置的地方添加实现,没找到更容易扩展的方式。开始想把GSubTextField挂在GTextField下作为子节点,这样诸如位置、缩放等的动态改变就不用额外考虑了,但实现中发现GTextField本身不是GComponen组件,由于本身对FairyGUI源码不太熟,不知道如何挂载,只能放在GTextField的同级下。
网友评论