cocos2dx RichText关于字符串截取的bug

cocos2dx 3.3rc版本中 RichText类 当自定义了RichText的宽度, 对于字符串的截取有问题,如果中文和英文数字混合的时候,会导致 要么 一行还有位置 就换行了,要么 显示不全。 看了下RichText实现,

重点是handlerTextRenderer方法

void RichText::handleTextRenderer(const std::string& text,const std::string& fontName,float fontSize,const Color3B &color,GLubyte opacity)
{
    auto fileExist = FileUtils::getInstance()->isFileExist(fontName);
    Label* textRenderer = nullptr;
    if (fileExist)
    {
        textRenderer = Label::createWithTTF(text,fontName,fontSize);
    }else if(FileUtils::getInstance()->isFileExist(Label::getCustomFont())){
		textRenderer = Label::createWithTTF(text,Label::getCustomFont(),fontSize);
	} 
    else
    {
        textRenderer = Label::createWithSystemFont(text,fontSize);
    }

    float textRendererWidth = textRenderer->getContentSize().width;
    _leftSpaceWidth -= textRendererWidth;
    if (_leftSpaceWidth < 0.0f)
    {
        <span style="color:#FF0000;">float overstepPercent = (-_leftSpaceWidth) / textRendererWidth;
        std::string curText = text;
        size_t stringLength = StringUtils::getCharacterCountInUTF8String(text);
        int leftLength = stringLength * (1.0f - overstepPercent);</span>

        std::string leftWords = Helper::getSubStringOfUTF8String(curText,leftLength);
        std::string cutWords = Helper::getSubStringOfUTF8String(curText,leftLength,stringLength - leftLength);

        if (leftLength > 0)
        {
            Label* leftRenderer = nullptr;
            if (fileExist)
            {
                leftRenderer = Label::createWithTTF(Helper::getSubStringOfUTF8String(leftWords,leftLength),fontSize);
            }
	    else if (FileUtils::getInstance()->isFileExist(Label::getCustomFont()))
	    {
		leftRenderer = Label::createWithTTF(Helper::getSubStringOfUTF8String(leftWords,fontSize);
	    }
            else 
            {
                leftRenderer = Label::createWithSystemFont(Helper::getSubStringOfUTF8String(leftWords,fontSize);
            }
            if (leftRenderer)
            {
                leftRenderer->setColor(color);
                leftRenderer->setOpacity(opacity);
                pushToContainer(leftRenderer);
            }
        }

        addNewLine();
        handleTextRenderer(cutWords.c_str(),fontSize,color,opacity);
    }
    else
    {
        textRenderer->setColor(color);
        textRenderer->setOpacity(opacity);
        pushToContainer(textRenderer);

    }
}
关键是标红的那几句代码: 他是用 当前行空余宽度 除以 文字纹理的长度得出的百分比 再乘以字符串长度得出字符个数,除出来的float值 用int 接收,直接丢弃掉小数部分,看似没有问题。实际上, 用百分比乘出来的宽度默认 认为 所有字符的宽度都是一样的, 所以当英文 数字混合的时候 就会导致 还有空余放的下 英文, 它却直接换行了。

还有个问题,就是 int 去接收 float类型的数据 可能导致浮点误差 比如 我 空余宽度除以文字纹理长度得出的百分比是 0.215054,文字字符个数是93, 我摁计算器算出来的都是20.000022, 用int 接收应该是20, 但是这里的出来的结果竟然是19??? 让人莫名其妙。 实际上 我知道 这是 类型运算的转换 可能导致的浮点误差 导致的。但是也没有理解到底问题在哪里。

起初想到的是 Label 指定了Label的宽度的时候,Label纹理宽度超过了指定宽度的时候 它也有自动换行的操作,但是看了Label的实现,觉得Label 写的可读性太差了,很难看懂呀。我看到 Label对 字符位置的更新是在 alignText这个方法里, 有个LabelTextFormatter:createStringSprite 的静态方法。

  LabelTextFormatter::createStringSprites(this);    
    if(_maxLineWidth > 0 && _contentSize.width > _maxLineWidth && LabelTextFormatter::multilineText(this) )      
        LabelTextFormatter::createStringSprites(this);

    if(_labelWidth > 0 || (_currNumLines > 1 && _hAlignment != TextHAlignment::LEFT))
        LabelTextFormatter::alignText(this);
看着就让人好费解, 先调用一次createStringSprites 判断条件 再调用一次。不知道它想干啥?
在createStringSprites中, 将字符串 创建出字符纹理,设置好坐标等, 但是 LabelTextFormatter::multilineText 这个方法看了几遍 愣是不知道它想干啥?


好, 回到 RichText。

其实 关键是要得到 leftLength这个 精确数值, 通过这个数值确定最终截取位置。

为了解决这个问题 看了下Label的实现, 无论是UIText 还是LabelTTF 最终渲染的 都是Label, Label 有 getLetter 这个接口,就可以 通过文字纹理拿到 当前的字符 ,从而拿到 当前字符的位置,然后再做精细调整。

最终结果就是:

 if (_leftSpaceWidth < 0.0f)
 {
        float overstepPercent = (-_leftSpaceWidth) / textRendererWidth;
        std::string curText = text;
        size_t stringLength = StringUtils::getCharacterCountInUTF8String(text);
        int leftLength = stringLength * (1.0f - overstepPercent);

	Sprite *letter =textRenderer->getLetter( leftLength );
	float pos_left_x_  = letter->getPositionX() -  letter->getContentSize().width/2;
	float pos_right_x_ = letter->getPositionX() + letter->getContentSize().width/2;

	while( pos_left_x_ < _leftSpaceWidth+textRendererWidth ) {
		leftLength = leftLength + 1;
		letter =textRenderer->getLetter( leftLength );
		if ( letter == NULL ){
			break;
		}
	        pos_left_x_  = letter->getPositionX() +  letter->getContentSize().width/2;
	}
		
	while( pos_right_x_ > _leftSpaceWidth+textRendererWidth ){
		leftLength = leftLength - 1;
		letter =textRenderer->getLetter( leftLength );
		if ( letter == NULL ){
		    break;
		}
		pos_right_x_ = letter->getPositionX() + letter->getContentSize().width/2;
	}

        std::string leftWords = Helper::getSubStringOfUTF8String(curText,fontSize);
            }
            if (leftRenderer)
            {
                leftRenderer->setColor(color);
                leftRenderer->setOpacity(opacity);
                pushToContainer(leftRenderer);
            }
        }
问题解决。 如果谁有更好的方案,求分享。

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...