Unity 实现文字过长显示省略号

需求:假设有一行文字由于界面空间不足只能显示一部分,那么剩余部分需要被截断,并且显示出来的文字末尾需要添加省略号“…”。比如“一二三四五六七八九十”,最终显示出来的样子是“一二三四…”。

这个需求在 CSS 中倒是挺好实现的。(题外话:这只限于显示出来的文字只有一行的情况。多行的情况下还是没那么容易,除非采用 WebKit 的 CSS 扩展属性 -webkit-line-clamp,不考虑兼容 W3C 标准。)但是在 Unity 中则需要摸索一下。目前已知的有两种方案。

方案一:使用 TextMesh Pro

当前的 Unity 2018 已经集成了 TextMesh Pro 插件(未确认之前的 Unity 版本是否集成)。这个插件非常强大,自然也包含了文字溢出时显示省略号的功能。使用时只要将 TextMesh Pro UGUI 组件中的 Overflow 选项设为 Ellipsis 即可。这个插件甚至能够很自然地实现多行文本的省略号截断。

但是这个方案有很明显的缺点—— TextMesh Pro 不支持动态字体,因此除非产品只需要英文,并且愿意使用 TextMesh Pro 自带的默认字体,否则一般都需要用插件自带的 Font Asset Creator 制作静态字体。这就意味着至少有如下几点需要注意:

  • TextMesh Pro 用的是 Unicode 编码,因此制作字体的时候要确保包含编码为 u2026 也就是省略号的字符,否则省略号截断的功能就没有效果。
  • 静态字体往往体积很大。我曾试着制作8192 × 8192的3500常用汉字的字体贴图,结果贴图大小达到了64MB左右,这对于很多小游戏小应用来说可能难以接受。当然可以试着调整参数来缩小体积,但一般也至少也有几MB,并且字体贴图越小,显示效果就越模糊。
  • 不知为什么时常会出现自制的字体无法正常显示的问题。通常重新只做一次字体文件或是重新创建一个 TextMesh Pro UGUI 对象可以解决问题。但是长此以往可能会烦不胜烦,并且制作一次字体资源非常耗时,甚至可能达到十几分钟。

方案二:自己写代码实现简单效果

自己写代码的好处是就不用费时费力地制作庞大的静态字体文件。

我 google 到了一位日本程序员写的文章,其中第4部分讲的就是如何实现省略号截断的功能:余談: 長い文章に対してellipsis(…)を適応するScript。大致原理是每帧利用 Unity 的 TextGenerator 类算出文本框的尺寸能容纳多少个字符,然后将能容纳的字符设置进文本框中,并将最后三个字符替换为省略号的三个点:“…”。

这种方法也能处理多行文本的情况。不足之处是,虽然在应对英文字符时没什么问题,但在应对中文字符时会提前截断,造成文本框末尾留有大片空白:

因此我对原文的代码做了一点修改,改为将最后一个字符替换为中文省略号:“…”,也就是省略号只占一个字符。这并不是完美的解决方案,但是至少文本框末尾的留白不会那么多了,在策划可以接受的范围内。

以下是我修改过的代码:

using UnityEngine;
using UnityEngine.UI;

public static class TextExtension
{
    // XXX operating in no overflow (Horizonal|Vertical)
    public static void SetTextWithEllipsis(this Text textComponent, string value)
    {
        // create generator with value and current Rect
        var generator = new TextGenerator();
        var rectTransform = textComponent.GetComponent();
        var settings = textComponent.GetGenerationSettings(rectTransform.rect.size);
        generator.Populate(value, settings);

        // trncate visible value and add ellipsis
        var characterCountVisible = generator.characterCountVisible;
        var updatedText = value;
        if (value.Length > characterCountVisible)
        {
            updatedText = value.Substring(0, characterCountVisible - 1);
            updatedText += "…";
        }

        // update text
        textComponent.text = updatedText;
    }
}

可以简单地每帧调用该接口:

using UnityEngine;
using UnityEngine.UI;

public class EllipsisTextTest : MonoBehaviour
{
    public string value;

    // Update is called once per frame
    void Update ()
    {
        this.GetComponent().SetTextWithEllipsis(value);
    }
}

但如果文字只设置一次的话,应该也可以只在初始化时调用一次。这一点还没确认过。

本文在我的独立博客上的地址:http://zxtechart.com/2018/08/30/unity-text-overflow-ellipsis/

你可能感兴趣的:(游戏开发)