深度解析:TextRenderManager——Cocos Creator艺术字体渲染核心类

一、类概述

TextRenderManager 是 Cocos Creator 中实现动态艺术字体渲染的核心单例类。它通过整合资源加载、缓存管理、异步队列和自动布局等功能,支持普通字符模式图集模式两种渲染方案,适用于游戏中的动态文本(如聊天内容、排行榜)和静态艺术字(如UI标题)的高效渲染。

二、核心功能与设计理念

1. 核心功能

  • 资源预加载:分批次异步加载字符纹理或图集,避免主线程阻塞。

  • 动态缓存管理:通过LRUCache实现字符纹理的智能缓存与淘汰。

  • 自动布局排版:支持水平对齐(左/中/右)、垂直对齐(上/中/下)及字符间距调整。

  • 多模式渲染

    • 普通模式:从独立文件加载每个字符的纹理。

    • 图集模式:从预生成的图集(如BMFont输出)中提取字符。

2. 设计目标

  • 高性能:通过异步加载、LRU缓存减少资源重复请求。

  • 易扩展:模块化设计,支持自定义布局策略与渲染模式。

  • 内存安全:动态释放无用资源,防止内存泄漏。

三、代码结构与核心方法详解

1. 私有属性与缓存机制

// 字符纹理缓存(LRU策略)
private _spriteCache = new LRUCache<string, SpriteFrame>(100);
// 加载队列(防止重复请求)
private _loadingQueue = new Map<string, Promise<void>>();
// 渲染请求队列(异步任务调度)
private _requestQueue: Array<{ text: string, config: TextRenderConfig, resolve: Function }> = [];
// 占位符纹理(加载失败时使用)
private _placeholderCache: SpriteFrame | null = null;

2. 核心方法解析

(1) 文本渲染入口:renderText()
/*** 渲染文本* @param text 文本内容* @param config 渲染配置* @returns 包含所有字符节点的容器*/
public async renderText(text: string, config: TextRenderConfig = {}): Promise<Node> {return new Promise((resolve) => {// 将请求加入队列,触发异步处理this._requestQueue.push({ text, config, resolve });this._processQueue();});
}
  • 流程

    1. 将渲染请求推入队列,确保异步任务按顺序执行。

    2. 调用_processQueue()处理队列中的任务。


(2) 队列处理器:_processQueue()
/** 处理渲染队列(单线程异步执行) */
private async _processQueue() {if (this._isProcessing || this._requestQueue.length === 0) return;this._isProcessing = true;const { text, config, resolve } = this._requestQueue.shift()!;try {const container = await this._doRender(text, config);resolve(container);} finally {this._isProcessing = false;this._processQueue(); // 递归处理下一个任务}
}
  • 关键点

    • 使用_isProcessing状态锁防止并发处理。

    • 通过递归调用实现队列的持续消费。


(3) 实际渲染逻辑:_doRender()
/** 执行实际渲染逻辑 */
private async _doRender(text: string, config: TextRenderConfig): Promise<Node> {const container = new Node('TextContainer');this._applyConfig(config, container); // 应用配置(位置、父节点等)// 预加载资源(字符或图集)try {if (config.useAtlas) {await this._loadAtlas(config);} else {await this._preloadCharacters(text, config);}} finally {if (config.preRender) {container.destroy(); // 预渲染模式直接销毁容器return;}}// 创建字符节点并布局const nodes = await this._createCharacterNodes(text, config);this._layoutNodes(nodes, config);// 设置字符缩放并添加到容器nodes.forEach(node => {container.addChild(node);if (config.fontSize) {node.setScale(new Vec3(config.fontSize / 100, config.fontSize / 100, 1));}});return container;
}
  • 流程

    1. 配置初始化:设置容器位置、父节点等基础属性。

    2. 资源预加载:根据配置选择加载图集或独立字符。

    3. 字符节点创建:生成所有字符的Sprite节点。

    4. 布局计算:根据对齐方式排列字符位置。


(4) 字符预加载:_preloadCharacters()
/** 预加载字符资源(分批次加载) */
private async _preloadCharacters(text: string, config: TextRenderConfig) {const uniqueChars = [...new Set(text.split(''))];// 分批次加载(每批5个字符)for (let i = 0; i < uniqueChars.length; i += 5) {const batch = uniqueChars.slice(i, i + 5);await Promise.all(batch.map(char => this._loadWithCacheControl(char, config)));}
}
  • 优化策略

    • 分批加载减少瞬时资源请求压力。

    • 使用_loadWithCacheControl结合LRU缓存管理。


(5) 布局计算:_layoutNodes()
/** 自动布局字符节点 */
private _layoutNodes(nodes: Node[], config: TextRenderConfig) {const firstNode = nodes[0]?.getComponent(Sprite);if (!firstNode?.spriteFrame) return;// 计算总宽度和基础高度const baseHeight = firstNode.spriteFrame.rect.height;const totalWidth = nodes.reduce((sum, node) => sum + node.getComponent(Sprite)!.spriteFrame.rect.width, 0) + (nodes.length - 1) * (config.spacing || 0);// 计算起始位置let xPos = this._calculateXPosition(totalWidth, config.alignment);const yPos = this._calculateYPosition(baseHeight, config.verticalAlign);// 排列节点nodes.forEach(node => {const width = node.getComponent(Sprite)!.spriteFrame.rect.width;node.setPosition(xPos, yPos, 0);xPos += width + (config.spacing || 0);});
}
  • 布局逻辑

    • 水平对齐:根据alignment计算起始X坐标。

    • 垂直对齐:根据verticalAlign计算起始Y坐标。

    • 动态间距:累加每个字符的宽度与间距,实现自动换行(如需)。


(6) 占位符生成:_getPlaceholder()
/** 生成透明占位符纹理 */
private _getPlaceholder(): SpriteFrame {if (!this._placeholderCache) {const frame = new SpriteFrame();const charWidth = 30, charHeight = 30; // 与实际字符尺寸一致const texture = new Texture2D();texture.create(charWidth, charHeight, Texture2D.PixelFormat.RGBA8888);const data = new Uint8Array(charWidth * charHeight * 4).fill(150); // 半透明灰色texture.uploadData(data);frame.rect = new Rect(0, 0, charWidth, charHeight);frame.texture = texture;this._placeholderCache = frame;}return this._placeholderCache;
}
  • 作用

    • 在字符加载失败时提供占位显示,避免UI错乱。

    • 使用透明纹理减少视觉干扰。


3. 关键配置参数:TextRenderConfig

/** 生成透明占位符纹理 */
private _getPlaceholder(): SpriteFrame {if (!this._placeholderCache) {const frame = new SpriteFrame();const charWidth = 30, charHeight = 30; // 与实际字符尺寸一致const texture = new Texture2D();texture.create(charWidth, charHeight, Texture2D.PixelFormat.RGBA8888);const data = new Uint8Array(charWidth * charHeight * 4).fill(150); // 半透明灰色texture.uploadData(data);frame.rect = new Rect(0, 0, charWidth, charHeight);frame.texture = texture;this._placeholderCache = frame;}return this._placeholderCache;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78839.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【漫话机器学习系列】229.特征缩放对梯度下降的影响(The Effect Of Feature Scaling Gradient Descent)

特征缩放对梯度下降的影响&#xff1a;为什么特征标准化如此重要&#xff1f; 在机器学习和深度学习中&#xff0c;梯度下降是最常用的优化算法之一。然而&#xff0c;很多人在训练模型时会遇到收敛速度慢、训练不稳定的问题&#xff0c;其中一个重要原因就是特征未进行适当的…

【神经网络与深度学习】批标准化(Batch Normalization)和层标准化(Layer Normalization)

引言 在深度学习中&#xff0c;标准化技术&#xff08;Normalization&#xff09;是提高神经网络训练效率和性能的重要工具。其中&#xff0c;批标准化&#xff08;Batch Normalization, BN&#xff09;和层标准化&#xff08;Layer Normalization, LN&#xff09;是两种常用的…

OpenHarmony之电源管理子系统公共事件定义

OpenHarmony之电源管理子系统公共事件定义 电源管理子系统面向应用发布如下系统公共事件&#xff0c;应用如需订阅系统公共事件&#xff0c;请参考公共事件接口文档。 COMMON_EVENT_BATTERY_CHANGED 表示电池充电状态、电平和其他信息发生变化的公共事件的动作。 值&#x…

linux 环境下 c++ 程序打印 core dump 信息

linux 信号机制 软中断信号 Signal&#xff0c;简称信号&#xff0c;用来通知进程发生了异步事件&#xff0c;进程之间可以互相通过系统调用 kill 等函数来发送软中断信号。内核也可以因为内部事件而给进程发送信号&#xff0c;通知进程发生了某个事件。 进程对信号的处理 进…

Qt开发环境的安装与问题的解决(2)

文章目录 1. Qt开发环境安装的说明2. 通过安装包进行安装3. 通过在线下载程序 解决问题下载 https....网路错误问题解决开始安装--第一部分开始安装--第二部分 4. 建议配置环境变量&#xff08;非必须&#xff09;配置环境变量的意义 简介&#xff1a;这篇文章主要分享Qt开发环…

【每日EDA行业分析】2025年4月25日

深度总结&#xff1a;EDA 软件行业现状与发展趋势 一、引言 在半导体产业的复杂生态中&#xff0c;EDA 软件宛如一颗闪耀的明珠&#xff0c;它是集成电路设计的核心工具&#xff0c;贯穿芯片从设计构思到最终封装测试的全流程&#xff0c;其重要性不言而喻&#xff0c;被誉为…

flutter实践:比例对比线图实现

需求&#xff1a;flutter实现一个左右对比线图,带有动画效果 效果图&#xff1a; Widget _buildTop() {return Container(height: themeData.heightXl,padding: EdgeInsets.symmetric(horizontal: themeData.hSpacingMd),child: Row(mainAxisAlignment: MainAxisAlignment.spa…

测试基础笔记第十五天

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、集合1.集合的定义二、使用集合列表去重 导包二、函数1.函数介绍2.定义函数3.调用函数4.函数实现登录案例5.函数的返回值 三、模块和包1.模块的概念(Module)2.模…

Linux中的shell脚本练习

1.判断字符串是否为空 #!/usr/bin/bash while : #:默认值为真 do read -p "请输入你的密码: " a pass123456 if [ -z $a ];thenecho "您输入的密码不能为空"exit 1 elseif [ $a $pass ];thenecho "登录成功"breakelseecho "您的密码输入有…

使用命令关闭Redis服务端

使用命令关闭Redis服务端。 命令 redis-cli -a 111111 -p 6379 shutdown 有些人redis的端口不是6379&#xff0c;那就自己查一下 参数解释&#xff1a; -a&#xff1a;Redis密码 -p&#xff1a;Redis端口 shutdown&#xff1a;关闭命令

嵌入式RTOS实战:uC/OS-III最新版移植指南(附项目源码)

文章目录 前言一、uC/OS简介二、工程移植2.1 下载ucos源码2.2 创建空白工程2.3 拷贝ucosiii源码文件2.3.1 UC-CONFIG2.3.2 UC-CPU2.3.3 UC-LIB2.3.4 UC-OS3 2.3 添加工程文件分组及路径2.4 代码首次编译2.5 源码修改2.5.1 cpu_cfg.h2.5.2 os_cpu_c.c2.5.3 lib_cfg.h2.5.4 sys.h…

TypeScript中的函数类型定义与类型约束

函数类型定义与类型约束 一、核心概念&#xff1a;类型别名与函数类型 1. 类型别名&#xff08;Type Alias&#xff09; 定义 类型别名使用 type 关键字为现有类型创建一个新名称&#xff0c;可以用于&#xff1a; 基础类型&#xff08;如 string、number&#xff09;&…

相机DreamCamera2录像模式适配尺寸

在开发中遇到 一个问题&#xff0c;相机切换视频模式时&#xff0c;预览时&#xff0c;界面不能充满屏幕两侧有黑边&#xff0c;客户要求修改&#xff0c;在此记录 一问题现象&#xff1a; 系统相机在视频模式下预览时如下现象如图1&#xff0c;期望现象如图2: 图1 …

SpringCloud组件——Gateway

一.网关 1.问题提出 我们通过Eureka&#xff0c;Nacos解决了服务注册&#xff0c;服务发现的问题&#xff0c;使用SpringCloud LoadBalance解决了负载均衡的问题&#xff0c;使用OpenFeign解决了远程调用的问题。 但是当前所有微服务的接口都是直接对外暴露的&#xff0c;可…

C#中构造器及属性的加载顺序

一.基本原则: 先加载静态构造函数和静态字段,后加载普通构造函数和普通字段;先加载基类再加载子类; 二.具体的加载顺序: 父类静态字段--->父类静态构造函数--->子类静态字段--->子类静态构造函数--->父类实例字段---> 父类实例构造函数--->子类实例字段-…

Python面试问题

一、Python 基础 1. Python 的特点 动态类型&#xff1a;变量无需声明类型。解释型语言&#xff1a;逐行解释执行。支持多种编程范式&#xff08;面向对象、函数式、过程式&#xff09;。 2. 列表&#xff08;List&#xff09;与元组&#xff08;Tuple&#xff09;的区别 特…

计算机视觉进化论:YOLOv12、YOLOv11与Darknet系YOLOv7的微调实战对比

摘要 YOLO系列作为实时目标检测领域的重要里程碑&#xff0c;持续引领速度与精度的平衡发展。本文围绕YOLOv7&#xff08;基于Darknet框架&#xff09;、YOLOv11及YOLOv12&#xff0c;系统、深入地对比了三款模型的架构创新、微调策略、核心技术及应用场景。我们详细解析了三者…

SQL Server 存储过程开发规范

SQL Server 存储过程开发规范&#xff08;高级版&#xff09; 1. 总则 1.1 目标 本规范旨在&#xff1a; 提高存储过程的事务一致性、异常可追踪性、错误透明度。 统一日志记录、错误码管理、链路追踪&#xff08;Trace ID&#xff09;。 支持复杂事务场景&#xff08;嵌套…

opendds的配置

配置的使用 文档中说明有4种使用配置的方式&#xff1a; 环境变量 命令行参数&#xff08;将覆盖环境变量中的配置&#xff09; 配置文件&#xff08;不会覆盖环境变量或命令行参数中的配置&#xff09; 用户调用的 API&#xff08;将覆盖现有配置&#xff09; 这里对开发…

(Go Gin)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收

1. 路由 gin 框架中采用的路优酷是基于httprouter做的 HttpRouter 是一个高性能的 HTTP 请求路由器&#xff0c;适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用&#xff0c;特别适合需要高性能和简单路由的应用场景。 主要特点 显式匹配&#xff1a;与其他路由…