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);
}
相关推荐
心前阳光2 小时前
Unity WebGL文本输入
unity·游戏引擎·webgl
天涯过客TYGK3 小时前
unity A星寻路
unity·游戏引擎
KhalilRuan3 小时前
Unity Demo——3D平台跳跃游戏笔记
笔记·游戏·unity·游戏引擎
AA陈超1 天前
虚幻引擎UE5专用服务器游戏开发-20 添加基础能力类与连招能力
c++·游戏·ue5·游戏引擎·虚幻
ttod_qzstudio1 天前
Unity中使用EzySlice实现模型切割与UV控制完全指南
unity
南無忘码至尊1 天前
Unity 实现与 Ollama API 交互的实时流式响应处理
unity·游戏引擎·交互
平行云1 天前
如何实现UE程序大并发多集群的像素流部署
unity·ue5·图形渲染
mit6.8242 天前
[shad-PS4] Vulkan渲染器 | 着色器_重新编译器 | SPIR-V 格式
c++·游戏引擎·ps4
向宇it2 天前
【unity小技巧】在 Unity 中将 2D 精灵添加到 3D 游戏中,并实现阴影投射效果,实现类《八分旅人》《饥荒》等等的2.5D游戏效果
游戏·3d·unity·编辑器·游戏引擎·材质
向宇it2 天前
Unity Universal Render Pipeline/Lit光照材质介绍
游戏·unity·c#·游戏引擎·材质