Unity 实现字幕打字效果

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

效果

请添加图片描述

实现功能

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

脚本使用

请添加图片描述
请添加图片描述

属性 解释
TypingSpeed 打字速度(.s 每过多少时间打一个)
RowShowMax 一行显示的最大值
AllowRedundancy 每行能够允许的冗余
IsUpdateText 更新文本,默认不启动,勾选点击运行就更新
Text 挂载text文本
OffsetY 对每行上移的补偿
SaveMarqueeoriginPosition 保存字幕整体的初始位置
TextCloseDelayTime 字幕消失的时间(Obsolete)
BottomShow 需要挂载下方显示的整体
函数 功能
UpdateText 更新文本,需要再Update中调用
OnFinish 更新完成,更新文本完成后调用的函数
OnTextUpdate(string) 文本更新,在打字过程中如果发生文本更新需要调用的方法
OnTextReset 文本重置


设置

** 1.物体设置 **
请添加图片描述

** 2.text文本设置 **
请添加图片描述
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);
}

猜你喜欢

转载自blog.csdn.net/blastospore/article/details/132140422