1. 程式人生 > >【Unity3D / UI】UGUI Text 文字調整字間距,並適配對齊方式

【Unity3D / UI】UGUI Text 文字調整字間距,並適配對齊方式

問題描述

最近的專案美術提需求要調整遊戲得分顯示的數字間隔,發現UGUI的Text元件只提供了行間距的調整,而無法調整字與字之間的間隔,搜尋之後發現一篇博文 UGUI之修改Text字間距 ,按文中提供的程式碼,可以實現文字字間距調整,但是隻支援左對齊方式,居中對齊和右對齊時,文字依舊是從左向右計算網格位置,佈局都是不對的,因而需要在原文程式碼的基礎上,做出一些調整。

問題分析

UGUI Text 元件中的每個文字,是由兩個網格組成,因而調整字間距,可以通過修改每個字的網格頂點位置來實現。上文提到的博文中,只實現了從左向右以第一個字的座標為起點,依次增加字元寬度和間距,作為偏移量計算出後續每個字的位置。但是右對齊時,文字位置需要從每行最後一個字作為起始位置,依次向左計算偏移量;居中對齊時,需要以第一個字作為起始位置,依次向右計算偏移量,但需要額外減去半行長度,時整行居中。半行長度的計算,需要考慮字元數的奇偶,奇數個字元時,需要額外偏移0.5個字元寬度。 根據以上思路,調整原博文中的程式碼如下:

實現效果

左對齊 居中對齊 右對齊

程式碼

using System.Collections.Generic;

namespace UnityEngine.UI
{
    [AddComponentMenu("UI/Effects/TextSpacing")]
    public class TextSpacing : BaseMeshEffect
    {
        #region Struct
        public enum HorizontalAligmentType
        {
            Left,
            Center,
            Right
        }

        public class Line
        {
            // 起點索引
            public int StartVertexIndex
            {
                get
                {
                    return _startVertexIndex;
                }
            }
            private int _startVertexIndex = 0;

            // 終點索引
            public int EndVertexIndex
            {
                get
                {
                    return _endVertexIndex;
                }
            }
            private int _endVertexIndex = 0;

            // 該行佔的點數目
            public int VertexCount
            {
                get
                {
                    return _vertexCount;
                }
            }
            private int _vertexCount = 0;

            public Line(int startVertexIndex, int length)
            {
                _startVertexIndex = startVertexIndex;
                _endVertexIndex = length * 6 - 1 + startVertexIndex;
                _vertexCount = length * 6;
            }
        } 
        #endregion

        public float Spacing = 1f;

        public override void ModifyMesh(VertexHelper vh)
        {
            if (!IsActive() || vh.currentVertCount == 0)
            {
                return;
            }

            var text = GetComponent<Text>();

            if (text == null)
            {
                Debug.LogError("Missing Text component");
                return;
            }

            // 水平對齊方式
            HorizontalAligmentType alignment;
            if (text.alignment == TextAnchor.LowerLeft || text.alignment == TextAnchor.MiddleLeft || text.alignment == TextAnchor.UpperLeft)
            {
                alignment = HorizontalAligmentType.Left;
            }
            else if(text.alignment == TextAnchor.LowerCenter || text.alignment == TextAnchor.MiddleCenter || text.alignment == TextAnchor.UpperCenter)
            {
                alignment = HorizontalAligmentType.Center;
            }
            else
            {
                alignment = HorizontalAligmentType.Right;
            }

            var vertexs = new List<UIVertex>();
            vh.GetUIVertexStream(vertexs);
            // var indexCount = vh.currentIndexCount;

            var lineTexts = text.text.Split('\n');

            var lines = new Line[lineTexts.Length];

            // 根據lines陣列中各個元素的長度計算每一行中第一個點的索引,每個字、字母、空母均佔6個點
            for (var i = 0; i < lines.Length; i++)
            {
                // 除最後一行外,vertexs對於前面幾行都有回車符佔了6個點
                if (i == 0)
                {
                    lines[i] = new Line(0, lineTexts[i].Length + 1);
                }
                else if (i > 0 && i < lines.Length - 1)
                {
                    lines[i] = new Line(lines[i - 1].EndVertexIndex + 1, lineTexts[i].Length + 1);
                }
                else
                {
                    lines[i] = new Line(lines[i - 1].EndVertexIndex + 1, lineTexts[i].Length);
                }
            }

            UIVertex vt;

            for (var i = 0; i < lines.Length; i++)
            {
                for (var j = lines[i].StartVertexIndex; j <= lines[i].EndVertexIndex; j++)
                {
                    if (j < 0 || j >= vertexs.Count)
                    {
                        continue;
                    }
                    vt = vertexs[j];

                    var charCount = lines[i].EndVertexIndex - lines[i].StartVertexIndex;
                    if (i == lines.Length - 1)
                    {
                        charCount += 6;
                    }

                    if (alignment == HorizontalAligmentType.Left)
                    {
                        vt.position += new Vector3(Spacing * ((j - lines[i].StartVertexIndex) / 6), 0, 0);
                    }
                    else if (alignment == HorizontalAligmentType.Right)
                    {
                        vt.position += new Vector3(Spacing * (-(charCount - j + lines[i].StartVertexIndex) / 6 + 1), 0, 0);
                    }
                    else if (alignment == HorizontalAligmentType.Center)
                    {
                        var offset = (charCount / 6) % 2 == 0 ? 0.5f : 0f;
                        vt.position += new Vector3(Spacing * ((j - lines[i].StartVertexIndex) / 6 - charCount / 12 + offset), 0, 0);
                    }

                    vertexs[j] = vt;
                    // 以下注意點與索引的對應關係
                    if (j % 6 <= 2)
                    {
                        vh.SetUIVertex(vt, (j / 6) * 4 + j % 6);
                    }
                    if (j % 6 == 4)
                    {
                        vh.SetUIVertex(vt, (j / 6) * 4 + j % 6 - 1);
                    }
                }
            }
        }
    }
}