效果图如下:





无论是按钮(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. 简要总结 数据仓库VS数据库

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/wl101yjx/article/details/31015367 本文简要总结以下两个问题,旨在高速 ...

  2. 洛谷p1064 金明的预算方法

    有附带条件的01背包 要那附件必须拿主件 因为一个主件最多有两个附件,所以每次遇到主件可能有四种选择 1.只拿主件 2.拿主件和一号附件 3.拿主件和二号附件 4.都拿 #include <cs ...

  3. Linux系统如何禁止普通用户切换root?

    Linux系统如何禁止普通用户切换root? 在上正文之前,我们先将一些基础的Linux用户以及用户组的相关命令: 1.添加用户 useradd [-g group] [-d user_home_di ...

  4. MVC学习十三:RouteDebugger插件应用

    1.下载第三方程序集RouteDebugger https://files.cnblogs.com/files/WarBlog/RouteDebugger.rar 2.把RouteDebugger程序 ...

  5. C/C++ Windows API——获取计算机信息 转

    转自:http://blog.csdn.net/chy555chy/article 函数 头文件 作用 GetVersionEx <windows.h> 获取系统版本信息(deprecat ...

  6. 时钟晶振32.768KHz为什么是15分频?

    实时时钟晶振为什么选择是32768Hz的晶振,在百度上搜索的话大部分的答案都是说2的15次方是32768,使用这个频率的晶振,人们可以很容易的通过分频电路得到1Hz的计时脉冲.但是话有说回来了,2的整 ...

  7. Swift_类型选择

    Swift_类型选择 点击查看源码 //类型选择 func test() { class MediaItem { } class Movie: MediaItem { } class Song: Me ...

  8. iOS之利用runtime,避免可变数组和可变字典为nil或者数组越界导致的崩溃

    NSArray.NSMutableArray.NSDictionary.NSMutableDictionary.是我们的在iOS开发中非常常用的类.当然,在享受这些类的便利的同时,它们也给我们带来一些 ...

  9. ubuntu18.04安装搜狗输入法

    首先,安装Fcitx输入框架 sudo apt install fcitx 其次,上搜狗输入法官网下载Linux版本搜狗输入法(32位和64位根据自己情况,在虚拟机上用浏览器下载即可 然后进入相应的下 ...

  10. Struts2+Spring+Hibernate整合开发(Maven多模块搭建)

    Struts2+Spring+Hibernate整合开发(Maven多模块搭建) 0.项目结构 Struts2:web层 Spring:对象的容器 Hibernate:数据库持久化操作 1.父模块导入 ...