Unity TMP_Text 竖排实现指南:从基础到进阶的完整方案
在Unity游戏开发中,TextMeshPro(TMP)作为新一代文本渲染组件,提供了比传统UI Text更强大的功能和视觉效果。当需要实现竖排文字时(如中文古籍、日式游戏UI等场景),TMP_Text的灵活配置能够满足多样化需求。本文将系统讲解三种主流实现方案,包含详细参数配置和代码示例。
一、TMP_Text竖排基础原理
TMP_Text的竖排实现主要基于两个核心机制:字符排列方向控制和文本对齐方式。与横排文本不同,竖排需要重新定义字符的基线方向和行进方向。TMP通过TextGenerationSettings中的horizontalAlignment和verticalAlignment参数,结合自定义着色器或脚本控制,实现文字的垂直排列。
1.1 字符方向控制参数
| 参数 | 功能 | 适用场景 |
|---|---|---|
HorizontalOverflow |
水平溢出处理 | 控制单行文字宽度限制 |
VerticalOverflow |
垂直溢出处理 | 控制多行文字高度限制 |
Alignment |
文本对齐方式 | 决定文字在矩形框内的位置 |
CharacterSpacing |
字符间距 | 调整竖排时字符垂直间距 |
二、方案一:通过Inspector面板配置竖排
2.1 基础配置步骤
- 创建TMP对象:在Hierarchy中右键选择
UI > Text - TextMeshPro - 修改对齐参数:
- 在Inspector的
Text Input区域,设置Alignment为Middle Center - 展开
Extra Settings,设置Vertical Alignment为Middle
- 在Inspector的
- 调整布局组件:
- 添加
Content Size Fitter组件,设置Horizontal Fit为Preferred Size - 添加
Vertical Layout Group(需先转换为RectTransform)
- 添加
2.2 关键参数详解
- Font Size:建议使用20-30pt范围,过大可能导致字符重叠
- Line Spacing:竖排时实际控制行间距,建议设置为1.2-1.5倍字高
- Word Wrapping:必须启用,否则长文本会超出边界
- Auto Size:建议开启
Enable Auto Sizing,设置最小/最大字号
2.3 示例配置
// 在Start方法中初始化竖排参数void InitializeVerticalText() {TMP_Text textComponent = GetComponent<TMP_Text>();textComponent.alignment = TextAlignmentOptions.Center;textComponent.enableAutoSizing = true;textComponent.fontSizeMin = 20;textComponent.fontSizeMax = 30;textComponent.lineSpacing = 1.3f; // 关键竖排行距参数}
三、方案二:使用自定义着色器实现精确控制
3.1 着色器开发原理
TMP_Text的着色器系统允许通过修改顶点着色器来改变字符排列方向。核心思路是将字符的UV坐标旋转90度,并调整顶点位置。
3.2 完整着色器代码
Shader "TextMeshPro/Vertical Text" {Properties {_MainTex ("Font Atlas", 2D) = "white" {}_FaceColor ("Text Color", Color) = (1,1,1,1)_OutlineColor ("Outline Color", Color) = (0,0,0,1)_OutlineWidth ("Outline Width", Range(0, 0.1)) = 0.01_VertexOffsetX ("Vertex Offset X", Float) = 0_VertexOffsetY ("Vertex Offset Y", Float) = 0}SubShader {Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }Lighting Off Cull Off ZWrite Off Blend One OneMinusSrcAlphaPass {CGPROGRAM#pragma vertex VertShader#pragma fragment PixShader#include "UnityCG.cginc"#include "UnityUI.cginc"struct appdata_t {float4 vertex : POSITION;float4 color : COLOR;float2 texcoord : TEXCOORD0;};struct v2f {float4 vertex : SV_POSITION;fixed4 color : COLOR;float2 texcoord : TEXCOORD0;float4 worldPosition : TEXCOORD1;};sampler2D _MainTex;fixed4 _FaceColor;float _VertexOffsetX;float _VertexOffsetY;v2f VertShader(appdata_t v) {v2f o;// 关键修改:旋转字符方向float2 rotatedUV = float2(-v.texcoord.y, v.texcoord.x);o.worldPosition = v.vertex;o.vertex = UnityObjectToClipPos(v.vertex);o.texcoord = rotatedUV;o.color = v.color * _FaceColor;return o;}fixed4 PixShader(v2f i) : SV_Target {fixed4 col = tex2D(_MainTex, i.texcoord) * i.color;clip(col.a - 0.01);return col;}ENDCG}}}
3.3 着色器应用步骤
- 创建新材质并应用上述着色器
- 在TMP_Text组件的
Material属性中指定该材质 - 调整
Character Spacing和Line Spacing参数优化显示效果
四、方案三:脚本动态控制竖排
4.1 核心实现思路
通过脚本修改每个字符的顶点位置,实现动态竖排效果。适用于需要运行时改变排列方向的场景。
4.2 完整代码实现
using UnityEngine;using TMPro;[RequireComponent(typeof(TMP_Text))]public class VerticalTextController : MonoBehaviour {[SerializeField] private float characterSpacing = 30f;[SerializeField] private float lineSpacing = 10f;private TMP_Text textComponent;private TMP_TextInfo textInfo;void Start() {textComponent = GetComponent<TMP_Text>();textInfo = textComponent.textInfo;UpdateVerticalText();}void UpdateVerticalText() {if (textInfo.characterCount == 0) return;// 重置所有字符位置for (int i = 0; i < textInfo.characterCount; i++) {TMP_CharacterInfo charInfo = textInfo.characterInfo[i];if (!charInfo.isVisible) continue;// 获取原始顶点数据Vector3[] vertices = charInfo.vertices;Vector2 size = charInfo.topRight - charInfo.bottomLeft;// 计算新位置(竖排排列)float baseY = charInfo.bottomLeft.y;float offsetX = i * characterSpacing;float offsetY = -i * (size.y + lineSpacing);// 修改顶点位置vertices[0].x = offsetX; vertices[0].y = baseY + offsetY;vertices[1].x = offsetX; vertices[1].y = baseY + size.y + offsetY;vertices[2].x = offsetX + size.x; vertices[2].y = baseY + size.y + offsetY;vertices[3].x = offsetX + size.x; vertices[3].y = baseY + offsetY;// 更新网格(需要TMP_MeshInfo访问)// 实际项目中需通过textComponent.meshInfo数组更新}// 强制刷新(简化示例,实际需更精确的mesh更新)textComponent.ForceMeshUpdate();}// 实际项目中更完整的实现应处理:// 1. 多行文本的换行逻辑// 2. 动态文本变化的监听// 3. 性能优化(对象池等)}
五、常见问题解决方案
5.1 字符重叠问题
原因:字符间距设置不当或字体选择不合适
解决方案:
- 增加
Character Spacing值(建议20-40) - 选择等宽字体(如Noto Sans CJK SC)
- 调整
Line Spacing为1.2-1.5倍字高
5.2 动态文本更新失效
原因:未正确处理TMP的文本更新机制
解决方案:
// 正确监听文本变化的方式void OnEnable() {textComponent.onTextChanged.AddListener(OnTextChanged);}void OnDisable() {textComponent.onTextChanged.RemoveListener(OnTextChanged);}void OnTextChanged(string obj) {UpdateVerticalText();}
5.3 性能优化建议
- 批处理优化:将静态竖排文本合并为单个TMP对象
- 材质实例化:避免为每个竖排文本创建新材质
- 动态文本缓存:对频繁更新的文本使用对象池
六、进阶应用场景
6.1 混合横竖排文本
// 示例:同一TMP对象中部分横排部分竖排public void SetMixedText(string horizontalText, string verticalText) {textComponent.text = $"<align=center>{horizontalText}\n</align>";// 实际实现需要更复杂的富文本处理或多个TMP对象组合}
6.2 曲线竖排效果
通过修改顶点着色器中的position计算,可以实现沿路径排列的竖排文本:
// 在顶点着色器中添加路径计算float2 pathPosition = CalculatePathPosition(i.worldPosition.x);o.vertex.xy += pathPosition * _CurveStrength;
七、最佳实践总结
- 字体选择:优先使用支持CJK的字体(如思源黑体、Noto系列)
- 参数基准:
- 字号:24-32pt(移动端)
- 字符间距:30-50单位
- 行间距:1.3-1.8倍字高
- 性能基准:
- 单个场景竖排TMP对象不超过50个
- 动态文本更新频率控制在30fps以下
通过以上三种方案的灵活组合,开发者可以应对从简单静态文本到复杂动态效果的各类竖排需求。实际项目中建议从Inspector配置方案开始,遇到特殊需求时再考虑着色器或脚本方案。