logo

Unity UGUI Text实现竖排文字的完整指南

作者:蛮不讲李2025.09.19 18:59浏览量:1

简介:本文详细讲解Unity UGUI中实现Text组件竖排文字的多种方法,涵盖字符换行控制、自定义着色器方案及TextMeshPro的高级实现,提供完整代码示例与性能优化建议。

Unity UGUI Text竖排文字实现方案全解析

在Unity UGUI系统中实现竖排文字显示是中文、日文等东亚语言开发中的常见需求,尤其在古籍展示、对联生成等文化类应用中不可或缺。本文将系统梳理三种主流实现方案,从基础字符控制到高级着色器实现,提供可落地的技术解决方案。

一、基础方案:字符换行控制

1.1 传统换行符方案

最基础的实现方式是利用换行符\n进行人工换行:

  1. // 示例:手动设置竖排文本
  2. Text verticalText = GetComponent<Text>();
  3. verticalText.text = "竖\n排\n文\n字\n示\n例";

此方案优点是实现简单,但存在明显缺陷:

  • 字符间距不可动态调整
  • 文本内容变更时需重新计算换行位置
  • 不支持自动适应文本框宽度变化

1.2 动态换行算法

更智能的实现需要编写换行控制逻辑:

  1. public void SetVerticalText(string text, int charPerLine = 1) {
  2. StringBuilder sb = new StringBuilder();
  3. for (int i = 0; i < text.Length; i += charPerLine) {
  4. string line = text.Substring(i, Mathf.Min(charPerLine, text.Length - i));
  5. sb.Append(line).Append("\n");
  6. }
  7. GetComponent<Text>().text = sb.ToString();
  8. }

该方案支持:

  • 每行显示指定字符数
  • 动态文本更新
  • 配合RectTransform自动适应

但需注意中文等双字节字符的边界处理,建议使用:

  1. // 更健壮的中文处理版本
  2. public void SetChineseVerticalText(string text) {
  3. char[] chars = text.ToCharArray();
  4. StringBuilder sb = new StringBuilder();
  5. for (int i = 0; i < chars.Length; i++) {
  6. sb.Append(chars[i]);
  7. // 非最后一个字符且不是标点时添加换行
  8. if (i < chars.Length - 1 && !IsPunctuation(chars[i])) {
  9. sb.Append("\n");
  10. }
  11. }
  12. GetComponent<Text>().text = sb.ToString();
  13. }
  14. private bool IsPunctuation(char c) {
  15. return ",。、;:?!()【】《》".Contains(c);
  16. }

二、进阶方案:自定义着色器实现

2.1 基础UV翻转方案

通过修改Text组件的材质着色器实现竖排:

  1. 创建自定义Shader:

    1. Shader "Custom/VerticalText" {
    2. Properties {
    3. _MainTex ("Font Atlas", 2D) = "white" {}
    4. _Color ("Text Color", Color) = (1,1,1,1)
    5. }
    6. SubShader {
    7. Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    8. Lighting Off Cull Off ZWrite Off Fog { Mode Off }
    9. Pass {
    10. CGPROGRAM
    11. #pragma vertex vert
    12. #pragma fragment frag
    13. #include "UnityCG.cginc"
    14. struct appdata {
    15. float4 vertex : POSITION;
    16. float2 uv : TEXCOORD0;
    17. };
    18. struct v2f {
    19. float2 uv : TEXCOORD0;
    20. float4 vertex : SV_POSITION;
    21. };
    22. sampler2D _MainTex;
    23. float4 _Color;
    24. v2f vert(appdata v) {
    25. v2f o;
    26. // 关键修改:翻转UV的y坐标
    27. o.uv = float2(v.uv.x, 1 - v.uv.y);
    28. o.vertex = UnityObjectToClipPos(v.vertex);
    29. return o;
    30. }
    31. fixed4 frag(v2f i) : SV_Target {
    32. fixed4 col = tex2D(_MainTex, i.uv);
    33. col *= _Color;
    34. col.a *= (col.r + col.g + col.b) > 0.1 ? 1 : 0;
    35. return col;
    36. }
    37. ENDCG
    38. }
    39. }
    40. }
  2. 应用材质:

    1. Material verticalMat = new Material(Shader.Find("Custom/VerticalText"));
    2. GetComponent<Text>().material = verticalMat;

2.2 高级字符旋转方案

更完善的实现需要处理字符旋转:

  1. // 在frag函数中添加旋转逻辑
  2. fixed4 frag(v2f i) : SV_Target {
  3. float2 rotatedUV = float2(
  4. i.uv.x * cos(_Rotation) - i.uv.y * sin(_Rotation),
  5. i.uv.x * sin(_Rotation) + i.uv.y * cos(_Rotation)
  6. );
  7. // 调整UV范围到[0,1]
  8. rotatedUV = (rotatedUV + 1) * 0.5;
  9. fixed4 col = tex2D(_MainTex, rotatedUV);
  10. // ...其余着色逻辑
  11. }

三、终极方案:TextMeshPro竖排实现

3.1 TMP基础竖排设置

TextMeshPro提供了更专业的竖排支持:

  1. 创建TMP文本对象
  2. 在Inspector中设置:

    • Text AlignmentMiddle Center
    • Vertical AlignmentMiddle
    • Enable Word Wrapping → 勾选
    • Character Spacing → 调整垂直间距
  3. 代码控制:
    ```csharp
    using TMPro;

public class TMPVerticalText : MonoBehaviour {
public TMP_Text textComponent;

  1. void Start() {
  2. textComponent.text = "TextMeshPro竖排示例";
  3. textComponent.alignment = TextAlignmentOptions.Midline;
  4. textComponent.enableWordWrapping = true;
  5. textComponent.verticalAlignment = VerticalAlignmentOptions.Middle;
  6. // 自定义字符间距
  7. textComponent.characterSpacing = 50;
  8. textComponent.lineSpacingAdjustment = 100;
  9. }

}

  1. ### 3.2 高级竖排控制
  2. 对于复杂需求,可利用TMP的富文本功能:
  3. ```csharp
  4. // 使用富文本标签控制
  5. textComponent.text = "第<size=+10>一</size>\n行\n<color=red>重</color>点";

或通过脚本动态控制:

  1. public void SetVerticalTMPText(string[] lines) {
  2. StringBuilder sb = new StringBuilder();
  3. foreach (string line in lines) {
  4. sb.Append(line).Append("\n");
  5. }
  6. textComponent.text = sb.ToString();
  7. // 动态调整布局
  8. textComponent.ForceMeshUpdate();
  9. RectTransform rt = textComponent.GetComponent<RectTransform>();
  10. rt.sizeDelta = new Vector2(rt.sizeDelta.x, textComponent.preferredHeight);
  11. }

四、性能优化与最佳实践

4.1 动态文本更新优化

对于频繁更新的文本,建议:

  1. // 使用对象池管理TMP文本
  2. private TMP_Text textPool;
  3. private Queue<TMP_Text> availableTexts = new Queue<TMP_Text>();
  4. public TMP_Text GetVerticalText() {
  5. TMP_Text text;
  6. if (availableTexts.Count > 0) {
  7. text = availableTexts.Dequeue();
  8. } else {
  9. text = Instantiate(textPool);
  10. }
  11. return text;
  12. }
  13. public void ReleaseText(TMP_Text text) {
  14. text.text = "";
  15. availableTexts.Enqueue(text);
  16. }

4.2 材质实例化控制

避免频繁创建材质实例:

  1. private Dictionary<string, Material> materialCache = new Dictionary<string, Material>();
  2. public Material GetVerticalMaterial(string baseShaderName) {
  3. string cacheKey = "Vertical_" + baseShaderName;
  4. if (!materialCache.ContainsKey(cacheKey)) {
  5. Shader baseShader = Shader.Find(baseShaderName);
  6. Material newMat = new Material(baseShader);
  7. // 设置竖排相关参数
  8. newMat.SetFloat("_Rotation", 90);
  9. materialCache.Add(cacheKey, newMat);
  10. }
  11. return materialCache[cacheKey];
  12. }

4.3 跨平台适配建议

  1. 移动端注意字体大小适配:

    1. public void AdjustFontSizeForPlatform() {
    2. float scaleFactor = Application.platform == RuntimePlatform.Android ? 0.8f : 1f;
    3. textComponent.fontSize = Mathf.RoundToInt(textComponent.fontSize * scaleFactor);
    4. }
  2. 不同分辨率适配方案:

    1. public void AdjustForResolution() {
    2. float aspectRatio = (float)Screen.width / Screen.height;
    3. float targetRatio = 16f / 9f;
    4. float scale = Mathf.Min(1, aspectRatio / targetRatio);
    5. RectTransform rt = GetComponent<RectTransform>();
    6. rt.localScale = new Vector3(scale, scale, 1);
    7. }

五、常见问题解决方案

5.1 中文标点定位问题

解决方案:

  1. public string ProcessChinesePunctuation(string text) {
  2. string[] punctuation = { ",", "。", "、", ";", ":" };
  3. StringBuilder sb = new StringBuilder();
  4. for (int i = 0; i < text.Length; i++) {
  5. string currentChar = text.Substring(i, 1);
  6. bool isPunctuation = punctuation.Contains(currentChar);
  7. if (isPunctuation && i > 0) {
  8. // 标点前添加空格(根据实际需求调整)
  9. sb.Append(" ").Append(currentChar).Append("\n");
  10. } else {
  11. sb.Append(currentChar);
  12. if (i < text.Length - 1) sb.Append("\n");
  13. }
  14. }
  15. return sb.ToString();
  16. }

5.2 动态宽度适应

实现自动宽度调整:

  1. public void AdjustWidthToContent(TMP_Text textComponent) {
  2. textComponent.ForceMeshUpdate();
  3. float preferredWidth = textComponent.preferredWidth;
  4. RectTransform rt = textComponent.GetComponent<RectTransform>();
  5. rt.sizeDelta = new Vector2(preferredWidth, rt.sizeDelta.y);
  6. }

5.3 多语言支持

国际化实现示例:

  1. public class LocalizedVerticalText : MonoBehaviour {
  2. public TMP_Text textComponent;
  3. public LanguageSettings[] languageSettings;
  4. public void SetLanguage(SystemLanguage language) {
  5. LanguageSettings setting = languageSettings.FirstOrDefault(
  6. s => s.language == language);
  7. if (setting != null) {
  8. textComponent.text = setting.verticalText;
  9. textComponent.fontSize = setting.fontSize;
  10. // 应用其他语言特定设置
  11. }
  12. }
  13. }
  14. [System.Serializable]
  15. public class LanguageSettings {
  16. public SystemLanguage language;
  17. public string verticalText;
  18. public int fontSize;
  19. // 其他语言相关设置
  20. }

六、完整实现示例

6.1 基础竖排文本组件

  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. [RequireComponent(typeof(Text))]
  4. public class VerticalText : MonoBehaviour {
  5. [SerializeField] private bool isChinese = true;
  6. [SerializeField] private int charactersPerLine = 1;
  7. private Text uiText;
  8. private void Awake() {
  9. uiText = GetComponent<Text>();
  10. }
  11. public void SetText(string newText) {
  12. if (isChinese) {
  13. uiText.text = ProcessChineseText(newText);
  14. } else {
  15. uiText.text = ProcessRegularText(newText);
  16. }
  17. AdjustLayout();
  18. }
  19. private string ProcessChineseText(string text) {
  20. char[] chars = text.ToCharArray();
  21. System.Text.StringBuilder sb = new System.Text.StringBuilder();
  22. for (int i = 0; i < chars.Length; i++) {
  23. sb.Append(chars[i]);
  24. bool shouldBreak = (i < chars.Length - 1) &&
  25. !IsChinesePunctuation(chars[i]) &&
  26. !IsChinesePunctuation(chars[i + 1]);
  27. if (shouldBreak) {
  28. sb.Append("\n");
  29. }
  30. }
  31. return sb.ToString();
  32. }
  33. private string ProcessRegularText(string text) {
  34. System.Text.StringBuilder sb = new System.Text.StringBuilder();
  35. for (int i = 0; i < text.Length; i += charactersPerLine) {
  36. int length = Mathf.Min(charactersPerLine, text.Length - i);
  37. sb.Append(text.Substring(i, length)).Append("\n");
  38. }
  39. return sb.ToString();
  40. }
  41. private bool IsChinesePunctuation(char c) {
  42. string punctuation = ",。、;:?!()【】《》";
  43. return punctuation.Contains(c.ToString());
  44. }
  45. private void AdjustLayout() {
  46. LayoutRebuilder.ForceRebuildLayoutImmediate(
  47. GetComponent<RectTransform>());
  48. }
  49. }

6.2 TMP高级竖排组件

  1. using TMPro;
  2. using UnityEngine;
  3. [RequireComponent(typeof(TMP_Text))]
  4. public class AdvancedVerticalText : MonoBehaviour {
  5. [SerializeField] private float lineSpacing = 50f;
  6. [SerializeField] private float characterSpacing = 0f;
  7. [SerializeField] private bool autoSizeToContent = true;
  8. private TMP_Text tmpText;
  9. private void Awake() {
  10. tmpText = GetComponent<TMP_Text>();
  11. ConfigureTMPSettings();
  12. }
  13. private void ConfigureTMPSettings() {
  14. tmpText.alignment = TextAlignmentOptions.Midline;
  15. tmpText.verticalAlignment = VerticalAlignmentOptions.Middle;
  16. tmpText.enableWordWrapping = true;
  17. tmpText.overflowMode = TextOverflowModes.Truncate;
  18. }
  19. public void SetVerticalText(string text, float widthConstraint = 0f) {
  20. tmpText.text = text;
  21. if (widthConstraint > 0) {
  22. tmpText.rectTransform.sizeDelta =
  23. new Vector2(widthConstraint, tmpText.rectTransform.sizeDelta.y);
  24. }
  25. if (autoSizeToContent) {
  26. tmpText.ForceMeshUpdate();
  27. float height = tmpText.preferredHeight + (lineSpacing * CountLines(text));
  28. tmpText.rectTransform.sizeDelta =
  29. new Vector2(tmpText.rectTransform.sizeDelta.x, height);
  30. }
  31. SetSpacing();
  32. }
  33. private int CountLines(string text) {
  34. return text.Split(new[] { '\n' }, System.StringSplitOptions.None).Length;
  35. }
  36. private void SetSpacing() {
  37. tmpText.lineSpacingAdjustment = lineSpacing / 100f;
  38. tmpText.characterSpacing = characterSpacing;
  39. }
  40. public void AnimateText(string fullText, float duration = 2f) {
  41. StartCoroutine(ShowTextOverTime(fullText, duration));
  42. }
  43. private System.Collections.IEnumerator ShowTextOverTime(string fullText, float duration) {
  44. tmpText.maxVisibleCharacters = 0;
  45. float elapsed = 0f;
  46. while (elapsed < duration) {
  47. elapsed += Time.deltaTime;
  48. float progress = Mathf.Clamp01(elapsed / duration);
  49. int visibleChars = Mathf.RoundToInt(fullText.Length * progress);
  50. tmpText.maxVisibleCharacters = visibleChars;
  51. yield return null;
  52. }
  53. tmpText.maxVisibleCharacters = int.MaxValue;
  54. }
  55. }

七、总结与建议

  1. 简单需求:使用基础换行符方案,配合字符处理函数
  2. 中等复杂度:采用TextMeshPro的内置竖排功能
  3. 高性能要求:实现自定义着色器方案,减少Draw Call
  4. 国际化项目:构建语言管理系统,支持多语言竖排布局

性能对比数据:
| 方案 | Draw Call | 内存占用 | 适用场景 |
|———|—————|—————|—————|
| 基础换行 | 低 | 低 | 静态文本 |
| TMP竖排 | 中 | 中 | 动态文本 |
| 自定义着色器 | 低 | 高 | 高频更新 |

建议开发者根据项目需求选择合适方案,对于中文项目优先推荐TextMeshPro方案,其内置的竖排支持和丰富的富文本功能能大幅提高开发效率。

相关文章推荐

发表评论

活动