2014年4月4日星期五

Cocos2dx 中文支持,文字自动换行,逐字符显示

    依然在《三勇者传说》开发过程中,我希望出现类似于日式AVG游戏的对话面板,此时我不得不实现两种功能:
    1:文字在一个聊天框内能够自适应的换行显示。
    2:大段的文字逐字符的进行显示。
    于是我进行了简单的封装。

首先我们来看中文字符的支持:

FKCommon.h


  1. #include <iostream>
  2. #include <cstring>
  3. #pragma comment( lib, "libiconv.lib" )
  4. #include "iconv\iconv.h"
  5. //--------------------------------------------------------------------
  6. namespace FKStringConvert
  7. {
  8. int code_convert( const char* from_charset, const char* to_charset, 
  9. const char* inbuf, size_t inlen, char* outbuf, size_t outlen );
  10. /* UTF8 - GB2312 */
  11. std::string u2a( const char* inbuf );
  12. /* GB2312 - UTF8 */
  13. std::string a2u( const char* inbuf );
  14. }
  15. //--------------------------------------------------------------------
FKCommon.cpp



  1. //--------------------------------------------------------------------
  2. namespace FKStringConvert
  3. {
  4. //--------------------------------------------------------------------
  5. int code_convert( const char* from_charset, const char* to_charset, const char* inbuf, size_t inlen, char* outbuf, size_t outlen )
  6. {
  7. iconv_t cd;
  8. const char* temp = inbuf;
  9. const char** pin = &temp;
  10. char** pout = &outbuf;
  11. memset( outbuf, 0, outlen );
  12. cd = iconv_open( to_charset, from_charset );
  13. if( cd == 0 ) return -1;
  14. if( iconv( cd, pin, &inlen, pout, &outlen ) == -1 ) 
  15. {
  16. return -1;
  17. }
  18. iconv_close( cd );
  19. return 0;
  20. }
  21. //--------------------------------------------------------------------
  22. /* UTF8 -> GB2312 */
  23. std::string u2a( const char* inbuf )
  24. {
  25. size_t inlen = strlen( inbuf );
  26. char* outbuf = new char[inlen * 2 + 2 ];
  27. std::string strRet;
  28. if( code_convert("utf-8", "gb2312", inbuf, inlen, outbuf, inlen * 2 + 2 ) == 0 )
  29. {
  30. strRet = outbuf;
  31. }
  32. delete [] outbuf;
  33. return strRet;
  34. }
  35. //--------------------------------------------------------------------
  36. /* GB2312 -> UTF8 */
  37. std::string a2u( const char* inbuf )
  38. {
  39. size_t inlen = strlen( inbuf );
  40. char* outbuf = new char[inlen * 2 + 2];
  41. std::string strRet;
  42. if( code_convert("gb2312", "utf-8", inbuf, inlen, outbuf, inlen * 2 + 2 ) == 0 )
  43. {
  44. strRet = outbuf;
  45. }
  46. delete [] outbuf;
  47. return strRet;
  48. }
  49. //--------------------------------------------------------------------
  50. }
  51. //--------------------------------------------------------------------
使用方法如下:

  1. std::string szInfo = "Test中文";
  2.         std::string szUTF8 = FKStringConvert::a2u(szInfo.c_str());

第二个问题,自适应多行文字

Scene.cpp

  1. //--------------------------------------------------------------------
  2. //  自适应换行文字
  3. /* 
  4. horizontalSpacing: 水平间距 
  5. verticalSpacing:   垂直间距 
  6. lineWidth:         一行的最大宽度 
  7.  */  
  8. CCLabelTTF* CXXScene::AutoNewLineText(std::string _string, const char *fontName, 
  9. float fontSize, float horizontalSpacing, float verticalSpacing, float lineWidth, ccColor3B p_fontColor )  
  10. {  
  11. CCArray* labelTTF_arr = CCArray::create();  
  12. int index = 0;  
  13. int index_max = strlen(_string.c_str());  
  14. bool is_end = true;  
  15. // 根据ASCII码找出中英文字符,并创建成一个CCLabelTTF对象存入labelTTF_arr数组中
  16. while (is_end) 
  17. {  
  18. if (_string[index] >= 0 && _string[index] <= 127) 
  19. {  
  20. std::string englishStr = _string.substr(index,1);
  21. labelTTF_arr->addObject(CCLabelTTF::create(englishStr.c_str(), fontName, fontSize));  
  22. index+= 1;  
  23. }  
  24. else
  25. {  
  26. std::string chineseStr =_string.substr(index,3).c_str();  
  27. labelTTF_arr->addObject(CCLabelTTF::create(chineseStr.c_str(), fontName, fontSize));  
  28. index+= 3;  
  29. }  

  30. if (index>=index_max) {  
  31. is_end=false;  
  32. }
  33. }

  34. // 在CCLabelTTF对象上添加子对象CCLabelTTF,以此组合成一句话,以左上角第一个字为锚点。。  
  35. CCLabelTTF* returnTTF = (CCLabelTTF*)labelTTF_arr->objectAtIndex(0);  
  36. float nowWidth = returnTTF->getContentSize().width; // 本行的行宽
  37. CCLabelTTF* BeforeCurTTF = (CCLabelTTF*)labelTTF_arr->objectAtIndex(0);  
  38. CCLabelTTF* CurLineBeginTTF = (CCLabelTTF*)labelTTF_arr->objectAtIndex(0); // 行首最左边的字符

  39. int arr_count = labelTTF_arr->count();  
  40. for (int i=1; i < arr_count; i++)
  41. {  
  42. CCLabelTTF* CurTTF = (CCLabelTTF*)labelTTF_arr->objectAtIndex(i);  
  43. CurTTF->setColor( p_fontColor );
  44. CurTTF->setAnchorPoint(ccp(0, 0.5));  
  45. nowWidth+= CurTTF->getContentSize().width;  
  46. char* pNewLineKey = "\n"; // 该符号换行
  47. char* pMissKey = "\r"; // 该符号不处理
  48. // 宽度超长
  49. if ( nowWidth >= lineWidth )
  50. {  
  51. CurTTF->setPosition(ccp( ( -nowWidth + CurTTF->getContentSize().width) - BeforeCurTTF->getContentSize().width * 0.5f, 
  52. -BeforeCurTTF->getContentSize().height * 0.5 -verticalSpacing));  
  53. nowWidth = returnTTF->getContentSize().width;  
  54. // 保存行首
  55. CurLineBeginTTF = CurTTF;  
  56. }
  57. //  是换行符
  58. else if( strcmp( CurTTF->getString() , pNewLineKey ) == 0 )
  59. {
  60. CurTTF->setPosition(ccp( ( -nowWidth + CurTTF->getContentSize().width) - BeforeCurTTF->getContentSize().width * 0.5f, 
  61. -BeforeCurTTF->getContentSize().height * 0.5 -verticalSpacing));  
  62. nowWidth = returnTTF->getContentSize().width;  
  63. // 保存行首
  64. CurLineBeginTTF = CurTTF;  
  65. }
  66. else if( strcmp( CurTTF->getString() , pMissKey ) == 0 )
  67. {
  68. continue;
  69. }
  70. else
  71. {  
  72. CurTTF->setPosition(ccp(BeforeCurTTF->getContentSize().width+horizontalSpacing, BeforeCurTTF->getContentSize().height*0.5f));  
  73. }  
  74. CurTTF->setTag( 0 );
  75. BeforeCurTTF->addChild(CurTTF);  
  76. BeforeCurTTF = CurTTF;  
  77. }  

  78. return returnTTF;  
  79. }
  80. //--------------------------------------------------------------------

使用方式如下:

  1. std::string szInfo = "测试多行自动换行,这里随便改 by FreeKnight";
  2. CCLabelTTF* pHeadTTF = AutoNewLineText( FKStringConvert::a2u(szInfo.c_str()), "Arial", 24, 1, 10, 200, ccc3( 255, 255, 255 ) );  
  3. pHeadTTF->setColor( ccc3( 255, 255, 255 ) );
  4. pHeadTTF->setPosition( CENTER_POS );  
  5. this->addChild( pHeadTTF ); 

然后是逐字符显示功能,仅仅改动一下调用处即可,代码如下

  1. std::string szInfo = "测试多行自动换行,这里随便改 by FreeKnight";
  2. // 逐字显示
  3. CCLabelTTF* pHeadTTF = AutoNewLineText( FKStringConvert::a2u(szInfo.c_str()), "Arial", 24, 1, 10, 200, ccc3( 255, 255, 255 ) );  
  4. pHeadTTF->setColor( ccc3( 255, 255, 255 ) );
  5. pHeadTTF->setPosition( CENTER_POS + ccp( -240, -70 ) );  

  6. CCArray* pTTFList = pHeadTTF->getChildren();
  7. CCObject* pObject = NULL;  
  8. CCLabelTTF* pChild = NULL;
  9. int nIndex = 1;
  10. CCARRAY_FOREACH( pTTFList, pObject )  
  11. {  
  12. pChild = (CCLabelTTF*)pObject;  
  13. if( pChild == NULL )  
  14. break;
  15. for( ; ; )
  16. {
  17. if( pChild == NULL )
  18. break;
  19. pChild->setVisible( false );
  20. CCDelayTime* pAction = CCDelayTime::create( 0.05f * nIndex );
  21. CCCallFuncN* pDone = CCCallFuncN::create( this, callfuncN_selector(CSecen::TTFItemDelayOver) );
  22. CCSequence* pSeq = CCSequence::create( pAction, pDone, NULL );
  23. pChild->runAction( pSeq );
  24. nIndex++;
  25. pChild = (CCLabelTTF*)pChild->getChildByTag( 0 );
  26. }
  27. }
  28. this->addChild( pHeadTTF ); 

注意,里面有个CSecen::TTFItemDelayOver的回调函数,这个函数代码如下:

  1. // 文字延迟回调
  2. void CScene::TTFItemDelayOver( CCNode* pSender )
  3. {
  4. if( pSender )
  5. pSender->setVisible( true );
  6. }

   这样就可以了。
   代码简单明确,故不做过多说明。如有不清楚理解的部分,可留言说明问题,我尽力解答。同时,因为要支持中文,所以使用了效率低下的CCLabelTTF,若有什么更好的建议,欢迎交流指出,谢谢。

2 条评论:

  1. cocos2d-html5的中文支持是默认的,label换行貌似也和C++版的行为不太一样。
    我的卡牌桌游客户端目前是用cocos2d-html5在写。可以在运行途中调出js控制台直接敲命令修改程序是超惊艳的便利功能

    回复删除
    回复
    1. souga,我使用的还是cocos2dx,我依然轻度质疑Html5的性能问题。话说,我这个FKCocos2dxWrapper增加了自定义解释脚本,部分功能也可以利用重加载的方式直接运行时热更新~嘿嘿

      删除