如何在Godot 4中实现适配固定尺寸的Label3D文本显示?
解决Label3D文本超出固定3D表面边界的两种方案
方案1:动态缩小字体适配固定尺寸
核心思路是实时测量文本渲染后的宽度,逐步缩小字体大小,直到文本宽度不超过目标3D表面的可用宽度,同时设置最小字体下限避免文字过小无法阅读。
实现步骤
- 定义目标最大宽度(即3D表面允许的文本显示宽度)和最小字体大小阈值。
- 监听Label3D的文本变化事件,或在文本更新后主动触发尺寸检查。
- 使用UI Toolkit的
TextMeasurementService测量当前字体下的文本宽度,若超出目标宽度则逐步减小字体,直到符合要求或达到最小字体。
代码示例
using UnityEngine; using UnityEngine.UIElements; [RequireComponent(typeof(Label3D))] public class AutoResizeLabel3D : MonoBehaviour { [SerializeField] private float maxAllowedWidth = 2f; // 3D空间中的目标宽度(单位:米) [SerializeField] private float minFontSize = 12f; private Label3D label3D; private TextMeasurementService textMeasurement; private void Awake() { label3D = GetComponent<Label3D>(); textMeasurement = new TextMeasurementService(); // 监听文本变化,自动触发调整 label3D.RegisterCallback<ChangeEvent<string>>(OnTextChanged); // 初始化时调整一次 AdjustFontSizeToFit(); } private void OnTextChanged(ChangeEvent<string> evt) { AdjustFontSizeToFit(); } private void AdjustFontSizeToFit() { if (string.IsNullOrEmpty(label3D.text)) return; float currentFontSize = label3D.style.fontSize.value.value; // 测量当前字体下的文本尺寸 Vector2 textSize = textMeasurement.MeasureText(label3D.text, label3D.style); // 转换为3D空间中的实际宽度 float textWorldWidth = textSize.x * label3D.transform.localScale.x; // 超出宽度且字体大于最小值时,逐步缩小 while (textWorldWidth > maxAllowedWidth && currentFontSize > minFontSize) { currentFontSize -= 1f; label3D.style.fontSize = currentFontSize; textSize = textMeasurement.MeasureText(label3D.text, label3D.style); textWorldWidth = textSize.x * label3D.transform.localScale.x; } // 确保字体不低于最小值 if (currentFontSize < minFontSize) { label3D.style.fontSize = minFontSize; } } }
方案2:文本截断显示
核心思路是计算当前字体下能容纳的最大文本长度,对原文本进行截断并添加省略号,保证显示内容不超出边界。
实现步骤
- 定义目标最大宽度,使用
TextMeasurementService测量文本片段的宽度。 - 从原文本开头逐步截取字符,每次测量截取后的文本宽度,直到接近目标宽度时停止,添加省略号。
代码示例
using UnityEngine; using UnityEngine.UIElements; [RequireComponent(typeof(Label3D))] public class TruncateLabel3D : MonoBehaviour { [SerializeField] private float maxAllowedWidth = 2f; private Label3D label3D; private TextMeasurementService textMeasurement; private string originalText; private void Awake() { label3D = GetComponent<Label3D>(); textMeasurement = new TextMeasurementService(); label3D.RegisterCallback<ChangeEvent<string>>(OnTextChanged); } private void OnTextChanged(ChangeEvent<string> evt) { originalText = evt.newValue; TruncateTextToFit(); } private void TruncateTextToFit() { if (string.IsNullOrEmpty(originalText)) { label3D.text = originalText; return; } string truncatedText = originalText; Vector2 textSize = textMeasurement.MeasureText(truncatedText, label3D.style); float textWorldWidth = textSize.x * label3D.transform.localScale.x; // 超出宽度时逐步截断文本 while (textWorldWidth > maxAllowedWidth && truncatedText.Length > 1) { truncatedText = truncatedText.Substring(0, truncatedText.Length - 1); // 拼接省略号并测量宽度 string displayText = truncatedText.EndsWith("...") ? truncatedText : $"{truncatedText}..."; textSize = textMeasurement.MeasureText(displayText, label3D.style); textWorldWidth = textSize.x * label3D.transform.localScale.x; } // 设置最终截断后的文本 label3D.text = truncatedText.EndsWith("...") ? truncatedText : $"{truncatedText}..."; } }
注意事项
maxAllowedWidth需根据3D表面实际尺寸调整,单位为米,与Label3D所在对象的缩放相关。- 若Label3D的
transform.localScale有变化,需在缩放改变时重新触发调整逻辑。 - 优先完整显示文本选方案1,优先保证文本可读性(避免字体过小)选方案2。
内容的提问来源于stack exchange,提问作者Martin Häusler
相关产品推荐
相关产品推荐

