Cocos2dx 中文支持,文字自动换行,逐字符显示
依然在《三勇者传说》开发过程中,我希望出现类似于日式AVG游戏的对话面板,此时我不得不实现两种功能: 1:文字在一个聊天框内能够自适应的换行显示。 2:大段的文字逐字符的进行显示。 于是我进行了简单的封装。
首先我们来看中文字符的支持:
FKCommon.h
#include <iostream>
#include <cstring>
#pragma comment( lib, "libiconv.lib" )
#include "iconv\iconv.h"
//--------------------------------------------------------------------
namespace FKStringConvert
{
int code_convert( const char* from_charset, const char* to_charset,
const char* inbuf, size_t inlen, char* outbuf, size_t outlen );
/* UTF8 - GB2312 */
std::string u2a( const char* inbuf );
/* GB2312 - UTF8 */
std::string a2u( const char* inbuf );
}
//--------------------------------------------------------------------
FKCommon.cpp
//--------------------------------------------------------------------
namespace FKStringConvert
{
//--------------------------------------------------------------------
int code_convert( const char* from_charset, const char* to_charset, const char* inbuf, size_t inlen, char* outbuf, size_t outlen )
{
iconv_t cd;
const char* temp = inbuf;
const char** pin = &temp;
char** pout = &outbuf;
memset( outbuf, 0, outlen );
cd = iconv_open( to_charset, from_charset );
if( cd == 0 ) return -1;
if( iconv( cd, pin, &inlen, pout, &outlen ) == -1 )
{
return -1;
}
iconv_close( cd );
return 0;
}
//--------------------------------------------------------------------
/* UTF8 -> GB2312 */
std::string u2a( const char* inbuf )
{
size_t inlen = strlen( inbuf );
char* outbuf = new char[inlen * 2 + 2 ];
std::string strRet;
if( code_convert("utf-8", "gb2312", inbuf, inlen, outbuf, inlen * 2 + 2 ) == 0 )
{
strRet = outbuf;
}
delete [] outbuf;
return strRet;
}
//--------------------------------------------------------------------
/* GB2312 -> UTF8 */
std::string a2u( const char* inbuf )
{
size_t inlen = strlen( inbuf );
char* outbuf = new char[inlen * 2 + 2];
std::string strRet;
if( code_convert("gb2312", "utf-8", inbuf, inlen, outbuf, inlen * 2 + 2 ) == 0 )
{
strRet = outbuf;
}
delete [] outbuf;
return strRet;
}
//--------------------------------------------------------------------
}
使用方法如下:
std::string szInfo = “Test中文”; std::string szUTF8 = FKStringConvert::a2u(szInfo.c_str());
第二个问题,自适应多行文字
Scene.cpp
//--------------------------------------------------------------------
// 自适应换行文字
/*
horizontalSpacing: 水平间距
verticalSpacing: 垂直间距
lineWidth: 一行的最大宽度
*/
CCLabelTTF* CXXScene::AutoNewLineText(std::string _string, const char *fontName,
float fontSize, float horizontalSpacing, float verticalSpacing, float lineWidth, ccColor3B p_fontColor )
{
CCArray* labelTTF_arr = CCArray::create();
int index = 0;
int index_max = strlen(_string.c_str());
bool is_end = true;
// 根据ASCII码找出中英文字符,并创建成一个CCLabelTTF对象存入labelTTF_arr数组中
while (is_end)
{
if (_string[index] >= 0 && _string[index] <= 127)
{
std::string englishStr = _string.substr(index,1);
labelTTF_arr->addObject(CCLabelTTF::create(englishStr.c_str(), fontName, fontSize));
index+= 1;
}
else
{
std::string chineseStr =_string.substr(index,3).c_str();
labelTTF_arr->addObject(CCLabelTTF::create(chineseStr.c_str(), fontName, fontSize));
index+= 3;
}
if (index>=index_max) {
is_end=false;
}
}
// 在CCLabelTTF对象上添加子对象CCLabelTTF,以此组合成一句话,以左上角第一个字为锚点。。
CCLabelTTF* returnTTF = (CCLabelTTF*)labelTTF_arr->objectAtIndex(0);
float nowWidth = returnTTF->getContentSize().width; // 本行的行宽
CCLabelTTF* BeforeCurTTF = (CCLabelTTF*)labelTTF_arr->objectAtIndex(0);
CCLabelTTF* CurLineBeginTTF = (CCLabelTTF*)labelTTF_arr->objectAtIndex(0); // 行首最左边的字符
int arr_count = labelTTF_arr->count();
for (int i=1; i < arr_count; i++)
{
CCLabelTTF* CurTTF = (CCLabelTTF*)labelTTF_arr->objectAtIndex(i);
CurTTF->setColor( p_fontColor );
CurTTF->setAnchorPoint(ccp(0, 0.5));
nowWidth+= CurTTF->getContentSize().width;
char* pNewLineKey = "\n"; // 该符号换行
char* pMissKey = "\r"; // 该符号不处理
// 宽度超长
if ( nowWidth >= lineWidth )
{
CurTTF->setPosition(ccp( ( -nowWidth + CurTTF->getContentSize().width) - BeforeCurTTF->getContentSize().width * 0.5f,
-BeforeCurTTF->getContentSize().height * 0.5 -verticalSpacing));
nowWidth = returnTTF->getContentSize().width;
// 保存行首
CurLineBeginTTF = CurTTF;
}
// 是换行符
else if( strcmp( CurTTF->getString() , pNewLineKey ) == 0 )
{
CurTTF->setPosition(ccp( ( -nowWidth + CurTTF->getContentSize().width) - BeforeCurTTF->getContentSize().width * 0.5f,
-BeforeCurTTF->getContentSize().height * 0.5 -verticalSpacing));
nowWidth = returnTTF->getContentSize().width;
// 保存行首
CurLineBeginTTF = CurTTF;
}
else if( strcmp( CurTTF->getString() , pMissKey ) == 0 )
{
continue;
}
else
{
CurTTF->setPosition(ccp(BeforeCurTTF->getContentSize().width+horizontalSpacing, BeforeCurTTF->getContentSize().height*0.5f));
}
CurTTF->setTag( 0 );
BeforeCurTTF->addChild(CurTTF);
BeforeCurTTF = CurTTF;
}
return returnTTF;
}
//--------------------------------------------------------------------
使用方式如下:
std::string szInfo = "测试多行自动换行,这里随便改 by FreeKnight";
CCLabelTTF* pHeadTTF = AutoNewLineText( FKStringConvert::a2u(szInfo.c_str()), "Arial", 24, 1, 10, 200, ccc3( 255, 255, 255 ) );
pHeadTTF->setColor( ccc3( 255, 255, 255 ) );
pHeadTTF->setPosition( CENTER_POS );
this->addChild( pHeadTTF );
然后是逐字符显示功能,仅仅改动一下调用处即可,代码如下
std::string szInfo = "测试多行自动换行,这里随便改 by FreeKnight";
// 逐字显示
CCLabelTTF* pHeadTTF = AutoNewLineText( FKStringConvert::a2u(szInfo.c_str()), "Arial", 24, 1, 10, 200, ccc3( 255, 255, 255 ) );
pHeadTTF->setColor( ccc3( 255, 255, 255 ) );
pHeadTTF->setPosition( CENTER_POS + ccp( -240, -70 ) );
CCArray* pTTFList = pHeadTTF->getChildren();
CCObject* pObject = NULL;
CCLabelTTF* pChild = NULL;
int nIndex = 1;
CCARRAY_FOREACH( pTTFList, pObject )
{
pChild = (CCLabelTTF*)pObject;
if( pChild == NULL )
break;
for( ; ; )
{
if( pChild == NULL )
break;
pChild->setVisible( false );
CCDelayTime* pAction = CCDelayTime::create( 0.05f * nIndex );
CCCallFuncN* pDone = CCCallFuncN::create( this, callfuncN_selector(CSecen::TTFItemDelayOver) );
CCSequence* pSeq = CCSequence::create( pAction, pDone, NULL );
pChild->runAction( pSeq );
nIndex++;
pChild = (CCLabelTTF*)pChild->getChildByTag( 0 );
}
}
this->addChild( pHeadTTF );
注意,里面有个CSecen::TTFItemDelayOver的回调函数,这个函数代码如下:
// 文字延迟回调
void CScene::TTFItemDelayOver( CCNode* pSender )
{
if( pSender )
pSender->setVisible( true );
}
这样就可以了。 代码简单明确,故不做过多说明。如有不清楚理解的部分,可留言说明问题,我尽力解答。同时,因为要支持中文,所以使用了效率低下的CCLabelTTF,若有什么更好的建议,欢迎交流指出,谢谢。