本文章用于原生组件 Text 的扩展 TextRainbow,对于新版TextMeshPro不适用。

一、效果预览图:

默认:Unity扩展 Text 彩虹文本-LMLPHP

随机:Unity扩展 Text 彩虹文本-LMLPHP

循环:Unity扩展 Text 彩虹文本-LMLPHP

二、原理

通过强制刷新顶点数据,来修改颜色。

通过Unity中自带的 BaseMeshEffect 抽象类,可以直接修改UI元素的网格,从而达到比如阴影,描边,UV顶点颜色等视觉效果。

新建一个脚本继承 BaseMeshEffect 抽象类,通过 ModifyMesh 方法来自定义想要的效果。 ModifyMesh 方法中自带参数类型 VertexHelper,通过参数类型中提供的 GetUIVertexStream、PopulateUIVertex 和 SetUIVertex 方法来获取当前网格的顶点数据并重新设置回去。

彩虹颜色的方式有很多种,这里推荐使用渐变 Gradient 去做比较方便。Gradient 的颜色设置和获取在代码中已有展示,没啥好说的。

对于颜色的显示三种方式,循环模式是通过Update方法驱动渐变颜色索引实现的,并且添加是否忽略引擎的TimeScale影响。

三、代码

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;
[RequireComponent(typeof(Text))]
public class TextRainbow : BaseMeshEffect
{
    public enum VerticesColorType
    {
        None,
        Random,
        Loop
    }
    [SerializeField] protected Text target;
    /// <summary>
    /// 彩虹条
    /// </summary>
    public Gradient gradient = new();
    /// <summary>
    /// 彩虹条颜色数量
    /// </summary>
    private int ColorCount => gradient.colorKeys.Length;
    /// <summary>
    /// 当前索引下标 0 ~ gradientColors
    /// </summary>
    private int _index = 0;
    /// <summary>
    /// 进度,0 ~ 1
    /// </summary>
    private float _timeProcess = 0;
    private bool _openRainbow = false;
    /// <summary>
    /// 存在彩虹文本
    /// </summary>
    private bool _haveRainbow = false;
    private readonly List<UIVertex> _vertices = new();
    /// <summary>
    /// 滚动速度
    /// </summary>
    public float ScrollSpeed = 3f;
    /// <summary>
    /// 打开彩虹
    /// </summary>
    public bool OpenRainbow
    {
        get => _openRainbow;
        set
        {
            if (_openRainbow == value)
            {
                return;
            }
            _openRainbow = value;
            UpdateIndex();
            HaveRainbowCondition();
            target.SetVerticesDirty();
        }
    }
    /// <summary>
    /// 彩虹颜色类型
    /// </summary>
    private VerticesColorType _colorType = VerticesColorType.None;
    public VerticesColorType ColorType
    {
        get => _colorType;
        set
        {
            if (_colorType == value)
            {
                return;
            }
            _colorType = value;
            UpdateIndex();
            if (_openRainbow)
            {
                target.SetVerticesDirty();
            }
        }
    }
    /// <summary>
    /// 忽略时间缩放
    /// </summary>
    public bool unScaledTime = false;
    public string Text
    {
        get => target.text;
        set => SetText(value);
    }
    protected override void Awake()
    {
        _index = 0;
        if (!target)
        {
            target = GetComponent<Text>();
        }
    }
    private void Update()
    {
        if (!OpenRainbow)
        {
            return;
        }
        if (!_haveRainbow)
        {
            return;
        }
        if (_colorType != VerticesColorType.Loop) return;
        _timeProcess += (unScaledTime ? Time.unscaledDeltaTime : Time.deltaTime) * ScrollSpeed;
        if (!(_timeProcess >= 1)) return;
        _timeProcess -= 1;
        _index++;
        target.SetVerticesDirty();
        if (_index >= ColorCount)
        {
            _index = 0;
        }
    }
#if UNITY_EDITOR
    protected override void Reset()
    {
        base.Reset();
        target = GetComponent<Text>();
    }
#endif
    public override void ModifyMesh(VertexHelper vh)
    {
        if (!_haveRainbow)
        {
            return;
        }
        if (!IsActive() || vh.currentIndexCount == 0)
        {
            return;
        }
        _vertices.Clear();
        vh.GetUIVertexStream(_vertices);
        UIVertex uiVertex = new();
        for (var i = 0; i < vh.currentVertCount; ++i)
        {
            vh.PopulateUIVertex(ref uiVertex, i);
            uiVertex.color = CalcColor(i);
            vh.SetUIVertex(uiVertex, i);
        }
    }
    private Color CalcColor(int index)
    {
        if (!OpenRainbow)
        {
            return target.color;
        }
        index /= 4;
        index += _index;
        if (index >= ColorCount)
        {
            index %= ColorCount;
        }

        return gradient.Evaluate(gradient.colorKeys[index].time);
    }
    private void SetText(string text)
    {
        if (string.IsNullOrEmpty(text))
        {
            _haveRainbow = false;
            target.text = string.Empty;
            return;
        }
        _haveRainbow = OpenRainbow;
        target.text = text;
    }
    private void HaveRainbowCondition()
    {
        if (string.IsNullOrEmpty(Text))
        {
            _haveRainbow = false;
            return;
        }
        _haveRainbow = OpenRainbow;
    }
    private void UpdateIndex()
    {
        switch (_colorType)
        {
            case VerticesColorType.Random:
                _index = Random.Range(0, ColorCount);
                break;
            case VerticesColorType.None:
            case VerticesColorType.Loop:
            default:
                _index = 0;
                break;
        }
    }
}

 面板扩展:

using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(TextRainbow), true)]
[CanEditMultipleObjects]
public class TextRainbowEditor : Editor
{
    private const int NumberOfColors = 7;
    private TextRainbow Target => (TextRainbow)target;
    private SerializedProperty _component;
    private SerializedProperty _unScaledTime;
    protected void OnEnable()
    {
        _component = serializedObject.FindProperty("target");
        _unScaledTime = serializedObject.FindProperty("unScaledTime");
    }
    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        GUI.enabled = false;
        EditorGUILayout.PropertyField(_component);
        GUI.enabled = true;
        EditorGUI.BeginChangeCheck();
        #region Gradient
        EditorGUILayout.BeginHorizontal();
        Target.gradient = EditorGUILayout.GradientField("Gradient", Target.gradient);
        // 预设彩虹渐变
        if (GUILayout.Button("Rainbow", GUILayout.Width(80)))
        {
            DrawRainbowGradient();
        }
        EditorGUILayout.EndHorizontal();
        #endregion
        Target.ColorType = (TextRainbow.VerticesColorType)EditorGUILayout.EnumPopup("Color Type", Target.ColorType);
        Target.ScrollSpeed = EditorGUILayout.Slider("Scroll Speed", Target.ScrollSpeed, 0, 10);
        Target.OpenRainbow = EditorGUILayout.Toggle("Open Rainbow", Target.OpenRainbow);
        EditorGUILayout.PropertyField(_unScaledTime);
        EditorGUILayout.LabelField("Text");
        Target.Text = EditorGUILayout.TextArea(Target.Text, GUILayout.MinHeight(EditorGUIUtility.singleLineHeight * 3));
        if (EditorGUI.EndChangeCheck())
        {
            EditorUtility.SetDirty(Target);
        }
        serializedObject.ApplyModifiedProperties();
    }
    
    // 预设彩虹渐变
    private void DrawRainbowGradient()
    {
        var colorKeys = new GradientColorKey[NumberOfColors];
        for (var i = 0; i < NumberOfColors; i++)
        {
            float dividend = NumberOfColors - 1;
            var index = i;
            if (i == 1)
            {
                dividend = 12f;
            }
            else if (i > 1)
            {
                index = i - 1;
            }
            colorKeys[i].color = Color.HSVToRGB(index / dividend, 1f, 1f);
            colorKeys[i].time = (float)i / (NumberOfColors - 1);
        }
        Target.gradient.colorKeys = colorKeys;
        Target.gradient.alphaKeys = new[]
        {
            new GradientAlphaKey(1, 0),
            new GradientAlphaKey(1, 1)
        };
    }
}

写出来后稍加整理就发出来了,如果有更优解(原生组件扩展)欢迎指教。

08-19 00:02