Text文本打字效果,TextMeshPro可以对应参考,差距不大,改改参数类型就能用。该脚本原本被我集成到其他的程序集中,现在已经分离。

效果

unity 文字配音_游戏引擎

实现功能

1.能够设置每行能够容纳的字数和允许的冗余
2.打字效果
3.每行打完上移
4.开头进入,结束弹出
5.行居中

脚本使用

unity 文字配音_unity_02


unity 文字配音_游戏引擎_03

属性

解释

TypingSpeed

打字速度(.s 每过多少时间打一个)

RowShowMax

一行显示的最大值

AllowRedundancy

每行能够允许的冗余

IsUpdateText

更新文本,默认不启动,勾选点击运行就更新

Text

挂载text文本

OffsetY

对每行上移的补偿

SaveMarqueeoriginPosition

保存字幕整体的初始位置

TextCloseDelayTime

字幕消失的时间(Obsolete)

BottomShow

需要挂载下方显示的整体

函数

功能

UpdateText

更新文本,需要再Update中调用

OnFinish

更新完成,更新文本完成后调用的函数

OnTextUpdate(string)

文本更新,在打字过程中如果发生文本更新需要调用的方法

OnTextReset

文本重置



设置

** 1.物体设置 **

unity 文字配音_unity 文字配音_04

** 2.text文本设置 **

unity 文字配音_unity 文字配音_05


1.需要文字的size(如果有外描边的话)来计算字占据的行宽,来决定每行上移的距离

2.文本每行居中,且顶格显示。

代码

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

namespace YBZ {

    public class TypewriterEffect : MonoBehaviour 
    {
     
        // 测试文本
        private readonly string text_test = "SVN中我需要维护的东西Scene; Scripts/Engine/WSC SteamingAsset/2DPivots.json Editor/WSCPivotEditor.cs \n 下方提示";

        [Header("文本显示"), Space(10)]
        public float typingSpeed = 0.2f; // 打字速度
        public int rowShowMax = 25; // 一行最大显示数量
        public int allowRedundancy = 6; // 允许的冗余数量
        public bool isUpdateText = false; // 是否播放

        public Text text; // 底部字幕

        [Range(-25, 25), Space(5)]
        public int offsetY = -3; // 第一行Y轴补偿
        public Vector3 saveMarqueeOriginPostion = new Vector3(0, -63, 0); // 下方字幕的原初位置

        private float textTimeCount = 0; // 更新用的计时器
        private string word = ""; //保存字幕
        private int currentPos = 0; // 打字字符索引
        private int LineBreakCount = 0; // 换行符计数

        private Vector3 saveTextLocatePostion;  // 用于保存TextUI位置

        private bool isOriginPosition = true;

        void Update() {
            UpdateText();
            if (Input.GetMouseButtonDown(0)) {
                OnTextUpdate(text_test);
            }
        }
        
        // 文本更新
        private void UpdateText() {
            if (!isUpdateText) {
                return;
            }

            // 检查字幕是否位于原初位置
            if (isOriginPosition) {
                isOriginPosition = !isOriginPosition;
                BottomShow.transform.DOLocalMove(Vector3.zero, 1.0f);
            }

            BottomShow.SetActive(true);

            if (saveTextLocatePostion == Vector3.zero) {
                saveTextLocatePostion = text.rectTransform.localPosition;
            }

            if (word == "") {
                word = text.text;
            }

            textTimeCount += Time.deltaTime;
            if (textTimeCount > typingSpeed) {
                textTimeCount = 0;
                currentPos++;
                if (currentPos >= word.Length) {
                    Debug.Log("播放完成");

                    OnFinish();
                    return;
                }
                text.text = word[..currentPos];//刷新文本显示内容
                if (word[currentPos - 1] == '\n') {
                    Debug.Log("发现换行符");
                    LineBreakCount++;

                    // 每次遇到一个换行符就上移25个单位
                    if (LineBreakCount == 1) {
                        text.rectTransform.DOLocalMoveY(text.rectTransform.localPosition.y + 25 + offsetY, 1f);
                    } else if (LineBreakCount != 1) {
                        text.rectTransform.DOLocalMoveY(text.rectTransform.localPosition.y + 25, 1f);
                    }

                }

                // 每次处理行超限
                int lineCount;
                if (LineBreakCount == 0) {
                    lineCount = text.text.Length;
                } else {
                    lineCount = text.text[text.text.LastIndexOf('\n')..].Length;
                }

                // 行超限
                if (lineCount > rowShowMax + allowRedundancy) {
                    word = text.text + '\n' + word[text.text.Length..];
                }

            }
        }

        [Header("下方显示延迟消失的所需要的时间")]
        public float textCloseDelayTime = 2.0f;

        /// <summary>
        /// 下方提示游戏物体,在使用前预加载
        /// </summary>
        public GameObject BottomShow;

        public IEnumerator IE_TextCloseDelayTime(float time) {
            yield return new WaitForSeconds(time);
            BottomShow.SetActive(false);
            Debug.Log("下方显示已关闭");
        }

        public IEnumerator IE_OnTextReset(float time) {
            yield return new WaitForSeconds(time);
            OnTextReset();
            Debug.Log("文本恢复默认");
            isOriginPosition = true;
            BottomShow.transform.DOLocalMove(saveMarqueeOriginPostion, 1.0f);
        }

        // 文本更新完成 , 一旦确认关闭就不要再更新文本, 否侧会出现逻辑错误,如若在播放完毕后更新文本一定要在下方显示关闭后, 字幕回滚的时候不能更新文本.
        private void OnFinish() {
            isUpdateText = false;
            // 完成之后下方显示 延迟关闭, 位置回调
            // StartCoroutine(IE_TextCloseDelayTime(textCloseDelayTime));
            // 延迟文本重置位置
            StartCoroutine(IE_OnTextReset(textCloseDelayTime));
            text.rectTransform.DOLocalMove(saveTextLocatePostion + new Vector3(0, offsetY, 0), textCloseDelayTime);
        }

        // 文本更新, 一旦更新就是确定要开始播放(╯‵□′)╯︵┻━┻(你更新不是为了播放?)
        private void OnTextUpdate(string newtext) {

            text ??= GameObject.Find("字幕文字").gameObject.GetComponent<Text>();
            OnTextReset();
            word = newtext;
            // StartCoroutine(IE_OnTextReset(0.5f));
            BottomShow.SetActive(true);
            isUpdateText = true;
        }

        // 文本恢復默认: 索引为0, 换行符统计为0, LocalPostion恢复, 文本置空
        private void OnTextReset() {
            if (saveTextLocatePostion == Vector3.zero) {
                saveTextLocatePostion = text.rectTransform.localPosition;
            }
            text.rectTransform.localPosition = saveTextLocatePostion;
            text.text = "";
            LineBreakCount = 0;
            currentPos = 0;
        }

        // 初始化
        public void Init() {
            Debug.Log("文本更新初始化完成");
        }

        public void UnInit() {
            Debug.Log("文本控制结束");
        }
        private void OnDestroy() {
            UnInit();
        }
    }
}

补充:
使用DoTween插件也可以实现对应的效果,但是如果不修改动画曲线会出现,一开始打字快,后续打字愈来愈慢,可以自己在DoTween中设置一种直线的动画曲线(我自己也没尝试过如何设置动画曲线)

public Text text;

private void TypingText() {
    float duration = 2.0f;
    text.DOText("sting", duration);
}