Search Unity

Adjustable Character Spacing - free script

Discussion in 'UGUI & TextMesh Pro' started by Deeperbeige, Dec 30, 2014.

  1. elmax

    elmax

    Joined:
    Oct 6, 2013
    Posts:
    19
    Yeah they added giant laser guns and warp drive, but forgot about the toilet paper holders
     
    guneyozsan and limengmeng001 like this.
  2. Skeledurr

    Skeledurr

    Joined:
    Jan 5, 2015
    Posts:
    23
    Thanks everyone <3
     
  3. senwj

    senwj

    Joined:
    Mar 10, 2015
    Posts:
    3
    This how you do not support the Chinese? I found the Chinese will be a problem.
     
  4. elmax

    elmax

    Joined:
    Oct 6, 2013
    Posts:
    19
    Umm, that... sounds a bit harsh? :D

    Kidding. I don't really use double byte languages - so, sorry if the line wrapping is broken now.
     
  5. renanssba

    renanssba

    Joined:
    Feb 22, 2014
    Posts:
    7
    This is amazing! A great thank you to Deeperbeige and everyone who ported it to newer versions of Unity :D
    I'm using it on my current game.
     
  6. rcashman

    rcashman

    Joined:
    Oct 15, 2013
    Posts:
    12
    Solid gold. Thanks to everyone for a great script.
     
  7. pwalters

    pwalters

    Joined:
    Oct 1, 2016
    Posts:
    3
    Thanks for the script Deeperbeige! Would you say this script is "open source"?
     
  8. Deeperbeige

    Deeperbeige

    Joined:
    May 12, 2013
    Posts:
    17
    When I originally wrote the component, I didn't really consider it as open source or anything else. It's just a thing I built that might be handy for others, so I posted it up here. It's lovely to see people have been maintaining it while Unity have endlessly changed the API under our feet. Basically, feel free to use it wherever and however you like, in any kind of project you want. Obviously it doesn't come with any kind of warranty or guarantee that it'll get updated etc. Use your common sense - include it in your games, but maybe leave it out of that critical nuclear plant control system eh?

    Someone contacted me recently about putting it on BitBucket so that the changes get a bit easier to manage over time. We'll probably do that soon, and I'll post links etc here when it happens.
     
  9. Deeperbeige

    Deeperbeige

    Joined:
    May 12, 2013
    Posts:
    17
    The component is now publicly available on BitBucket. The repo includes the various iterations and improvements people have been making to the script since it was first made available here. Thanks to everyone who has contributed, and thanks to Andrew Pavlovich for setting up the hosted repo etc.

    Grab it here:
    https://bitbucket.org/AcornGame/adjustable-character-spacing

    If you know your way around GIT, you can clone the repo in the usual way, and contribute if you want to. If none of that makes sense to you, you can just grab the latest copy from the downloads section on the left as a zip, and build it directly into your projects.
     
  10. Qwolf0521

    Qwolf0521

    Joined:
    Jan 5, 2015
    Posts:
    2
    This is amazing! Thanks to everyone for a great script.
     
    5argon likes this.
  11. RedRina

    RedRina

    Joined:
    Jul 6, 2017
    Posts:
    3
    How about minus value of spacing???
    in my case, just fitted original preferred size but the rect-size could not be lesser than it(of course using with ContentSizeFitter)
    I'm on this problem...
     
  12. ChoMPi

    ChoMPi

    Joined:
    Jul 11, 2013
    Posts:
    112
    You should try out https://www.assetstore.unity3d.com/en/#!/content/84126 now that it's free...
     
    Stephan_B likes this.
  13. RedRina

    RedRina

    Joined:
    Jul 6, 2017
    Posts:
    3
  14. Ing3nu

    Ing3nu

    Joined:
    Jul 21, 2012
    Posts:
    15
    Hey everyone! I'm working on cleaning up an app in Unity5.2.1 and have given this script a try. It's throwing the following errors for me:

    'LetterSpacing.ModifyMesh(VertexHelper)': no suitable method found to override

    and
    'VertexHelper' does not contain a definition for 'Clear' and no extension method 'Clear' accepting a first argument of type 'VertexHelper' could be found (are you missing a using directive or an assembly reference?)

    Since everyone else on this thread seems to be able to run the script without errors, I'm wondering if I'm missing something upstream? Has anyone else seen these errors and did you find a solution?

    Thanks!
     
  15. rainbowshell

    rainbowshell

    Joined:
    Mar 30, 2016
    Posts:
    3
    Hello everyone,

    I just updated to Unity 2019.1.5f1 and the letter spacing script is not working like in the previous version of Unity

    It work well for only one line, but if I have a newline in the text, it work in a wrong way.

    EDIT:

    Hi guys, I have the final version of the script for Unity 2019.1.5f1 (attached)
    It seems that Unity 2019 does not generate verts for space character and carriage return character so I add the line 173:

    upload_2019-6-9_22-7-17.png

    And delete the line 233:

    upload_2019-6-9_22-7-38.png


    Regards
     

    Attached Files:

    Last edited: Jun 10, 2019
    Conferno and DrBlort like this.
  16. Conferno

    Conferno

    Joined:
    Feb 27, 2014
    Posts:
    49
    I found that this script very complicates rendering.
    If I have many labels with this script, fps is very slow down. Any thoughts?
     
  17. Luedrin

    Luedrin

    Joined:
    Aug 21, 2018
    Posts:
    4
    Sorry for necro-ing this post, but i'm having problems after upgrading my project to 2019.2
    In 2019.2 it seems that the RichText part of this script no longer works.
    Does anyone have a fix for this? Or alternatives to use that are easy to change to from this script?
     
  18. Briner

    Briner

    Joined:
    Jul 8, 2015
    Posts:
    25
    Here you have the script modified to work with Unity 2019.2 and support Rich Text:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using System.Text.RegularExpressions;
    4.  
    5. namespace UnityEngine.UI
    6. {
    7.     [AddComponentMenu("UI/Letter Spacing", 14)]
    8.     public class LetterSpacing : BaseMeshEffect
    9.     {
    10.         [SerializeField, Range(0, 100)]
    11.         private float m_spacing = 7f;
    12.  
    13.         private const string SupportedTagRegexPattersn = @"<b>|</b>|<i>|</i>|<size=.*?>|</size>|<color=.*?>|</color>|<material=.*?>|</material>";
    14.  
    15.         protected LetterSpacing() { }
    16.  
    17. #if UNITY_EDITOR
    18.         protected override void OnValidate()
    19.         {
    20.             spacing = m_spacing;
    21.             base.OnValidate();
    22.         }
    23. #endif
    24.         private Text _text;
    25.         private Text textComponent
    26.         {
    27.             get
    28.             {
    29.                 if(_text == null) { _text = GetComponent<Text>(); }
    30.                 return _text;
    31.             }
    32.         }
    33.  
    34.         private string[] GetLines()
    35.         {
    36.             IList<UILineInfo> lineInfos = textComponent.cachedTextGenerator.lines;
    37.             string[] lines = new string[lineInfos.Count];
    38.             for (int i = 0; i < lineInfos.Count; i++)
    39.             {
    40.                 if ((i + 1) < lineInfos.Count)
    41.                 {
    42.                     int length = (lineInfos[i + 1].startCharIdx - 1) - lineInfos[i].startCharIdx;
    43.                     lines[i] = this.textComponent.text.Substring(lineInfos[i].startCharIdx, length);
    44.                 }
    45.                 else
    46.                 {
    47.                     lines[i] = this.textComponent.text.Substring(lineInfos[i].startCharIdx);
    48.                 }
    49.             }
    50.             return lines;
    51.         }
    52.  
    53.         public float spacing
    54.         {
    55.             get { return m_spacing; }
    56.             set
    57.             {
    58.                 if (m_spacing == value) return;
    59.                 m_spacing = value;
    60.                 if (graphic != null) graphic.SetVerticesDirty();
    61.             }
    62.         }
    63.  
    64.         public void ModifyVertices(List<UIVertex> verts)
    65.         {
    66.             if (!IsActive()) return;
    67.  
    68.             if (textComponent == null)
    69.             {
    70.                 Debug.LogWarning("LetterSpacing: Missing Text component");
    71.                 return;
    72.             }
    73.  
    74.             string[] lines = GetLines();
    75.  
    76.             Vector3 pos;
    77.             float letterOffset = spacing * (float)textComponent.fontSize / 100f;
    78.             float alignmentFactor = 0;
    79.             int glyphIdx = 0;
    80.  
    81.             switch (textComponent.alignment)
    82.             {
    83.                 case TextAnchor.LowerLeft:
    84.                 case TextAnchor.MiddleLeft:
    85.                 case TextAnchor.UpperLeft:
    86.                     alignmentFactor = 0f;
    87.                     break;
    88.  
    89.                 case TextAnchor.LowerCenter:
    90.                 case TextAnchor.MiddleCenter:
    91.                 case TextAnchor.UpperCenter:
    92.                     alignmentFactor = 0.5f;
    93.                     break;
    94.  
    95.                 case TextAnchor.LowerRight:
    96.                 case TextAnchor.MiddleRight:
    97.                 case TextAnchor.UpperRight:
    98.                     alignmentFactor = 1f;
    99.                     break;
    100.             }
    101.             for (int lineIdx = 0; lineIdx < lines.Length; lineIdx++)
    102.             {
    103.                 string line = WithoutRichText(lines[lineIdx]);
    104.                 int lineLength = line.Length;
    105.  
    106.                 float lineOffset = (lineLength - 1) * letterOffset * alignmentFactor;
    107.  
    108.                 for (int charIdx = 0, charPositionIndex = 0; charIdx < line.Length; charIdx++, charPositionIndex++)
    109.                 {
    110.                     if (line[charIdx] == ' ') continue;
    111.  
    112.                     int idx1 = glyphIdx * 6 + 0;
    113.                     int idx2 = glyphIdx * 6 + 1;
    114.                     int idx3 = glyphIdx * 6 + 2;
    115.                     int idx4 = glyphIdx * 6 + 3;
    116.                     int idx5 = glyphIdx * 6 + 4;
    117.                     int idx6 = glyphIdx * 6 + 5;
    118.  
    119.                     // Check for truncated text (doesn't generate verts for all characters)
    120.                     if (idx4 > verts.Count - 1) return;
    121.  
    122.                     UIVertex vert1 = verts[idx1];
    123.                     UIVertex vert2 = verts[idx2];
    124.                     UIVertex vert3 = verts[idx3];
    125.                     UIVertex vert4 = verts[idx4];
    126.                     UIVertex vert5 = verts[idx5];
    127.                     UIVertex vert6 = verts[idx6];
    128.  
    129.                     pos = Vector3.right * (letterOffset * charPositionIndex - lineOffset);
    130.  
    131.                     vert1.position += pos;
    132.                     vert2.position += pos;
    133.                     vert3.position += pos;
    134.                     vert4.position += pos;
    135.                     vert5.position += pos;
    136.                     vert6.position += pos;
    137.  
    138.                     verts[idx1] = vert1;
    139.                     verts[idx2] = vert2;
    140.                     verts[idx3] = vert3;
    141.                     verts[idx4] = vert4;
    142.                     verts[idx5] = vert5;
    143.                     verts[idx6] = vert6;
    144.  
    145.                     //glyphIdx++;
    146.                 }
    147.             }
    148.         }
    149.  
    150.         public override void ModifyMesh(VertexHelper vh)
    151.         {
    152.             if (!this.IsActive())
    153.                 return;
    154.  
    155.             List<UIVertex> vertexList = new List<UIVertex>();
    156.             vh.GetUIVertexStream(vertexList);
    157.  
    158.             ModifyVertices(vertexList);
    159.  
    160.             vh.Clear();
    161.             vh.AddUIVertexTriangleStream(vertexList);
    162.         }
    163.  
    164.         private string WithoutRichText(string line)
    165.         {
    166.             line = Regex.Replace(line, SupportedTagRegexPattersn, "");
    167.             return line;
    168.         }
    169.     }
    170. }
     
    Last edited: Mar 2, 2020
    TigerHix, Conferno and HdG_S like this.
  19. Mahgo

    Mahgo

    Joined:
    Dec 4, 2014
    Posts:
    52
    Thanks for the update. Did you intend for glyphIdx++; to be commented out? Uncommenting it seems to work for me
     
    andersemil likes this.
  20. mikejm_

    mikejm_

    Joined:
    Oct 9, 2021
    Posts:
    346
    This is not working in Unity 2020.3.21f1.

    Does anyone know how to make it work again? I know we have TextMeshPro but I am using the True Shadow effect on my text which doesn't work on TextMeshPro. I need regular text for that.

    Thanks.
     
  21. RyuheiNodake

    RyuheiNodake

    Joined:
    Sep 15, 2023
    Posts:
    2
    NotRichText ver

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Text.RegularExpressions;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. [AddComponentMenu("UI/Effects/Letter Spacing")]
    8. public class LetterSpacing : BaseMeshEffect
    9. {
    10.  
    11.     [SerializeField]
    12.     private float m_spacing = 0f;
    13.  
    14.     protected LetterSpacing() { }
    15.  
    16. #if UNITY_EDITOR
    17.     protected override void OnValidate()
    18.     {
    19.         spacing = m_spacing;
    20.         base.OnValidate();
    21.     }
    22. #endif
    23.  
    24.     public float spacing
    25.     {
    26.         get { return m_spacing; }
    27.         set
    28.         {
    29.             if (m_spacing == value) return;
    30.             m_spacing = value;
    31.             if (graphic != null) graphic.SetVerticesDirty();
    32.         }
    33.     }
    34.  
    35.     public override void ModifyMesh(VertexHelper vh)
    36.     {
    37.         if (!IsActive()) return;
    38.  
    39.         var vertices = new List<UIVertex>();
    40.         vh.GetUIVertexStream(vertices);
    41.  
    42.         ModifyVertices(vertices);
    43.  
    44.         vh.Clear();
    45.         vh.AddUIVertexTriangleStream(vertices);
    46.     }
    47.  
    48.     public void ModifyVertices(List<UIVertex> verts)
    49.     {
    50.         if (!IsActive()) return;
    51.  
    52.         Text text = this.GetComponent<Text>();
    53.         string str = text.text;
    54.  
    55.  
    56.         IList<UILineInfo> lineInfos = text.cachedTextGenerator.lines;
    57.         for (int i = lineInfos.Count - 1; 0 < i; --i)
    58.         {
    59.             str = str.Insert(lineInfos[i].startCharIdx, "\n");
    60.             str = str.Remove(lineInfos[i].startCharIdx - 1, 1);
    61.  
    62.         }
    63.         string[] lines = str.Split('\n');
    64.  
    65.         if (text == null)
    66.         {
    67.             Debug.LogWarning("LetterSpacing:Missing Text Component");
    68.         }
    69.  
    70.         Vector3 pos;
    71.         float letterOffset = spacing * (float)text.fontSize / 100f;
    72.         int glyphIdx = 0;
    73.  
    74.         for (int lineIdx = 0; lineIdx < lines.Length; ++lineIdx)
    75.         {
    76.             string line = lines[lineIdx];
    77.  
    78.             for (int charIdx = 0; charIdx < line.Length; ++charIdx)
    79.             {
    80.                 int[] idx = {
    81.                         glyphIdx * 6 + 0,
    82.                         glyphIdx * 6 + 1,
    83.                         glyphIdx * 6 + 2,
    84.                         glyphIdx * 6 + 3,
    85.                         glyphIdx * 6 + 4,
    86.                         glyphIdx * 6 + 5,
    87.                     };
    88.  
    89.                 if (verts.Count < idx[5] - 1)
    90.                 {
    91.                     return;
    92.                 }
    93.  
    94.                 UIVertex[] vert =
    95.                 {
    96.                         verts[idx[0]],
    97.                         verts[idx[1]],
    98.                         verts[idx[2]],
    99.                         verts[idx[3]],
    100.                         verts[idx[4]],
    101.                         verts[idx[5]]
    102.                     };
    103.  
    104.                 pos = Vector3.right * (letterOffset * charIdx);
    105.  
    106.                 for (int i = 0; i < vert.Length; ++i)
    107.                 {
    108.                     vert[i].position += pos;
    109.                     verts[idx[i]] = vert[i];
    110.                 }
    111.  
    112.                 ++glyphIdx;
    113.             }
    114.         }
    115.     }
    116. }
     
    Deeperbeige likes this.