cocos2d-x3.7 cclabel文字破碎,异常,变乱

前端之家收集整理的这篇文章主要介绍了cocos2d-x3.7 cclabel文字破碎,异常,变乱前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

效果图如下:



无论是按钮(control button),还是普通的label都有小概率出现这种情况。
该问题发现于cocos2d-x3.7

原因:

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

  1. //CCFontAtlas.cpp
  2. const int FontAtlas::CacheTextureWidth = 512;
  3. 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 */),该函数会重置字体的纹理缓存:

  1. void Label::setFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = false */,bool useA8Shader /* = false */)
  2. {
  3. if (atlas == _fontAtlas)
  4. {
  5. FontAtlasCache::releaseFontAtlas(atlas);
  6. return;
  7. }
  8.  
  9. if (_fontAtlas)
  10. {
  11. FontAtlasCache::releaseFontAtlas(_fontAtlas);
  12. _fontAtlas = nullptr;
  13. }
  14.  
  15. _fontAtlas = atlas;
  16.  
  17. if (_textureAtlas)
  18. {
  19. //这里重置了_batchNodes的第一个元素的问题,也就是cclabel自己,
  20. //那么如果之前缓存了多个纹理,即_batchNodes.size>1,
  21. //其他的纹理就还没有被释放!!!
  22. _textureAtlas->setTexture(_fontAtlas->getTexture(0));
  23. }
  24. else
  25. {
  26. SpriteBatchNode::initWithTexture(_fontAtlas->getTexture(0),30);
  27. }
  28.  
  29. //...
  30. }

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

  1. void Label::alignText()
  2. {
  3. if (_fontAtlas == nullptr || _currentUTF16String.empty())
  4. {
  5. setContentSize(Size::ZERO);
  6. return;
  7. }
  8.  
  9. _fontAtlas->prepareLetterDefinitions(_currentUTF16String);
  10. auto& textures = _fontAtlas->getTextures();
  11.  
  12. //如果上面旧的缓存一直没有释放,
  13. //那么当新的字体纹理缓存创建第二个时,不会被更新!!!
  14. //所以你看到的文字就是错乱的!
  15. if (textures.size() > _batchNodes.size())
  16. {
  17. for (auto index = _batchNodes.size(); index < textures.size(); ++index)
  18. {
  19. auto batchNode = SpriteBatchNode::createWithTexture(textures.at(index));
  20. batchNode->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT);
  21. batchNode->setPosition(Vec2::ZERO);
  22. Node::addChild(batchNode,0,Node::INVALID_TAG);
  23. _batchNodes.push_back(batchNode);
  24. }
  25. }
  26. //.......
  27. }

根据上面的分析,其实可以发现,出现的这个bug的概率是很小的:
1. 首先得用ttfconfig创建一个label,且显示过大量文字,导致创建了第二个缓存纹理
2. 然后更换字体,或者更改字体大小(setTTFSize)
3. 更改后的label显示过大量文字,导致创建第二个缓存纹理,如果显示了第二个纹理上的文字就会是错乱的。

解决方案:

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

  1. void Label::setFontAtlas(FontAtlas* atlas,bool useA8Shader /* = false */)
  2. {
  3. if (atlas == _fontAtlas)
  4. {
  5. FontAtlasCache::releaseFontAtlas(atlas);
  6. return;
  7. }
  8.  
  9. if (_fontAtlas)
  10. {
  11. FontAtlasCache::releaseFontAtlas(_fontAtlas);
  12. _fontAtlas = nullptr;
  13. }
  14.  
  15. _fontAtlas = atlas;
  16.  
  17. if (_textureAtlas)
  18. {
  19. _textureAtlas->setTexture(_fontAtlas->getTexture(0));
  20. }
  21. else
  22. {
  23. SpriteBatchNode::initWithTexture(_fontAtlas->getTexture(0),30);
  24. }
  25.  
  26. //issue: text disorder
  27. _batchNodes.clear();
  28. _batchNodes.push_back(this);
  29.  
  30. //...
  31. }

v3.10

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

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

而每一个文字使用:class LabelLetter : public Sprite
看了一下setFontAtlas函数,上述的bug已经不存在了

  1. void Label::setFontAtlas(FontAtlas* atlas,bool useA8Shader /* = false */)
  2. {
  3. if (atlas == _fontAtlas)
  4. {
  5. FontAtlasCache::releaseFontAtlas(atlas);
  6. return;
  7. }
  8.  
  9. if (_fontAtlas)
  10. {
  11. _batchNodes.clear(); //这里已经重置
  12. FontAtlasCache::releaseFontAtlas(_fontAtlas);
  13. _fontAtlas = nullptr;
  14. }
  15.  
  16. _fontAtlas = atlas;
  17. //.....
  18. }

猜你在找的Cocos2d-x相关文章