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

一、类概述

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

二、核心功能与设计理念

1. 核心功能

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

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

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

  • 多模式渲染

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

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

2. 设计目标

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

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

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

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

1. 私有属性与缓存机制

// 字符纹理缓存(LRU策略)
private _spriteCache = new LRUCache(100);
// 加载队列(防止重复请求)
private _loadingQueue = new Map>();
// 渲染请求队列(异步任务调度)
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 {
  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 {
  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;
}

你可能感兴趣的:(cocos2d,游戏引擎,javascript)