依然在《三勇者传说》开发过程中,我希望出现类似于日式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,若有什么更好的建议,欢迎交流指出,谢谢。