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.文本每行居中,但定格显示

代码

cs 复制代码
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中设置一种直接打动画曲线(我自己也没尝试过)

cs 复制代码
public Text text;

private void TypingText() {
    float duration = 2.0f;
    text.DOText("sting", duration);
}
相关推荐
weixin_4242946714 小时前
在Unity中,摄像机移动时出现“残影”或“闪烁”是常见问题,主要原因和处理方法。
unity·游戏引擎
孟无岐15 小时前
【Laya】Browser 使用说明
typescript·游戏引擎·游戏程序·laya
天人合一peng15 小时前
unity 3d 通过游戏对象的名子查到其对象
游戏·unity·游戏引擎
纯属个人爱好16 小时前
Unity2020+PicoNeo3Pro开发
unity·vr
__water20 小时前
RHK《Unity接入DeepSeek问答》
unity·游戏引擎·智能问答·deepseek接入·deepseekapikey
康de哥21 小时前
MCP Unity + Claude Code 配置关键步骤
unity·mcp·claude code
美团骑手阿豪1 天前
Unity3D大规模点击检测:GPU Picking vs MeshCollider + Raycast
unity
在路上看风景1 天前
1.4 Unity运行时路径
unity·游戏引擎
郝学胜-神的一滴1 天前
Qt OpenGL 生成Mipmap技术详解
开发语言·c++·qt·系统架构·游戏引擎·图形渲染·unreal engine
孟无岐2 天前
【Laya】Laya 类使用说明
typescript·游戏引擎·游戏程序·laya