效果图如下:





无论是按钮(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;
//.....
}

cocos2d-x3.7 cclabel文字破碎,异常,变乱的更多相关文章

  1. cocos2d-x在IOS7下面文字显示异常的解决办法 CGBitmapContextCreate: unsupported parameter combination

    首先定位到libs-->cocos2dx-->platform-->iOS-->CCImage.mm 找到这个文件. 打开CCImage.mm文件,定位到如下函数:   [cp ...

  2. [cocos2d] 显示状态与文字

    前言: 对于显示数值与文字一般有三种方式可以使用: CCLabelTTF .CCLabelBMFont .CCLabelAtlas 详细区别可参考:cocos2d 中添加显示文字的三种方式(CCLab ...

  3. cocos2d 中加入显示文字的三种方式(CCLabelTTF 、CCLabelBMFont 和CCLabelAtlas)

    在 cocos2d 中有三个类能够在层或精灵中加入文字: CCLabelTTF CCLabelBMFont CCLabelAtlas      CCLabelTTF CCLabelTTF 每次调用 s ...

  4. 完美解决移动Web小于12px文字居中的问题

    前几天的一篇博文:移动Web单行文字垂直居中的问题,提到了移动web里小于12px的文字居中异常的问题,最后还是改为12px才近乎解决了问题.但是有时候或许并不是那么乐观,你并不能将原本定为10px的 ...

  5. Canvas 笔记(持续更新中)

    1.从线条开始 HTML <canvas id="canvas"></canvas> Javascript var canvas=document.getE ...

  6. cocos2d-x学习知识点记录

    环境搭建 http://4137613.blog.51cto.com/4127613/751149 Cocos2d-x初探,HelloWorld解读 http://www.cnblogs.com/Ke ...

  7. pytest自动化7:assert断言

    前言:assert断言就是将实际结果和期望结果做对比,符合预期结果就测试pass,不符合预期就测试failed. 实例1:简单断言 实例1优化版--增加异常信息文字描述 异常断言 excinfo 是一 ...

  8. 移动端网页使用flexible.js加入百度联盟广告样式不一致问题解决

    flexible.js是淘宝推出的一款移动端手机自适应的库,源码内容很简洁,当网页使用了该库之后,页面会在head中加入对应的页面响应式的meta标签. 当使用flexible.js的时候,引入百度联 ...

  9. Android自定义控件练手——简单的时钟

    首先这应该是一个老生常谈的设计了,但是毕竟身为小白的自己都没动手做过,不动手怎么提高自己呢,所以在这梅林沉船闲暇之际,我就把我的设计流程与思路记录下来.首先来看看效果图吧: 如上图就是一个简单并没有美 ...

随机推荐

  1. CF839D Winter is here

    题目分析 显然我们不可能直接计算每一个子序列的贡献,而应该计算对于每一个gcd对答案的贡献. 考虑容斥.按照套路: 设\(q(i)\)表示序列\(gcd\)为\(i\)的倍数的序列长度和. 设\(g( ...

  2. SOJ1029 Humble Numbers (枚举)

    A number whose only prime factors are 2,3,5 or 7 is called a humble number. The sequence 1, 2, 3, 4, ...

  3. Yii自定义验证规则

    简单的方法:在 model 内部定义规则 最简单的定义验证规则的方法是在使用它的模型(model)内部定义. 比方说,你要检查用户的密码是否足够安全. 通常情况下你会使用 CRegularExpres ...

  4. 【[SCOI2010]股票交易】

    感谢dzm,尽管接受了\(The \text{ }\text{ }king\text{ } \text{ }of\text{ } \text{ }SD\)的指点但我还是不会 至少方程还是比较好推的 状 ...

  5. 自学安卓练习作品单词APP(1)-安卓的hello word与有道字典防爬虫破解

    1.前言 闲来无聊.手机每天又是都接触的东西.程序什么的最容易接触到.想到有些人说前后端都做就是全栈的说法.哦,你看html5全栈. 要我说多接触一些多有意思.天天写后端.还不是业务层.又不是什么高大 ...

  6. java 编写小工具 尝试 学习(五)

    1.今天 学习 标签 的 控件 的使用 ,学习 视频教程 参考  :http://edu.51cto.com/lesson/id-17733.html 常用控件如下截图: import javax.s ...

  7. angular1的复选框指令--checklistModel

    这个指令可以改变一组checkbox的model格式,提交的时候格式为[x,y,z,...] //复选框指令 .directive('checklistModel', ['$parse', '$com ...

  8. Oracle创建聚簇表

    创建聚簇表过程: 创建簇(cluster)----创建簇表(基本类似创建一般表但有区别)----创建簇索引(index)----数据管理 创建簇: create cluster stu_ach(sid ...

  9. Oracle 触发器(二)

    Oracle触发器详解   触发器是许多关系数据库系统都提供的一项技术.在oracle系统里,触发器类似过程和函数,都有声明,执行和异常处理过程的PL/SQL块. 8.1 触发器类型 触发器在数据库里 ...

  10. Mybatis Mapper动态代理方式 typeAliases 别名的使用

    目录结构及配置文件与原始dao方法相比更简便 只需一个UserMapper的接口,放在一起的配置文件,配置文件中namespace的地址确定jdk动态代理的对象 <?xml version=&q ...