Unity3D 文字线条效果

基于Unity实现的文字线条效果,代码添加到Text组件的物体上修改Type属性即可看到效果。

2021/4/22. 添加分辨率适配

using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;

public enum LineType {
    none,
    upperline,
    middleLine,
    underLine
}

public class Line {
    public Vector2 position = Vector2.zero;
    public Vector2 size = Vector2.one;
    public Vector2 pivot = Vector2.zero;
    public Color color = Color.black;

    public Line(Text text, LineType type, int index) {
        var characters = text.cachedTextGenerator.characters;
        var lines = text.cachedTextGenerator.lines[index];
        UICharInfo charInfo = characters[lines.startCharIdx];
        CanvasScaler cs = text.GetComponentInParent<CanvasScaler>();
        float scaleFactor = (Screen.width * cs.referenceResolution[1] / (Screen.height * cs.referenceResolution[0])) >= 1 ? cs.referenceResolution[1] / Screen.height : cs.referenceResolution[0] / Screen.width;
        position = CalculatePosition(text.alignment, text.rectTransform.rect.width,
            (charInfo.cursorPos.y - text.cachedTextGenerator.lines[index].height * GetTypeWeight(type)) * scaleFactor);
        size = new Vector2(GetWidth(characters, lines.startCharIdx, characters.Count - 1, text.rectTransform.rect.width),
            text.fontSize * 0.1f == 0 ? float.Epsilon : text.fontSize * 0.1f) * scaleFactor;
        color = text.color;
        SetPivot(text.alignment);
    }

    // public Line(Text text, LineType type, int index, int start, int length) {
    //     UICharInfo charInfo = text.cachedTextGenerator.characters[start];
    //     position = new Vector2(charInfo.cursorPos.x, charInfo.cursorPos.y - text.cachedTextGenerator.lines[index].height * GetTypeWeight(type));
    //     size = new Vector2(GetWidth(text.cachedTextGenerator.characters, start, length - 1),
    //         text.fontSize * 0.1f == 0 ? float.Epsilon : text.fontSize * 0.1f);
    //     color = text.color;
    //     SetPivot(text.alignment);
    // }

    float GetTypeWeight(LineType type) {
        switch (type) {
        case LineType.upperline:
            return 0;
        case LineType.middleLine:
            return 0.5f;
        case LineType.underLine:
        default:
            return 1;
        }
    }

    float GetWidth(IList<UICharInfo> characters, int start, int end, float width) {
        float num = 0;
        int lastCharIdx = end - 1;
        for (int i = start; i < end; ++i) {
            if (characters[i].cursorPos[0] > characters[i + 1].cursorPos[0]) {
                num = characters[i].cursorPos[0] + characters[i].charWidth - characters[start].cursorPos[0];
                break;
            }

            if (i == lastCharIdx) {
                num = characters[i + 1].cursorPos[0] - characters[start].cursorPos[0];
                break;
            }
        }
        return num >= width ? width * 0.5f : num;
    }

    public Vector2 CalculatePosition(TextAnchor anchor, float width, float y) {
        switch (anchor) {
        case TextAnchor.MiddleCenter:
        case TextAnchor.UpperCenter:
        case TextAnchor.LowerCenter:
            return Vector2.up * y;
        case TextAnchor.MiddleRight:
        case TextAnchor.UpperRight:
        case TextAnchor.LowerRight:
            return new Vector2(width * 0.5f, y);
        default:
            return new Vector2(-width * 0.5f, y);
        }
    }

    void SetPivot(TextAnchor anchor) {
        switch (anchor) {
        case TextAnchor.LowerLeft:
            pivot = Vector2.zero;
            break;
        case TextAnchor.LowerCenter:
            pivot = new Vector2(0.5f, 0);
            break;
        case TextAnchor.LowerRight:
            pivot = new Vector2(1, 0);
            break;
        case TextAnchor.MiddleLeft:
            pivot = new Vector2(0, 0.5f);
            break;
        case TextAnchor.MiddleCenter:
            pivot = Vector2.one * 0.5f;
            break;
        case TextAnchor.MiddleRight:
            pivot = new Vector2(1, 0.5f);
            break;
        case TextAnchor.UpperLeft:
            pivot = new Vector2(0, 1);
            break;
        case TextAnchor.UpperCenter:
            pivot = new Vector2(0.5f, 1);
            break;
        case TextAnchor.UpperRight:
            pivot = Vector2.one;
            break;
        default:
            pivot = Vector2.zero;
            break;
        }
    }
}

public class LineMesh : MonoBehaviour {
    private Text text;
    public LineType type = LineType.none;

    private bool isInitialize = false;

    private int characterCount = 0;
    private LineType currentType = LineType.none;
    private Color currentColor = Color.white;
    private FontStyle currentFontStyle;
    private TextAnchor currentAnchor;
    private float lineSpacing;

    private List<Image> lines;

    // 标签化正则, 暂时未实现
    // 删除线
    //private const string deleteLineEvaluator = @"~~[^~]+?~~";
    //private const string deleteLineContentEvaluator = @"(?<=~~)[^~]+?(?=~~)";

    // 上划线
    //private const string underLineEvaluator = @"<up>.*</up>";
    //private const string underLineContentEvaluator = @"(?<=<up>).*(?=</up>)";

    // 下划线
    //private const string underLineEvaluator = @"<un>.*</un>";
    //private const string underLineContentEvaluator = @"(?<=<un>).*(?=</un>)";

    private void Awake() {
        text = this.GetComponent<Text>();
        lines = new List<Image>();
    }

    // Start is called before the first frame update
    private void Start() {
        UpdateParams();
        isInitialize = true;
    }

    // Update is called once per frame
    private void Update() {
        if (isInitialize) {
            if (type == LineType.none && currentType != type) {
                currentType = type;
                ClearLines();
            } else if (currentType != type || characterCount != text.cachedTextGenerator.characterCount ||
                currentColor != text.color || currentFontStyle != text.fontStyle ||
                currentAnchor != text.alignment || lineSpacing != text.lineSpacing) {
                UpdateParams();
                List<Line> lines = GetLines(type);
                CreateLines(lines);
            }
        }
    }

    private void UpdateParams() {
        characterCount = text.cachedTextGenerator.characterCount;
        currentType = type;
        currentColor = text.color;
        currentFontStyle = text.fontStyle;
        currentAnchor = text.alignment;
        lineSpacing = text.lineSpacing;
    }

    private List<Line> GetLines(LineType type) {
        if (type == LineType.none) return null;
        List<Line> lineInfos = new List<Line>();
        for (int j = 0; j < text.cachedTextGenerator.lineCount; ++j) {
            Line line = new Line(text, type, j);
            if (line.size[0] == 0 || line.size[1] == 0)
                continue;
            lineInfos.Add(line);
        }
        return lineInfos;
    }

    private void ClearLines() {
        foreach (var line in lines)
            Destroy(line.gameObject);
        lines.Clear();
    }

    private void CreateLines(List<Line> lineInfos) {
        if (lineInfos == null) return;
        ClearLines();
        for (int i = 0; i < lineInfos.Count; ++i) {
            Image img = new GameObject().AddComponent<Image>();
            img.transform.SetParent(text.transform, false);
            img.name = $"line{i}";
            img.color = lineInfos[i].color;
            img.rectTransform.pivot = lineInfos[i].pivot;
            lines.Add(img);

            Texture2D texture = new Texture2D((int)lineInfos[i].size[0], (int)lineInfos[i].size[1], TextureFormat.ARGB32, false);
            var colors = texture.GetPixels();
            for (int j = 0; j < colors.Length; ++j)
                colors[j] = img.color;
            texture.SetPixels(colors);
            texture.Apply();

            img.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
            img.SetNativeSize();
            img.rectTransform.sizeDelta = img.rectTransform.sizeDelta;
            lines[i].rectTransform.anchoredPosition = lineInfos[i].position;
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Unity 3D文字特效是指在Unity引擎中使用文本来创造出独特的视觉效果和动画效果。它可以使文字呈现出炫酷的外观和动态效果,增强游戏或应用程序的视觉吸引力。 Unity 3D文字特效可以通过使用Unity中的预设或编写自定义的脚本来实现。预设是预先制作好的文本特效,包括旋转、缩放、闪烁、爆炸等效果。用户可以直接在场景中添加这些预设,并调整其参数以获得所需的外观。 另一种方式是通过编写脚本来创建自定义的文字特效。用户可以使用C#或Unity的自带脚本语言来控制文本的动态行为。例如,可以编写一个脚本来使文字改变颜色、随时间变化、根据物理效果进行移动等。 Unity 3D文字特效可以用于游戏中的标题、菜单、角色对话框等各种场景。它可以提升用户体验,增加游戏的乐趣和互动性。通过使用不同的字体、颜色、大小和动画效果,可以为游戏中的文本创造出与众不同的风格和情感。 除了游戏开发,Unity 3D文字特效也可用于其他类型的应用程序,如广告、教育、建模等。它可以帮助开发者更好地传达信息,吸引用户的注意力。 总之,Unity 3D文字特效是一种非常强大和灵活的工具,可以使文字在游戏和应用程序中展现出令人惊叹的效果。无论是通过使用预设还是编写自定义脚本,用户都可以根据自己的需求打造出独特而引人注目的文字效果。 ### 回答2: Unity 3D文字特效是一种用于在Unity游戏引擎中创建和展示炫酷的文字效果的功能。通过这个特效,我们可以在游戏中添加各种各样的文字效果,例如动态的文字动画、闪烁、投影、光影、渐变等等。 要使用Unity 3D文字特效,首先我们需要创建一个3D文本对象。在Unity中,我们可以通过在场景中创建一个空对象,然后将文字网格组件添加到该对象上来实现。添加完组件后,我们可以在资源文件夹中选择任意字体,应用到文本对象上,并设置字体的大小、位置、对齐方式和字间距等属性。 在创建完文本对象后,我们可以通过添加脚本来实现更加复杂的文字特效。例如,我们可以通过脚本来实现文字的旋转、缩放、颜色变化等动画效果。我们还可以通过添加碰撞器和物理材料,使得文字在游戏场景中可交互,例如文字随着玩家的触碰而变大或者发生碰撞。 此外,Unity 3D文字特效还支持使用粒子系统来制作更加震撼的文字效果。我们可以通过创建一个粒子系统,将其以某种方式与文本对象关联起来,从而实现文字的粒子喷射、消散、渐变等效果。通过调整粒子系统的参数,我们可以改变粒子的形状、大小、颜色和运动方式,从而创造出很多独特的文字特效。 总之,Unity 3D文字特效是一个非常强大的功能,可以帮助我们在游戏中创建出各种各样炫酷的文字效果。无论是制作一个悬浮的菜单栏、一个震撼的开场动画,还是一个令人印象深刻的字幕效果Unity 3D文字特效都可以帮助我们实现。 ### 回答3: Unity 3D文字特效是指在Unity 3D游戏引擎中使用特定技术和工具实现的文字效果。这些效果可以为游戏增添视觉吸引力、增强沉浸感,提升用户的游戏体验。 Unity 3D文字特效有多种实现方式。其中,使用粒子系统是常见的一种方式。通过在Unity中创建和调整粒子系统,我们可以很容易地实现文字的燃烧、爆炸、流动等效果。粒子系统可以控制粒子的形状、速度、大小、颜色等属性,使文字看起来更加生动。 另一种常见的文字特效是使用着色器(Shader)。着色器是以GPU为基础的程序,它可以修改游戏对象或文字的渲染方式。通过使用特定的着色器,我们可以实现文字的发光、阴影、扭曲、反射等效果,从而让文字看起来更加华丽和真实。 此外,Unity 3D还提供了丰富的动画功能,可以用于制作文字的动态效果。我们可以通过在Unity中创建动画控制器和动画序列帧,实现文字的位移、旋转、缩放等动态变换。这样,文字可以在游戏中呈现出平滑流畅的动画效果。 总的来说,Unity 3D文字特效能够提供丰富多样的视觉效果,丰富了游戏的画面和氛围。开发者可以根据游戏的风格和需求,选择适合的特效方式,从而为玩家带来更加吸引人的游戏体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值