You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity技术咨询:检测UI未显示文本及文本溢出字符定位方法

嘿,刚好在Unity项目里折腾过UI文本溢出的相关问题,给你分享下我实际用到的解决方案,分两部分来说:

1. 判断UI文本是否存在未显示(溢出)的情况

这里分UGUI原生Text组件和TextMeshPro的TMP_Text组件两种场景:

针对UGUI Text组件

核心思路是计算文本实际需要的渲染宽度,再和容器的可用宽度对比(要扣除内边距)。可以用TextGenerator来精准计算:

public bool IsTextOverflow(Text text)
{
    TextGenerator textGenerator = new TextGenerator();
    // 获取当前文本组件的生成设置
    TextGenerationSettings genSettings = text.GetGenerationSettings(text.rectTransform.rect.size);
    // 填充文本内容到生成器
    textGenerator.Populate(text.text, genSettings);
    
    // 计算文本实际需要的宽度
    float requiredWidth = textGenerator.GetPreferredWidth(text.text, genSettings);
    // 计算容器的可用宽度(扣除左右内边距)
    float availableWidth = text.rectTransform.rect.width - text.padding.left - text.padding.right;
    
    // 实际宽度超过可用宽度则判定为溢出
    return requiredWidth > availableWidth;
}

注意:如果你的文本组件开启了horizontalOverflow = HorizontalWrapMode.Wrap,文本会自动换行,这时候溢出判断应该基于高度而非宽度,把代码里的宽度计算换成高度逻辑即可。

针对TextMeshPro TMP_Text组件

TMP提供了更便捷的内置属性,也可以手动计算验证:

public bool IsTMPTextOverflow(TMP_Text tmpText)
{
    // 直接用TMP自带的溢出判断属性(最便捷)
    if (tmpText.isTextOverflowing)
        return true;
    
    // 手动计算验证的方式
    float requiredWidth = tmpText.preferredWidth;
    float availableWidth = tmpText.rectTransform.rect.width - tmpText.margin.left - tmpText.margin.right;
    
    return requiredWidth > availableWidth;
}

2. 溢出时获取最后显示/第一个隐藏的字符

这个需求需要找到文本刚好超出容器宽度的临界点,这里同样分两种组件场景,还提供了性能优化的方案:

针对UGUI Text组件

基础逐字符遍历法(适合短文本)

逐字符累加计算宽度,直到找到溢出的那个字符:

public (char LastVisible, char FirstHidden) GetTextOverflowBoundary(Text text)
{
    // 先判断是否溢出,没溢出直接返回最后一个字符和空字符
    if (!IsTextOverflow(text))
        return (text.text.LastOrDefault(), '\0');
    
    TextGenerator textGenerator = new TextGenerator();
    TextGenerationSettings genSettings = text.GetGenerationSettings(text.rectTransform.rect.size);
    float availableWidth = text.rectTransform.rect.width - text.padding.left - text.padding.right;
    
    char lastVisible = '\0';
    char firstHidden = '\0';
    
    for (int i = 0; i < text.text.Length; i++)
    {
        string subText = text.text.Substring(0, i + 1);
        textGenerator.Populate(subText, genSettings);
        float currentWidth = textGenerator.GetPreferredWidth(subText, genSettings);
        
        if (currentWidth > availableWidth)
        {
            firstHidden = text.text[i];
            if (i > 0) lastVisible = text.text[i - 1];
            break;
        }
        lastVisible = text.text[i];
    }
    
    return (lastVisible, firstHidden);
}

二分查找优化法(适合长文本)

如果文本很长,逐字符遍历性能不够,可以用二分查找快速定位最大可见字符数:

// 先获取最大可见字符数
private int GetMaxVisibleCharCount(Text text)
{
    TextGenerator textGenerator = new TextGenerator();
    TextGenerationSettings genSettings = text.GetGenerationSettings(text.rectTransform.rect.size);
    float availableWidth = text.rectTransform.rect.width - text.padding.left - text.padding.right;
    
    int left = 0;
    int right = text.text.Length;
    int maxVisibleCount = 0;
    
    while (left <= right)
    {
        int mid = (left + right) / 2;
        if (mid == 0)
        {
            left = mid + 1;
            continue;
        }
        
        string subText = text.text.Substring(0, mid);
        textGenerator.Populate(subText, genSettings);
        float subWidth = textGenerator.GetPreferredWidth(subText, genSettings);
        
        if (subWidth <= availableWidth)
        {
            maxVisibleCount = mid;
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }
    
    return maxVisibleCount;
}

// 再通过最大可见数获取边界字符
public (char LastVisible, char FirstHidden) GetTextOverflowBoundaryOptimized(Text text)
{
    if (!IsTextOverflow(text))
        return (text.text.LastOrDefault(), '\0');
    
    int maxCount = GetMaxVisibleCharCount(text);
    char lastVisible = maxCount > 0 ? text.text[maxCount - 1] : '\0';
    char firstHidden = maxCount < text.text.Length ? text.text[maxCount] : '\0';
    
    return (lastVisible, firstHidden);
}

针对TextMeshPro TMP_Text组件

TMP的文本信息更透明,可以直接通过textInfo获取每个字符的位置和尺寸,效率更高:

public (char LastVisible, char FirstHidden) GetTMPOverflowBoundary(TMP_Text tmpText)
{
    if (!tmpText.isTextOverflowing)
        return (tmpText.text.LastOrDefault(), '\0');
    
    // 强制更新网格信息,确保字符数据是最新的
    tmpText.ForceMeshUpdate();
    TMP_TextInfo textInfo = tmpText.textInfo;
    float availableWidth = tmpText.rectTransform.rect.width - tmpText.margin.left - tmpText.margin.right;
    
    char lastVisible = '\0';
    char firstHidden = '\0';
    
    for (int i = 0; i < textInfo.characterCount; i++)
    {
        TMP_CharacterInfo charInfo = textInfo.characterInfo[i];
        // 字符的右边界超过容器可用宽度,判定为第一个隐藏字符
        if (charInfo.xAdvance + charInfo.origin > availableWidth)
        {
            firstHidden = tmpText.text[i];
            if (i > 0) lastVisible = tmpText.text[i - 1];
            break;
        }
        lastVisible = tmpText.text[i];
    }
    
    return (lastVisible, firstHidden);
}

内容的提问来源于stack exchange,提问作者MBS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:53:37