Skip to content

CV 风格 FNT 字体优化分析报告

整体统计

指标数值
.fnt 文件总数4,060 个
唯一字体名称2,261 个
.fnt 元数据大小33 MB
字体纹理总大小186 MB

目录分布

目录文件数占比
activity/1,92347.4%
casino/2566.3%
dynamic/2385.9%
common/1463.6%
slot/1353.3%
card_system_lagload/551.4%
其他(关卡等)~1,30732.2%

重复最多的字体文件 TOP15

字体文件名重复次数用途
140_3.fnt81编号字体
jackpot_fever_48_green.fnt55彩票活动
jackpot_fever_48_blue.fnt46彩票活动
jackpot_fever_48.fnt43彩票活动
jackpot_fever_discount_60.fnt33折扣展示
hr_wheel_white_34.fnt31高额游戏
bingo_fever_tip_white_48.fnt22Bingo 提示
journey_info3_w_24.fnt19Journey 活动
store_coin_yellow_60.fnt18商店金币
new_spinui_win_44.fnt18旋转 UI
new_spinui_win_32.fnt18旋转 UI
store_coin_yellow_120.fnt17商店金币
road_mission_info_28.fnt17Road 活动
title_107_2.fnt15标题字体
activity_center_white_24.fnt15活动中心

活动目录字体分布 TOP15

活动名字体数
bingo_island42
vegas_pass_treasure33
magical_journey_lb33
snake_ladders_202530
magical_journey_id429
magical_journey_hw29
system_casino_challenge28
vegas_quest_first_quest27
santa_s_retreat25
paddy_s_wonderland_202525
club_bingo25
casino_challenge25
9th_brithday_fiesta25
10th_birthday_fiesta25
club_pinata_game24

文件大小分布

大小范围数量占比
2-5 KB2,52262.1%
5-10 KB2395.9%
> 10 KB1,29932.0%

最大纹理文件 TOP10

文件大小位置
mysterious_gift_wild_font_anim.png1.2 MB关卡特效
reward_video_150.png916 KBcommon/fonts
168_tanban_num1.png908 KB关卡字体
brilliant_diamonds_symbol_bonus_spins_font_3d.png740 KB关卡特效
haunted_treats_symbol_wild_10x_trigger_3d_font.png652 KB关卡特效
glitzy_hits_symbols_jackpot_font_3d_sweep.png612 KB关卡特效
1_8.png580 KBjackpot_clover
brilliant_diamonds_symbol_500x_font_jackpot_win_3d.png548 KB关卡特效
haunted_treats_symbol_batch_bonus_font_3d.png536 KB关卡特效
brilliant_diamonds_symbol_bonus_free_font_3d.png516 KB关卡特效

common/fonts 公共字体(75 个)

常用字体示例:

字体文件大小用途
button_white_30.fnt15.6 KB按钮文字
dialog_title_40.fnt12 KB对话框标题
gold_font_30pt.fnt12 KB金色数字
gold_neuron_90.fnt19.7 KB金色标题
white_one.fnt-白色通用
bigwin_160.fnt2.8 KB大赢展示

关键发现

  1. 活动目录占比近半 - 1,923 个字体在 activity/ 目录,占 47.4%
  2. 重复严重 - 同一字体文件(如 140_3.fnt)在 81 个不同位置存在副本
  3. 大纹理集中在关卡 - 超过 500KB 的纹理主要用于关卡 3D 特效字体
  4. 大文件占比高 - 32% 的 fnt 文件超过 10KB

引擎现有优化机制

1. 字体配置缓存(FNTConfig)

位置: frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFont.js

机制说明
解析器cc._fntLoader 负责解析 .fnt 文件
缓存位置cc.loader.cache[fntFile] 全局缓存
缓存内容commonHeight、atlasName、fontDefDictionary、kerningDict
javascript
// 初始化时从缓存获取
var newConf = cc.loader.getRes(fntFile);
if (!newConf) {
    var txt = cc.loader._loadTxtSync(fntFile);
    newConf = cc._fntLoader.parseFnt(txt, fntFile);
    cc.loader.cache[fntFile] = newConf;  // 缓存配置
}

2. 纹理缓存机制

位置: frameworks/cocos2d-html5/cocos2d/core/textures/CCTextureCache.js

机制说明
全局单例cc.textureCache
缓存结构_textures 对象(key = 路径,value = Texture2D)
自动关联字体纹理通过 cc.textureCache.addImage() 自动加载
javascript
// 字体初始化时加载纹理
texture = cc.textureCache.addImage(newConf.atlasName);

3. Sprite 复用机制

机制说明
复用策略使用 tag(字符索引)管理 Sprite
更新逻辑复用已存在的 Sprite,仅更新纹理区域
javascript
var fontChar = self.getChildByTag(i);  // 尝试获取已存在的 Sprite
if (!fontChar) {
    fontChar = new cc.Sprite();  // 不存在才创建
    this.addChild(fontChar, 0, i);
} else {
    cmd._updateCharTexture(fontChar, rect, key);  // 复用
}

字体加载流程

text
阶段 1: .fnt 文件加载与解析
┌─────────────────────────────────────────────────────────────┐
│ new cc.LabelBMFont(str, fntFile)                            │
│     ↓                                                       │
│ cc.loader.getRes(fntFile)  ← 检查缓存                        │
│     ↓ [未缓存]                                               │
│ cc._fntLoader.load(fntFile)  ← 异步加载                      │
│     ↓                                                       │
│ cc._fntLoader.parseFnt(txt)  ← 解析 .fnt 格式                │
│     ↓                                                       │
│ cc.loader.cache[fntFile] = config  ← 缓存配置对象            │
└─────────────────────────────────────────────────────────────┘

阶段 2: 纹理图集加载
┌─────────────────────────────────────────────────────────────┐
│ cc.textureCache.addImage(config.atlasName)                  │
│     ↓                                                       │
│ 检查 _textures[atlasName]                                   │
│     ↓ [未缓存]                                               │
│ cc.loader.loadImg(atlasName)  ← 加载 PNG                     │
│     ↓                                                       │
│ new cc.Texture2D() + initWithElement()                      │
│     ↓                                                       │
│ _textures[atlasName] = texture  ← 缓存纹理                   │
└─────────────────────────────────────────────────────────────┘

阶段 3: 字符 Sprite 创建
┌─────────────────────────────────────────────────────────────┐
│ createFontChars()                                           │
│     ↓                                                       │
│ 遍历字符串每个字符                                            │
│     ↓                                                       │
│ 从 fontDefDictionary 获取字符矩形区域                         │
│     ↓                                                       │
│ new cc.Sprite() + initWithTexture(texture, rect)  ← 共享纹理 │
│     ↓                                                       │
│ addChild(sprite, 0, charIndex)                              │
└─────────────────────────────────────────────────────────────┘

关键点

  1. .fnt 配置只解析一次,存储在 cc.loader.cache
  2. 纹理全局共享,所有使用相同字体的 Label 共享同一纹理对象
  3. 延迟加载,纹理异步加载,Label 监听 "load" 事件后再创建字符

字体释放机制

引擎提供的释放方法

方法说明
cc.textureCache.removeTexture(texture)移除单个纹理
cc.textureCache.removeTextureForKey(key)按路径移除纹理
cc.textureCache.removeAllTextures()清空所有纹理
texture.releaseTexture()释放纹理(调用 WebGL deleteTexture)

自动释放时机

场景行为
配置缓存cc.loader.cache[fntFile] 永不自动释放
纹理缓存cc.textureCache._textures 永不自动释放
Label 销毁removeFromParent() 仅清理子节点 Sprite,不释放纹理

项目资源管理现状

维度现状风险
活动资源清理无统一机制🔴 高
字体引用计数🔴 高
配置内存永久缓存🟡 中
纹理内存永久缓存🔴 高

核心问题:引擎提供了纹理缓存机制,但项目层面缺少字体资源的生命周期管理,活动结束后资源未释放。


运行时性能影响分析

BMFont 引擎实现原理

cc.LabelBMFont 继承自 cc.SpriteBatchNode,每个字符作为独立的 cc.Sprite 子节点。

text
cc.LabelBMFont
  └─ extends cc.SpriteBatchNode
      └─ 每个字符 = 1 个 cc.Sprite 子节点

性能影响因素

1. Draw Call 问题

场景Draw Call 数量说明
同一字体的多个 Label1 次SpriteBatchNode 自动合批
不同字体的多个 LabelN 次每个字体纹理 = 1 次 Draw Call
同屏 10 种字体10+ 次纹理切换导致批次打断

关键结论:字体文件数量多 → 纹理种类多 → Draw Call 增加

2. 内存占用

类型占用方式影响
纹理内存 (GPU)每个 .fnt 对应的 .png 加载到显存186 MB 纹理 = 186 MB 显存上限
配置内存 (CPU).fnt 文件解析后的字符映射表每个字体 ~10-50 KB
Sprite 对象每个字符创建 1 个 Sprite100 字文本 = 100 个对象

3. 字符串更新开销

javascript
// setString() 会触发 createFontChars()
// 每次更新文本 = 销毁旧 Sprite + 创建新 Sprite
label.setString("新文本");  // 触发完整重建

高频更新场景(如计时器、积分)会产生大量 GC 压力。

4. 纹理切换代价

text
渲染顺序:Label A (字体1) → Label B (字体2) → Label C (字体1)
Draw Call:    1              2              3 (无法合并回字体1)

即使使用相同字体,如果渲染顺序被其他字体打断,也无法合批。

性能问题严重度评估

问题严重度影响范围
纹理种类过多导致 Draw Call 高🔴 高全局渲染性能
大纹理加载延迟🟡 中首次显示卡顿
频繁 setString 的 GC 压力🟡 中动态文本场景
配置内存常驻🟢 低内存占用

优化建议

优先级 1:资源去重(高收益)

1.1 重建全局共享字体库

  • 目标:消除跨活动重复
  • 预期收益:减少 30-40% 活动字体文件

1.2 字体尺寸精简

当前问题:存在过多尺寸变体(24, 28, 30, 32, 34, 40, 44, 48, 50, 60, 80, 100, 120, 150, 160pt)

建议保留

  • 小号:30pt(按钮、提示)
  • 中号:48pt(正文、标签)
  • 大号:80pt(标题、奖励)
  • 特大:120pt(大赢展示)

预期收益:减少 30-40% 字体变体

优先级 2:资源生命周期管理(中等收益)

2.1 活动资源清理机制

目标:活动结束时释放专属字体资源

javascript
// 在 BaseActivity.prototype.onActivityEnd 中添加
cleanupActivityFonts: function() {
    var themeName = this.getThemeName();
    var pattern = "activity/" + themeName + "/";
    var textures = cc.textureCache._textures;

    for (var key in textures) {
        if (key.indexOf(pattern) !== -1) {
            cc.textureCache.removeTextureForKey(key);
        }
    }
};

2.2 字体资源池(引用计数)

目标:避免误删仍在使用的共享字体

javascript
var FontResourcePool = {
    _refCount: {},

    retainFont: function(fntFile) {
        this._refCount[fntFile] = (this._refCount[fntFile] || 0) + 1;
    },

    releaseFont: function(fntFile) {
        if (--this._refCount[fntFile] <= 0) {
            var config = cc.loader.getRes(fntFile);
            if (config && config.atlasName) {
                cc.textureCache.removeTextureForKey(config.atlasName);
            }
            delete cc.loader.cache[fntFile];
        }
    }
};

2.3 清理未使用字体

  • 生成完整的字体使用报告
  • 识别零引用的字体文件
  • 清理未使用字体

2.4 大纹理优化

  • 评估 1MB+ 的特效字体是否必要
  • 考虑使用更小的尺寸或压缩

优先级 3:合批优化(运行时性能)

3.1 字体纹理合并(Atlas 合并)

目标:将多个小字体纹理合并为单一 Atlas,减少纹理切换

text
合并前:
  white_30.png (64KB) → 1 Draw Call
  white_48.png (80KB) → 1 Draw Call
  gold_30.png  (70KB) → 1 Draw Call
  共 3 次 Draw Call

合并后:
  common_fonts_atlas.png (200KB) → 1 Draw Call
  共 1 次 Draw Call

实施步骤

  1. 使用 TexturePacker 合并常用字体纹理
  2. 更新 .fnt 文件中的纹理坐标映射
  3. 修改字体加载路径

预期收益:减少 50-70% 字体相关 Draw Call

3.2 渲染顺序优化

原则:将使用相同字体的 Label 在渲染树中相邻排列

text
优化前:
  Layer
    ├─ Label A (font_white)
    ├─ Sprite X
    ├─ Label B (font_gold)
    ├─ Sprite Y
    └─ Label C (font_white)  // 无法与 A 合批

优化后:
  Layer
    ├─ Label A (font_white)
    ├─ Label C (font_white)  // 可与 A 合批
    ├─ Sprite X
    ├─ Sprite Y
    └─ Label B (font_gold)

3.3 字体复用策略

统一常用字体

用途推荐字体替代
白色文本white_48.fnt替代 white_30/32/34/40/44
金色数字gold_font_48pt.fnt替代 gold_30/40/54/60/64
按钮文字button_white_30.fnt统一使用

收益:减少同屏字体种类 → 减少 Draw Call

3.4 动态文本优化

问题:频繁 setString() 导致 Sprite 重建

方案

  • 使用对象池复用 Sprite 对象
  • 避免每帧更新文本,使用节流

预期收益汇总

维度当前优化后节省
文件数量4,060~2,50038%
存储空间219 MB140 MB36%
下载体积--20-30%
Draw Call--50-70%

关键文件参考

类型文件路径
引擎 BMFont 实现frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFont.js
引擎纹理缓存frameworks/cocos2d-html5/cocos2d/core/textures/CCTextureCache.js
引擎批量渲染frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteBatchNode.js
字体加载器frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFont.js (cc._fntLoader)
项目资源管理src/common/model/ResourceMan.js
活动基类src/task/entity/BaseActivity.js
公共字体目录res_oldvegas/common/fonts/
活动字体目录res_oldvegas/activity/*/fonts/

生成日期: 2025-11-27 数据来源: res_oldvegas 目录分析

Released under the MIT License.