• 【Unity记录】【解析几何】令文本保持字符间距地环绕在圆弧上(将线段映射到圆弧上)


    本文内容

    阅读须知:

    • 本文注重介绍做法;原理只给出,不进行解释(因为我也不会涅😅)
    • 该做法只适合半圆弧内的映射,因为其并不是将直线上的点等距离地映射至圆弧上,但在半圆弧范围内,肉眼几乎无法感知距离的差异
    • 只考虑了单行的实现,多行文字如不加修改,感官上会稍微差一些

    本文将介绍:

    • 利用复变函数 w = 1 / z w=1/z w=1/z将直线映射到圆弧上的数学方法(Matlab实现与C#实现)
    • 计算线段在圆上等长弧的两端点(C#实现)
    • 在Unity中应用该做法将TextMeshPro文本以圆弧形式排列渲染(C#实现)

    最终效果

    有时我们需要将文字环绕在圆弧上,比如你的美术突然给了你这么一个素材:
    啪的一下很酷啊,但问题来了,应该怎么把文字贴上圆弧呢?

    使用本文介绍的方法,结果是这样的:
    在这里插入图片描述

    可以看到,无论是中文字符还是英文字符,都保留了其原有的字符间距,并环绕在圆弧上
    就算字符不足以撑满圆弧,字符的间距也不会分散:

    但是注意,如果文本超过了1/3圆弧的位置,还是会肉眼看到字符存在差异:


    而且这种差异会随着字符增加进一步明显,超过2/3时达到几乎不可用的状态。

    建议使用的情况:
    也就是这个做法最多只能用于半圆弧的文字排布;
    如果需要全圆排布,可以参考现有脚本部分的第二个做法;
    如果那个做法也无法满足你的需求,那你可能就需要再想办法写一个了涅。

    实现方法

    现有做法

    TextMeshPro Sample Scene 25

    最开始肯定不愿意自己写,上网找现成的脚本。
    TextMeshPro官方的Sample Scene 25有一个弯曲文本的实现,但那个做法只改变了Y坐标与旋转,而X坐标没做修改,结果就是,如果文字长度并非远小于半径时,会得到这样的结果:

    在靠近两侧的地方,文字间距呈灾难式递增。

    建议使用的情况:

    • 文字只在圆弧1/4内

    CurvedTextMeshPro

    这是由TonyViT编写的脚本
    它可以360°环形排布多行文本,但是这个做法必须手动设定每个字符的间隔,而摒弃了字符本身的间距设定。当使用一个设定应对不同的文字时,可能会出现重叠或分散的结果,比如:
    按照中文手动调整好间距,显示正常:

    如果此时换成英文,或改变文字,可能会出现下面的情况:

    可见,英文字符的间距被明显拉大
    建议使用的情况:

    • 需要360°文本环绕
    • 沿椭圆或其它函数图像环绕(没错,这个做法可以借助一些数学计算以其它曲线实现)
    • 文字的间距只要求均匀分布,对紧凑不做要求

    本文做法

    说白了就是想要保持字符本身的间距。以该角度入手,事实上相当于把线段沿圆弧附着。(想象一下胶带依附于中心轮的关系)
    因此进一步分析就是:需要将线段映射到圆弧上。

    均匀地映射肯定是最好,但本人才疏学浅,暂时没找到完全均匀映射的数学做法,只找到了一个在一定范围内可接受的方法。

    线段圆弧映射

    该做法利用复变函数 w = 1 / z w=1/z w=1/z将直线上的点映射到圆上。
    具体的做法嘛……我也不会啊😭,我就一敲代码的……
    但总之我在Stack Exchange上找到了一个大佬的解释:
    原文链接

    问题描述

    I’m looking for a mapping f : R 2 → R 2 f: \mathbb{R}^2 \rightarrow \mathbb{R}^2 f:R2R2 that converts a line segment A A ′ A A^{\prime} AA with two end points A = ( x A , y A ) A=\left(x_A, y_A\right) A=(xA,yA) and A = ( x A ′ , y A ′ ) A=\left(x_{A^{\prime}}, y_{A^{\prime}}\right) A=(xA,yA) to an arc ⁡ B B ′ \operatorname{arc} B B^{\prime} arcBB with radius r r r centered at O O O, with end points B = ( x B , y B ) B=\left(x_B, y_B\right) B=(xB,yB) and B ′ = ( x B ′ , y B ′ ) B^{\prime}=\left(x_{B^{\prime}}, y_{B^{\prime}}\right) B=(xB,yB).

    This mapping should map A A A to B B B and A ′ A^{\prime} A to B ′ B^{\prime} B. Does anyone know a closed-form relation for such mapping?

    I know that mappings such as w = 1 / z w=1 / z w=1/z in complex plain can convert lines to circles. But I don’t know how to modify this to convert a particular line segment to an arc given the endpoints and radius.

    解决方案

    Treat everything as complex numbers, let P P P be a point on the line segment A A ′ A A^{\prime} AA. The map
    P ↦ 1 A ′ − A ( P − A + A ′ 2 ) P \mapsto \frac{1}{A^{\prime}-A}\left(P-\frac{A+A^{\prime}}{2}\right) PAA1(P2A+A)

    sends the line segment A A ′ A A^{\prime} AA to the line segment joining − 1 2 -\frac{1}{2} 21 to 1 2 \frac{1}{2} 21. Multiply B ′ − B B^{\prime}-B BB will rotate and scale the line segment to a parallel copy of B B ′ B B^{\prime} BB. After another translation, the map
    P ↦ P ′ =  def  B + B ′ 2 + B ′ − B A ′ − A ( P − A + A ′ 2 ) P \mapsto P^{\prime} \stackrel{\text { def }}{=} \frac{B+B^{\prime}}{2}+\frac{B^{\prime}-B}{A^{\prime}-A}\left(P-\frac{A+A^{\prime}}{2}\right) PP= def 2B+B+AABB(P2A+A)
    sends line segment A A ′ A A^{\prime} AA to line segment B B ′ B B^{\prime} BB.

    To send line segment B B ′ B B^{\prime} BB to an arc joining them, you first figure out the mid point M M M of the circular arc B B ′ B B^{\prime} BB and let N = 2 O − M N=2 O-M N=2OM be the antipodal point of M M M on the circle holding the arc.
    N = O − 2 r 4 r 2 − ∣ B − B ′ ∣ 2 ( B + B ′ 2 − O ) N=O-\frac{2 r}{\sqrt{4 r^2-\left|B-B^{\prime}\right|^2}}\left(\frac{B+B^{\prime}}{2}-O\right) N=O4r2BB2 2r(2B+BO)
    If you perform a circle inversion with respect to N N N and radius R = ∣ B − N ∣ = ∣ B ′ − N ∣ R=|B-N|=\left|B^{\prime}-N\right| R=BN=BN, the point P ′ P^{\prime} P will get mapped to a point P ′ ′ P^{\prime \prime} P′′ on the circular arc joining B B ′ B B^{\prime} BB.
    P ′ ↦ P ′ ′ =  def  N + R 2 P ˉ ′ − N ˉ P^{\prime} \mapsto P^{\prime \prime} \stackrel{\text { def }}{=} N+\frac{R^2}{\bar{P}^{\prime}-\bar{N}} PP′′= def N+PˉNˉR2
    Please note that in denominator of above expression, you need to take complex conjugation for P ′ P^{\prime} P and N N N.

    Matlab实现

    作为一个码农我唯一能做的就是测试一下顶不顶真了。

    O = 0i +0;
    B = sqrt(2)/2 + sqrt(2)/2i;
    B1 = sqrt(2)/2 - sqrt(2)/2i;
    A1 = 2 + 1i;
    A = 2 - 1i;
    P = 2 - 0.5i;
    
    BB1toBB1arc(CalculateN(B, B1, O, 1), B, AA1toBB1(A,A1,B,B1,P))
    
    function P1 = AA1toBB1(a, a1, b, b1, P)
        P1 = (b+b1)/2+(b1-b)/(a1-a)*(P-(a+a1)/2);
    end
    
    function N = CalculateN(b, b1, o, r)
        N = o - 2 * r/sqrt(4*r^2-abs(b-b1)^2)*((b+b1)/2-o);
    end
    
    function P2 = BB1toBB1arc(n, b, P1)
        R = abs(b-n);
        P2 = n + R^2/(conj(P1)-conj(n));
        
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    寻找与线段等长的圆弧

    给定圆心、弧中点和线段长度就可以计算出半径及应向两端延伸的距离。进而利用坐标关系求出弧的两端点:

    C#代码

    Vector2 O;
    Vector2 MapMidPoint;
    float Radius = (O - MapMidPoint).Length();
    
    //求线段在圆弧上的端点(中点位置往圆两侧延伸各一半)
    void CalculateEndPoints(float length)
    {
        float lineHalfLength = length / 2;
    
        B = VectorAsComplex(CalculateEndPoint(true));
        B1 = VectorAsComplex(CalculateEndPoint(false));
    
        Vector2 CalculateEndPoint(bool clockwise)
        {
            var angle = MathF.Atan2(MapMidPoint.Y - O.Y, MapMidPoint.X - O.X);
            if (clockwise)
            {
                angle = angle - lineHalfLength / Radius;
            }
            else
            {
                angle = angle + lineHalfLength / Radius;
            }
            Vector2 result = new Vector2(O.X + Radius * MathF.Cos(angle), O.Y + Radius * MathF.Sin(angle));
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    线段圆弧映射(C#实现)

    Line2CirArcTransformator 是进行映射的类,创造对象时就会预计算好所有参数,当以直线上的点P作为函数MapLinePoint(Vector2 p)的输入时,就会给出其在对应圆上的位置。
    该类不只局限于Unity使用,在任何需要用到相关计算的场景都可以调用。

    该脚本的另一个构造函数还支持指定弧上两端点进行映射。

    using System;
    using System.Numerics;
    
    namespace MapLineSegmentToIsometricArc
    {
        /* 求线段在某个圆中对应长的弧端点:
         * https://math.stackexchange.com/questions/275201/how-to-find-an-end-point-of-an-arc-given-another-end-point-radius-and-arc-dire
         * 在虚数空间中求线段在弧中的对应位置映射:
         * https://math.stackexchange.com/questions/3912758/a-mapping-that-converts-a-line-segment-to-an-arc
         */
        class Program
        {
            static void Main(string[] args)
            {
                const float ya = 4;
                //线段
                var A = new Vector2(2, -ya);
                var A1 = new Vector2(2, ya);
                //单位圆
                var O = new Vector2(0, 0);
                var B = new Vector2(MathF.Sqrt(2) / 2, MathF.Sqrt(2) / 2);
                var B1 = new Vector2(MathF.Sqrt(2) / 2, -MathF.Sqrt(2) / 2);
    
                var transformator = new Line2CirArcTransformator(A, A1, B, B1, 1); //向给定弧映射
    
                Console.WriteLine(transformator.MapLinePoint(new Vector2(2, ya)));
                Console.WriteLine(transformator.MapLinePoint(new Vector2(2, 0)));
                Console.WriteLine(transformator.MapLinePoint(new Vector2(2, -ya)));
    
                transformator = new Line2CirArcTransformator(A, A1, O, new Vector2(1, 0)); //找等长弧映射
                Console.WriteLine(transformator.MapLinePoint(new Vector2(2, ya)));
                Console.WriteLine(transformator.MapLinePoint(new Vector2(2, 0)));
                Console.WriteLine(transformator.MapLinePoint(new Vector2(2, -ya)));
            }
        }
    
        public class Line2CirArcTransformator
        {
            public Complex A1 { get; private set; }
            public Complex A { get; private set; }
            public Complex LineMidPoint { get; private set; }
            public Complex B { get; private set; }
            public Complex B1 { get; private set; }
            public Complex BBMidPoint { get; private set; }
            public Complex O { get; private set; }
            public double Radius { get; private set;}
            private Complex N { get; set; }
            private double InversionCircleRadius { get; set; }
            /// 
            /// 计算P1时会用到的中间变量
            /// 
            private Complex FactorForP { get; set; }
    
            /// 
            /// 根据给定圆上位置找出一条等长弧并映射直线上的点
            /// 根据线段长度和圆半径决定优劣弧
            /// 
            /// 直线的端点1
            /// 直线的端点1
            /// 圆心
            /// 映射圆弧在圆上的中点位置
            public Line2CirArcTransformator(Vector2 A, Vector2 A1, Vector2 O, Vector2 MapMidPoint)
            {
                float Radius = (O - MapMidPoint).Length();
                this.Radius = Radius;
                float segmentLength = (A - A1).Length();
                CalculateEndPoints(segmentLength);
    
                Initialize(A, A1, O, segmentLength > MathF.PI * Radius);
    
                Console.WriteLine("B: " + B);
                Console.WriteLine("B1:" + B1);
    
                //求线段在圆弧上的端点(中点位置往圆两侧延伸各一半)
                void CalculateEndPoints(float length)
                {
                    float lineHalfLength = length / 2;
    
                    B = VectorAsComplex(CalculateEndPoint(true));
                    B1 = VectorAsComplex(CalculateEndPoint(false));
    
                    Vector2 CalculateEndPoint(bool clockwise)
                    {
                        var angle = MathF.Atan2(MapMidPoint.Y - O.Y, MapMidPoint.X - O.X);
                        if (clockwise)
                        {
                            angle = angle - lineHalfLength / Radius;
                        }
                        else
                        {
                            angle = angle + lineHalfLength / Radius;
                        }
                        Vector2 result = new Vector2(O.X + Radius * MathF.Cos(angle), O.Y + Radius * MathF.Sin(angle));
                        return result;
                    }
                }
            }
    
            /// 
            /// 根据给定的弧映射直线上的点
            /// 所确定的圆心永远在自B向B1的右边
            /// 需要指定优劣弧,默认为劣弧
            /// 
            /// 直线的端点1
            /// 直线的端点1
            /// 圆弧的端点1
            /// 圆弧的端点2
            /// 圆的半径
            public Line2CirArcTransformator(Vector2 A, Vector2 A1, Vector2 B, Vector2 B1, float Radius, bool isMajorArc = false)
            {
                this.Radius = Radius;
                this.B = VectorAsComplex(B);
                this.B1 = VectorAsComplex(B1);
                Initialize(A, A1, CalculateO(), isMajorArc);
    
                Console.WriteLine("B: " + B);
                Console.WriteLine("B1:" + B1);
    
                //https://math.stackexchange.com/questions/1781438/finding-the-center-of-a-circle-given-two-points-and-a-radius-algebraically
                Vector2 CalculateO()
                {
                    var xa = (B1.X - B.X) / 2;
                    var ya = (B1.Y - B.Y) / 2;
                    var a = MathF.Sqrt(xa * xa + ya * ya);
                    var b = MathF.Sqrt(Radius * Radius - a * a);
                    return new Vector2((B.X + B1.X) / 2 + b * ya / a, (B.Y + B1.Y) / 2 - b * xa / a);
                }
            }
    
            /// 
            /// 初始化除B以外的变量
            /// 
            /// 直线的端点1
            /// 直线的端点1
            /// 圆心
            private void Initialize(Vector2 A, Vector2 A1, Vector2 O, bool isMajorArc)
            {
                //测试时发现需要调转个方向
                this.A1 = VectorAsComplex(A);
                this.A = VectorAsComplex(A1);
                this.O = VectorAsComplex(O);
                //this.MapMidPoint = VectorAsComplex(MapMidPoint);
    
                LineMidPoint = VectorAsComplex((A + A1) / 2);
    
                //B = VectorAsComplex(new Vector2(MathF.Sqrt(2)/2, MathF.Sqrt(2) / 2));
                //B1 = VectorAsComplex(new Vector2(MathF.Sqrt(2)/2, -MathF.Sqrt(2) / 2));
                BBMidPoint = (B + B1) / 2;
                N = CalculateN();
                InversionCircleRadius = (B - N).Magnitude;
                FactorForP = (B1 - B) / (this.A1 - this.A);
    
                Complex CalculateN()
                {
                    return this.O  + (isMajorArc ? 1: -1) * 2 * Radius / Math.Sqrt(4 * Radius * Radius - Math.Pow((B - B1).Magnitude, 2)) * (BBMidPoint - this.O);
                }
            }
    
            private Complex VectorAsComplex(Vector2 vector) => new Complex(vector.X, vector.Y);
    
            public Vector2 MapLinePoint(Vector2 point)
            {
                var p = VectorAsComplex(point);
                // AA' to BB'
                Complex p1 = BBMidPoint + FactorForP * (p - LineMidPoint);
                Complex result = N + InversionCircleRadius * InversionCircleRadius / (Complex.Conjugate(p1) - Complex.Conjugate(N));
                return new Vector2((float)result.Real, (float)result.Imaginary);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171

    Unity中实现TextMeshPro文字弯曲

    1. 引入上面的脚本(低版本Unity需要手动将MathF库改为UnityEngine.Mathf)
    2. 改写TextMeshPro Sample Scene 25的脚本
    using UnityEngine;
    using TMPro;
    
    namespace Scripts.Utilities
    {
        [ExecuteInEditMode]
        public class TextProOnACircleCurve : MonoBehaviour
        {
            [SerializeField]
            private float radius = 50;
            /// 
            /// The text component of interest
            /// 
            private TMP_Text m_TextComponent;
    
            /// 
            /// True if the text must be updated at this frame 
            /// 
            private bool m_forceUpdate;
    
            /// 
            /// Awake
            /// 
            private void Awake()
            {
                m_TextComponent = gameObject.GetComponent<TMP_Text>();
            }
    
            /// 
            /// OnEnable
            /// 
            private void OnEnable()
            {
                //every time the object gets enabled, we have to force a re-creation of the text mesh
                m_forceUpdate = true;
            }
    
            /// 
            /// Update
            /// 
            protected void Update()
            {
                //if the text and the parameters are the same of the old frame, don't waste time in re-computing everything
                if (!m_forceUpdate && !m_TextComponent.havePropertiesChanged)
                {
                    return;
                }
    
                m_forceUpdate = false;
    
                //during the loop, vertices represents the 4 vertices of a single character we're analyzing, 
                //while matrix is the roto-translation matrix that will rotate and scale the characters so that they will
                //follow the curve
                Vector3[] vertices;
                Matrix4x4 matrix;
    
                //Generate the mesh and get information about the text and the characters
                m_TextComponent.ForceMeshUpdate();
    
                TMP_TextInfo textInfo = m_TextComponent.textInfo;
                int characterCount = textInfo.characterCount;
    
                //if the string is empty, no need to waste time
                if (characterCount == 0)
                    return;
    
                //gets the bounds of the rectangle that contains the text 
                float boundsMinX = m_TextComponent.bounds.min.x;
                float boundsMaxX = m_TextComponent.bounds.max.x;
    
    
                var origin = new System.Numerics.Vector2(0, 0 - radius);
                var transformator = new Line2CirArcTransformator(
                    new System.Numerics.Vector2(boundsMinX, 0),
                    new System.Numerics.Vector2(boundsMaxX, 0),
                    origin,
                    new System.Numerics.Vector2(0, 0));
    
                //for each character
                for (int i = 0; i < characterCount; i++)
                {
                    //skip if it is invisible
                    if (!textInfo.characterInfo[i].isVisible)
                        continue;
    
                    //Get the index of the mesh used by this character, then the one of the material... and use all this data to get
                    //the 4 vertices of the rect that encloses this character. Store them in vertices
                    int vertexIndex = textInfo.characterInfo[i].vertexIndex;
                    int materialIndex = textInfo.characterInfo[i].materialReferenceIndex;
                    vertices = textInfo.meshInfo[materialIndex].vertices;
    
                    //Compute the baseline mid point for each character. This is the central point of the character.
                    //we will use this as the point representing this character for the geometry transformations
                    Vector3 offsetToMidBaseline = new Vector2((vertices[vertexIndex + 0].x + vertices[vertexIndex + 2].x) / 2, textInfo.characterInfo[i].baseLine);
    
                    //remove the central point from the vertices point. After this operation, every one of the four vertices 
                    //will just have as coordinates the offset from the central position. This will come handy when will deal with the rotations
                    vertices[vertexIndex + 0] += -offsetToMidBaseline;
                    vertices[vertexIndex + 1] += -offsetToMidBaseline;
                    vertices[vertexIndex + 2] += -offsetToMidBaseline;
                    vertices[vertexIndex + 3] += -offsetToMidBaseline;
    
                    var result = transformator.MapLinePoint(new System.Numerics.Vector2(offsetToMidBaseline.x, offsetToMidBaseline.y));
    
                    //calculate atan2 as if the origin of circle is (0,0)
                    var rayResult = result - origin;
                    float angle = Mathf.Atan2(rayResult.Y, rayResult.X); //we need radians for sin and cos
    
                    matrix = Matrix4x4.TRS(new Vector3(result.X, result.Y, 0), Quaternion.AngleAxis(angle * Mathf.Rad2Deg - 90, Vector3.forward), Vector3.one);
    
                    //apply the transformation, and obtain the final position and orientation of the 4 vertices representing this char
                    vertices[vertexIndex + 0] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 0]);
                    vertices[vertexIndex + 1] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 1]);
                    vertices[vertexIndex + 2] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 2]);
                    vertices[vertexIndex + 3] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 3]);
                }
    
                //Upload the mesh with the revised information
                m_TextComponent.UpdateVertexData();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    1. 在inspector中挂载该脚本到TextMeshProUGUI物体上,调整radius,取消再重新勾选以在editor中预览。
    2. 调整位置,直至文本贴合背景图片上的曲线,大功告成!
      在这里插入图片描述
  • 相关阅读:
    【电机控制】FOC电机控制
    java计算机毕业设计高校科研信息管理系统源码+mysql数据库+系统+lw文档+部署
    Scan Context++:在城市环境中具有鲁棒性的位置识别描述子
    [Python人工智能] 四十二.命名实体识别 (3)基于Bert+BiLSTM-CRF的中文实体识别万字详解(异常解决中)
    聊聊如何利用管道模式来进行业务编排(下篇)
    《智能网联汽车自动驾驶功能测试规程》
    窗口-视口转换(详细)
    MongoDB 未授权访问漏洞
    问道管理:机器人产业迎催化 黄金价格或将突破前高
    Android Retrofit 高级使用与原理
  • 原文地址:https://blog.csdn.net/mkr67n/article/details/126764944