zoukankan      html  css  js  c++  java
  • cocos2d-x3.7 cclabel文字破碎,异常,变乱

    效果图如下:



    无论是按钮(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;
        //.....
    }
    
  • 相关阅读:
    什么事数据对象以及属性分为什么类型?
    Oracle数据库安装
    红黑树和B树
    Java多线程02
    HashMap分析
    java虚拟机——垃圾回收与内存分配
    Java虚拟机——对象
    Java虚拟机——内存
    java数据结构03
    LeetCode刷题
  • 原文地址:https://www.cnblogs.com/songcf/p/5101713.html
Copyright © 2011-2022 走看看