效果图如下:





无论是按钮(control button),还是普通的label都有小概率出现这种情况。

该问题发现于cocos2d-x3.7

原因:

在3.x中使用ttfconfig创建的label,为了性能是创建了缓存的,字体纹理缓存的大小默认是512*512(不同版本大小可能不一样)

//CCFontAtlas.cpp
const int FontAtlas::CacheTextureWidth = 512;
const int FontAtlas::CacheTextureHeight = 512;

v3.7的label继承于batchnode:class CC_DLL Label : public SpriteBatchNode, public LabelProtocol,且有一个存放batchnode数组的成员变量std::vector<SpriteBatchNode*> _batchNodes;,这个数组的第一个元素就是cclabel自己,当一张512*512大小的字体纹理缓存不够用时,会再创建一个,此时就是push_back到这个数组中。该batchnode数组会在绘制使用ttfconfig创建的字体时被使用。

那么,问题主要在于当改变字体大小,或者改变字体,都会调用函数void Label::setFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = false */, bool useA8Shader /* = false */),该函数会重置字体的纹理缓存:

void Label::setFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = false */, bool useA8Shader /* = false */)
{
if (atlas == _fontAtlas)
{
FontAtlasCache::releaseFontAtlas(atlas);
return;
} if (_fontAtlas)
{
FontAtlasCache::releaseFontAtlas(_fontAtlas);
_fontAtlas = nullptr;
} _fontAtlas = atlas; if (_textureAtlas)
{
//这里重置了_batchNodes的第一个元素的问题,也就是cclabel自己,
//那么如果之前缓存了多个纹理,即_batchNodes.size>1,
//其他的纹理就还没有被释放!!!
_textureAtlas->setTexture(_fontAtlas->getTexture(0));
}
else
{
SpriteBatchNode::initWithTexture(_fontAtlas->getTexture(0), 30);
} //...
}

再来看一下_batchNodes添加新的缓存纹理相应的代码:

void Label::alignText()
{
if (_fontAtlas == nullptr || _currentUTF16String.empty())
{
setContentSize(Size::ZERO);
return;
} _fontAtlas->prepareLetterDefinitions(_currentUTF16String);
auto& textures = _fontAtlas->getTextures(); //如果上面旧的缓存一直没有释放,
//那么当新的字体纹理缓存创建第二个时,不会被更新!!!
//所以你看到的文字就是错乱的!
if (textures.size() > _batchNodes.size())
{
for (auto index = _batchNodes.size(); index < textures.size(); ++index)
{
auto batchNode = SpriteBatchNode::createWithTexture(textures.at(index));
batchNode->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT);
batchNode->setPosition(Vec2::ZERO);
Node::addChild(batchNode,0,Node::INVALID_TAG);
_batchNodes.push_back(batchNode);
}
}
//.......
}

根据上面的分析,其实可以发现,出现的这个bug的概率是很小的:

  1. 首先得用ttfconfig创建一个label,且显示过大量文字,导致创建了第二个缓存纹理
  2. 然后更换字体,或者更改字体大小(setTTFSize)
  3. 更改后的label显示过大量文字,导致创建第二个缓存纹理,如果显示了第二个纹理上的文字就会是错乱的。

解决方案:

因为更换字体、更改字体大小等,都会更换新的纹理(setFontAtlas),那么我们只用在该函数中重置_batchNodes即可:

void Label::setFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = false */, bool useA8Shader /* = false */)
{
if (atlas == _fontAtlas)
{
FontAtlasCache::releaseFontAtlas(atlas);
return;
} if (_fontAtlas)
{
FontAtlasCache::releaseFontAtlas(_fontAtlas);
_fontAtlas = nullptr;
} _fontAtlas = atlas; if (_textureAtlas)
{
_textureAtlas->setTexture(_fontAtlas->getTexture(0));
}
else
{
SpriteBatchNode::initWithTexture(_fontAtlas->getTexture(0), 30);
} //issue: text disorder
_batchNodes.clear();
_batchNodes.push_back(this); //...
}

v3.10

github上已经有3.10的代码,看了一下cclabel,已经重构了,label不再继承BatchNode

class CC_DLL Label : public Node, public LabelProtocol, public BlendProtocol

而每一个文字使用:class LabelLetter : public Sprite

看了一下setFontAtlas函数,上述的bug已经不存在了

void Label::setFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = false */, bool useA8Shader /* = false */)
{
if (atlas == _fontAtlas)
{
FontAtlasCache::releaseFontAtlas(atlas);
return;
} if (_fontAtlas)
{
_batchNodes.clear(); //这里已经重置
FontAtlasCache::releaseFontAtlas(_fontAtlas);
_fontAtlas = nullptr;
} _fontAtlas = atlas;
//.....
}

最新文章

  1. .Net 大型分布式基础服务架构横向演变概述
  2. C# 利用SQLite对.DB和.logdb加密和解密和SQLite创建数据库
  3. 微信OAuth2.0网页授权
  4. 织梦channelid是什么?dede channel typeid有什么区别
  5. NVPerfHUD
  6. IT综合学习网站收集
  7. OpenJudge/Poj 2027 No Brainer
  8. swift-01-简述swift与OC区别
  9. jquery.validate.unobtrusive.js实现气泡提示mvc错误
  10. Spring 自动装配及自动注册的相关配置
  11. css实现多行文本溢出显示省略号(…)全攻略
  12. 微信中H5网页如何唤醒打开外部浏览器打开指定链接
  13. 南大算法设计与分析课程OJ答案代码(2)最大子序列和问题、所有的逆序对
  14. 运营商挂时长神器,批量导入账号,导出账号状态,随机修改MAC地址
  15. 通过Cookie跳过登录验证码【限cookie不失效有用】
  16. Oracle数据库11gR2的卸载 - deinstall
  17. linux shell 脚本攻略学习6-xargs详解
  18. 命令行修改MySQL数据库密码
  19. VS2017无法启动程序 操作在当前状态中是非法的
  20. NSDate 时间加减

热门文章

  1. PHP 获取数组随意下标key的上一个prev和下一个next下标值
  2. ADF中遍历VO中的行数据(Iterator)
  3. Thread-Specific-Storage for C/C++
  4. 7.spring:SpringAOP(配置文件)
  5. CToolBarCtrl基本内容控件
  6. HDU 1006 Tick and Tick(时钟,分钟,秒钟角度问题)
  7. C/C++判断文件/文件夹是否存在 转
  8. Linux下的压缩解压缩命令
  9. java8的新特性,Collections.sort(排序的List集合)的使用,对list封装Map里面的某个值进行排序
  10. linux ping命令实践