当前位置: 首页 > backend >正文

Unity Text打字机效果,支持富文本

将之前的代码给deepseek跑了一下,感觉优化的很不错。效果与预期相符。
能正确显示和解析富文本
TypewriterEffect.cs

using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;namespace MYTOOL.UI
{[DisallowMultipleComponent][RequireComponent(typeof(UnityEngine.UI.Text))]public class TypewriterEffect : MonoBehaviour{[SerializeField, Tooltip("默认打字速度(每个字符的间隔时间)")]private float defaultTypeInterval = 0.05f;private UnityEngine.UI.Text targetTextComponent;private readonly StringBuilder typewriterBuilder = new StringBuilder();private string processedText;private Coroutine typingCoroutine;private Action onCompleteCallback;private static readonly string[] uguiRichTextTags = { "b", "i", "size", "color" };private readonly Stack<string> activeTags = new Stack<string>();private void Awake(){ValidateTextComponent();}public void StartTyping(string content, float speed = -1){ValidateTextComponent();PrepareForNewTyping(content, speed);typingCoroutine = StartCoroutine(TypingRoutine(content));}public void SkipTyping(){if (!IsTyping) return;StopCoroutine(typingCoroutine);typingCoroutine = null;targetTextComponent.text = processedText;activeTags.Clear();onCompleteCallback?.Invoke();}public bool IsTyping => typingCoroutine != null;public void SetCompleteCallback(Action callback){onCompleteCallback = callback;}private void ValidateTextComponent(){if (targetTextComponent == null){targetTextComponent = GetComponent<UnityEngine.UI.Text>();if (targetTextComponent == null){throw new MissingComponentException("Text component is required for TypewriterEffect");}}}private void PrepareForNewTyping(string content, float speed){defaultTypeInterval = speed > 0 ? speed : defaultTypeInterval;processedText = ProcessSpeedTags(content);targetTextComponent.text = string.Empty;activeTags.Clear();if (typingCoroutine != null){StopCoroutine(typingCoroutine);}}private IEnumerator TypingRoutine(string originalText){typewriterBuilder.Clear();float currentSpeed = defaultTypeInterval;for (int index = 0; index < originalText.Length; index++){if (TryProcessSpeedTag(originalText, ref index, out float newSpeed)){currentSpeed = newSpeed;continue;}if (TryProcessRichTextTag(originalText, ref index)){UpdateTextDisplay();continue;}typewriterBuilder.Append(originalText[index]);UpdateTextDisplay();yield return new WaitForSeconds(currentSpeed);}FinalizeTyping();}private bool TryProcessSpeedTag(string text, ref int index, out float speed){speed = defaultTypeInterval;const string speedTagOpen = "[speed=";const string speedTagClose = "[/speed]";// 处理开标签if (text[index] == '[' && text.Length > index + speedTagOpen.Length && text.Substring(index, speedTagOpen.Length) == speedTagOpen){int closingBracketIndex = text.IndexOf(']', index);if (closingBracketIndex == -1) return false;string speedValue = text.Substring(index + speedTagOpen.Length, closingBracketIndex - index - speedTagOpen.Length);if (float.TryParse(speedValue, out speed)){index = closingBracketIndex;return true;}}// 处理闭标签if (text.Length > index + speedTagClose.Length && text.Substring(index, speedTagClose.Length) == speedTagClose){speed = defaultTypeInterval;index += speedTagClose.Length - 1;return true;}return false;}private bool TryProcessRichTextTag(string text, ref int index){if (text[index] != '<') return false;// 处理开标签foreach (var tag in uguiRichTextTags){string tagStart = $"<{tag}";if (text.Length > index + tagStart.Length && text.Substring(index, tagStart.Length) == tagStart){int closingIndex = text.IndexOf('>', index);if (closingIndex == -1) return false;string fullTag = text.Substring(index, closingIndex - index + 1);typewriterBuilder.Append(fullTag);activeTags.Push(tag);index = closingIndex; // 跳过整个标签内容return true;}}// 处理闭标签foreach (var tag in uguiRichTextTags){string tagEnd = $"</{tag}>";if (text.Length >= index + tagEnd.Length && text.Substring(index, tagEnd.Length) == tagEnd){if (activeTags.Count > 0 && activeTags.Peek() == tag){activeTags.Pop();}typewriterBuilder.Append(tagEnd);index += tagEnd.Length - 1; // 跳过整个闭合标签return true;}}return false;}private void UpdateTextDisplay(){targetTextComponent.text = typewriterBuilder.ToString() + GenerateActiveTagsClosure();}private string GenerateActiveTagsClosure(){StringBuilder closure = new StringBuilder();foreach (var tag in activeTags){closure.Append($"</{tag}>");}return closure.ToString();}private void FinalizeTyping(){typingCoroutine = null;onCompleteCallback?.Invoke();}private static string ProcessSpeedTags(string input){return System.Text.RegularExpressions.Regex.Replace(input, @"\[speed=[^\]]+\]", string.Empty, System.Text.RegularExpressions.RegexOptions.IgnoreCase);}}public static class TypewriterExtensions{public static void StartTypewriter(this UnityEngine.UI.Text textComponent, string content, float speed = 0.05f, Action onComplete = null){TypewriterEffect effect = GetOrAddTypewriterComponent(textComponent);effect.StartTyping(content, speed);effect.SetCompleteCallback(onComplete);}public static void SkipTypewriter(this UnityEngine.UI.Text textComponent){if (textComponent.TryGetComponent<TypewriterEffect>(out var effect)) effect.SkipTyping();}public static bool IsTyping(this UnityEngine.UI.Text textComponent){TypewriterEffect effect = textComponent.GetComponent<TypewriterEffect>();return effect != null && effect.IsTyping;}private static TypewriterEffect GetOrAddTypewriterComponent(UnityEngine.UI.Text textComponent){if (!textComponent.TryGetComponent<TypewriterEffect>(out var effect)){effect = textComponent.gameObject.AddComponent<TypewriterEffect>();}return effect;}}
}

效果
测试代码
在这里插入图片描述

http://www.xdnf.cn/news/3612.html

相关文章:

  • (11)Vue-Router路由的详细使用
  • SQL面试题——留存分析之使用bitmap 计算留存
  • 进程与线程:05 内核级线程实现
  • stm32教程:软件I2C通信协议 代码模板提供
  • Linux_su命令
  • 西电雨课堂《知识产权法》课后作业答案
  • 删除电脑中的AlibabaProtect
  • 论软件需求管理
  • LLMs Tokenizer Byte-Pair Encoding(BPE)
  • [ Qt ] | 第一个Qt程序
  • MySQL进阶(一)
  • 密码学_加密
  • 高露洁牙膏是哪个国家的品牌?高露洁牙膏哪一款最好?
  • [预备知识]5. 优化理论(一)
  • MySQL基础关键_004_DQL(三)
  • 【学习笔记】深入理解Java虚拟机学习笔记——第2章 Java内存区域与内存溢出异常
  • C++析构函数详解
  • Socat 用法详解:网络安全中的瑞士军刀
  • 部署Superset BI(一)试水
  • 非对称加密算法(RSA、ECC、SM2)——密码学基础
  • 2025年- H18-Lc126-54.螺旋矩阵(矩阵)---java版
  • 【阿里云大模型高级工程师ACP习题集】2.9 大模型应用生产实践(上篇)
  • Android学习总结之jetpack组件间的联系
  • HTTP和HTTPS
  • IntelliJ IDEA
  • 微信小程序
  • Python集合全解析:从基础到高阶应用实战
  • 信息收集新利器:SSearch Chrome 插件来了
  • 如何理解 MCP 和 A2A 的区别?|AI系统架构科普
  • B站Michale_ee——ESP32_IDF SDK——FreeRTOS_5 事件组同步与等待