Unity UGUI Text实现竖排文字的完整指南
2025.09.19 18:59浏览量:1简介:本文详细讲解Unity UGUI中实现Text组件竖排文字的多种方法,涵盖字符换行控制、自定义着色器方案及TextMeshPro的高级实现,提供完整代码示例与性能优化建议。
Unity UGUI Text竖排文字实现方案全解析
在Unity UGUI系统中实现竖排文字显示是中文、日文等东亚语言开发中的常见需求,尤其在古籍展示、对联生成等文化类应用中不可或缺。本文将系统梳理三种主流实现方案,从基础字符控制到高级着色器实现,提供可落地的技术解决方案。
一、基础方案:字符换行控制
1.1 传统换行符方案
最基础的实现方式是利用换行符\n进行人工换行:
// 示例:手动设置竖排文本Text verticalText = GetComponent<Text>();verticalText.text = "竖\n排\n文\n字\n示\n例";
此方案优点是实现简单,但存在明显缺陷:
- 字符间距不可动态调整
- 文本内容变更时需重新计算换行位置
- 不支持自动适应文本框宽度变化
1.2 动态换行算法
更智能的实现需要编写换行控制逻辑:
public void SetVerticalText(string text, int charPerLine = 1) {StringBuilder sb = new StringBuilder();for (int i = 0; i < text.Length; i += charPerLine) {string line = text.Substring(i, Mathf.Min(charPerLine, text.Length - i));sb.Append(line).Append("\n");}GetComponent<Text>().text = sb.ToString();}
该方案支持:
- 每行显示指定字符数
- 动态文本更新
- 配合RectTransform自动适应
但需注意中文等双字节字符的边界处理,建议使用:
// 更健壮的中文处理版本public void SetChineseVerticalText(string text) {char[] chars = text.ToCharArray();StringBuilder sb = new StringBuilder();for (int i = 0; i < chars.Length; i++) {sb.Append(chars[i]);// 非最后一个字符且不是标点时添加换行if (i < chars.Length - 1 && !IsPunctuation(chars[i])) {sb.Append("\n");}}GetComponent<Text>().text = sb.ToString();}private bool IsPunctuation(char c) {return ",。、;:?!()【】《》".Contains(c);}
二、进阶方案:自定义着色器实现
2.1 基础UV翻转方案
通过修改Text组件的材质着色器实现竖排:
创建自定义Shader:
Shader "Custom/VerticalText" {Properties {_MainTex ("Font Atlas", 2D) = "white" {}_Color ("Text Color", Color) = (1,1,1,1)}SubShader {Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }Lighting Off Cull Off ZWrite Off Fog { Mode Off }Pass {CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata {float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f {float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _Color;v2f vert(appdata v) {v2f o;// 关键修改:翻转UV的y坐标o.uv = float2(v.uv.x, 1 - v.uv.y);o.vertex = UnityObjectToClipPos(v.vertex);return o;}fixed4 frag(v2f i) : SV_Target {fixed4 col = tex2D(_MainTex, i.uv);col *= _Color;col.a *= (col.r + col.g + col.b) > 0.1 ? 1 : 0;return col;}ENDCG}}}
应用材质:
Material verticalMat = new Material(Shader.Find("Custom/VerticalText"));GetComponent<Text>().material = verticalMat;
2.2 高级字符旋转方案
更完善的实现需要处理字符旋转:
// 在frag函数中添加旋转逻辑fixed4 frag(v2f i) : SV_Target {float2 rotatedUV = float2(i.uv.x * cos(_Rotation) - i.uv.y * sin(_Rotation),i.uv.x * sin(_Rotation) + i.uv.y * cos(_Rotation));// 调整UV范围到[0,1]rotatedUV = (rotatedUV + 1) * 0.5;fixed4 col = tex2D(_MainTex, rotatedUV);// ...其余着色逻辑}
三、终极方案:TextMeshPro竖排实现
3.1 TMP基础竖排设置
TextMeshPro提供了更专业的竖排支持:
- 创建TMP文本对象
在Inspector中设置:
Text Alignment→Middle CenterVertical Alignment→MiddleEnable Word Wrapping→ 勾选Character Spacing→ 调整垂直间距
代码控制:
```csharp
using TMPro;
public class TMPVerticalText : MonoBehaviour {
public TMP_Text textComponent;
void Start() {textComponent.text = "TextMeshPro竖排示例";textComponent.alignment = TextAlignmentOptions.Midline;textComponent.enableWordWrapping = true;textComponent.verticalAlignment = VerticalAlignmentOptions.Middle;// 自定义字符间距textComponent.characterSpacing = 50;textComponent.lineSpacingAdjustment = 100;}
}
### 3.2 高级竖排控制对于复杂需求,可利用TMP的富文本功能:```csharp// 使用富文本标签控制textComponent.text = "第<size=+10>一</size>\n行\n<color=red>重</color>点";
或通过脚本动态控制:
public void SetVerticalTMPText(string[] lines) {StringBuilder sb = new StringBuilder();foreach (string line in lines) {sb.Append(line).Append("\n");}textComponent.text = sb.ToString();// 动态调整布局textComponent.ForceMeshUpdate();RectTransform rt = textComponent.GetComponent<RectTransform>();rt.sizeDelta = new Vector2(rt.sizeDelta.x, textComponent.preferredHeight);}
四、性能优化与最佳实践
4.1 动态文本更新优化
对于频繁更新的文本,建议:
// 使用对象池管理TMP文本private TMP_Text textPool;private Queue<TMP_Text> availableTexts = new Queue<TMP_Text>();public TMP_Text GetVerticalText() {TMP_Text text;if (availableTexts.Count > 0) {text = availableTexts.Dequeue();} else {text = Instantiate(textPool);}return text;}public void ReleaseText(TMP_Text text) {text.text = "";availableTexts.Enqueue(text);}
4.2 材质实例化控制
避免频繁创建材质实例:
private Dictionary<string, Material> materialCache = new Dictionary<string, Material>();public Material GetVerticalMaterial(string baseShaderName) {string cacheKey = "Vertical_" + baseShaderName;if (!materialCache.ContainsKey(cacheKey)) {Shader baseShader = Shader.Find(baseShaderName);Material newMat = new Material(baseShader);// 设置竖排相关参数newMat.SetFloat("_Rotation", 90);materialCache.Add(cacheKey, newMat);}return materialCache[cacheKey];}
4.3 跨平台适配建议
移动端注意字体大小适配:
public void AdjustFontSizeForPlatform() {float scaleFactor = Application.platform == RuntimePlatform.Android ? 0.8f : 1f;textComponent.fontSize = Mathf.RoundToInt(textComponent.fontSize * scaleFactor);}
不同分辨率适配方案:
public void AdjustForResolution() {float aspectRatio = (float)Screen.width / Screen.height;float targetRatio = 16f / 9f;float scale = Mathf.Min(1, aspectRatio / targetRatio);RectTransform rt = GetComponent<RectTransform>();rt.localScale = new Vector3(scale, scale, 1);}
五、常见问题解决方案
5.1 中文标点定位问题
解决方案:
public string ProcessChinesePunctuation(string text) {string[] punctuation = { ",", "。", "、", ";", ":" };StringBuilder sb = new StringBuilder();for (int i = 0; i < text.Length; i++) {string currentChar = text.Substring(i, 1);bool isPunctuation = punctuation.Contains(currentChar);if (isPunctuation && i > 0) {// 标点前添加空格(根据实际需求调整)sb.Append(" ").Append(currentChar).Append("\n");} else {sb.Append(currentChar);if (i < text.Length - 1) sb.Append("\n");}}return sb.ToString();}
5.2 动态宽度适应
实现自动宽度调整:
public void AdjustWidthToContent(TMP_Text textComponent) {textComponent.ForceMeshUpdate();float preferredWidth = textComponent.preferredWidth;RectTransform rt = textComponent.GetComponent<RectTransform>();rt.sizeDelta = new Vector2(preferredWidth, rt.sizeDelta.y);}
5.3 多语言支持
国际化实现示例:
public class LocalizedVerticalText : MonoBehaviour {public TMP_Text textComponent;public LanguageSettings[] languageSettings;public void SetLanguage(SystemLanguage language) {LanguageSettings setting = languageSettings.FirstOrDefault(s => s.language == language);if (setting != null) {textComponent.text = setting.verticalText;textComponent.fontSize = setting.fontSize;// 应用其他语言特定设置}}}[System.Serializable]public class LanguageSettings {public SystemLanguage language;public string verticalText;public int fontSize;// 其他语言相关设置}
六、完整实现示例
6.1 基础竖排文本组件
using UnityEngine;using UnityEngine.UI;[RequireComponent(typeof(Text))]public class VerticalText : MonoBehaviour {[SerializeField] private bool isChinese = true;[SerializeField] private int charactersPerLine = 1;private Text uiText;private void Awake() {uiText = GetComponent<Text>();}public void SetText(string newText) {if (isChinese) {uiText.text = ProcessChineseText(newText);} else {uiText.text = ProcessRegularText(newText);}AdjustLayout();}private string ProcessChineseText(string text) {char[] chars = text.ToCharArray();System.Text.StringBuilder sb = new System.Text.StringBuilder();for (int i = 0; i < chars.Length; i++) {sb.Append(chars[i]);bool shouldBreak = (i < chars.Length - 1) &&!IsChinesePunctuation(chars[i]) &&!IsChinesePunctuation(chars[i + 1]);if (shouldBreak) {sb.Append("\n");}}return sb.ToString();}private string ProcessRegularText(string text) {System.Text.StringBuilder sb = new System.Text.StringBuilder();for (int i = 0; i < text.Length; i += charactersPerLine) {int length = Mathf.Min(charactersPerLine, text.Length - i);sb.Append(text.Substring(i, length)).Append("\n");}return sb.ToString();}private bool IsChinesePunctuation(char c) {string punctuation = ",。、;:?!()【】《》";return punctuation.Contains(c.ToString());}private void AdjustLayout() {LayoutRebuilder.ForceRebuildLayoutImmediate(GetComponent<RectTransform>());}}
6.2 TMP高级竖排组件
using TMPro;using UnityEngine;[RequireComponent(typeof(TMP_Text))]public class AdvancedVerticalText : MonoBehaviour {[SerializeField] private float lineSpacing = 50f;[SerializeField] private float characterSpacing = 0f;[SerializeField] private bool autoSizeToContent = true;private TMP_Text tmpText;private void Awake() {tmpText = GetComponent<TMP_Text>();ConfigureTMPSettings();}private void ConfigureTMPSettings() {tmpText.alignment = TextAlignmentOptions.Midline;tmpText.verticalAlignment = VerticalAlignmentOptions.Middle;tmpText.enableWordWrapping = true;tmpText.overflowMode = TextOverflowModes.Truncate;}public void SetVerticalText(string text, float widthConstraint = 0f) {tmpText.text = text;if (widthConstraint > 0) {tmpText.rectTransform.sizeDelta =new Vector2(widthConstraint, tmpText.rectTransform.sizeDelta.y);}if (autoSizeToContent) {tmpText.ForceMeshUpdate();float height = tmpText.preferredHeight + (lineSpacing * CountLines(text));tmpText.rectTransform.sizeDelta =new Vector2(tmpText.rectTransform.sizeDelta.x, height);}SetSpacing();}private int CountLines(string text) {return text.Split(new[] { '\n' }, System.StringSplitOptions.None).Length;}private void SetSpacing() {tmpText.lineSpacingAdjustment = lineSpacing / 100f;tmpText.characterSpacing = characterSpacing;}public void AnimateText(string fullText, float duration = 2f) {StartCoroutine(ShowTextOverTime(fullText, duration));}private System.Collections.IEnumerator ShowTextOverTime(string fullText, float duration) {tmpText.maxVisibleCharacters = 0;float elapsed = 0f;while (elapsed < duration) {elapsed += Time.deltaTime;float progress = Mathf.Clamp01(elapsed / duration);int visibleChars = Mathf.RoundToInt(fullText.Length * progress);tmpText.maxVisibleCharacters = visibleChars;yield return null;}tmpText.maxVisibleCharacters = int.MaxValue;}}
七、总结与建议
- 简单需求:使用基础换行符方案,配合字符处理函数
- 中等复杂度:采用TextMeshPro的内置竖排功能
- 高性能要求:实现自定义着色器方案,减少Draw Call
- 国际化项目:构建语言管理系统,支持多语言竖排布局
性能对比数据:
| 方案 | Draw Call | 内存占用 | 适用场景 |
|———|—————|—————|—————|
| 基础换行 | 低 | 低 | 静态文本 |
| TMP竖排 | 中 | 中 | 动态文本 |
| 自定义着色器 | 低 | 高 | 高频更新 |
建议开发者根据项目需求选择合适方案,对于中文项目优先推荐TextMeshPro方案,其内置的竖排支持和丰富的富文本功能能大幅提高开发效率。

发表评论
登录后可评论,请前往 登录 或 注册