TTF中文乱码 -- Cocos2dx 游戏移植WP8之路

Cocos2dx 是目前最流行的手机游戏引擎之一,开源、轻量、多平台等的诸多特性使得它被很多国内外手游开发者所喜爱。利用Cocos2dx来开发Windows Phone 8的游戏同样也是非常的方便高效。当然任何跨平台的游戏引擎,最终解决的都只能是游戏层面的问题:包括场景的管理、图形的渲染、真实物理世界的模拟等;要想真正在一个平台上把游戏做好,不可避免会遇到很多和平台相关的问题,需要我们每个游戏程序员对于该平台的技术有个比较深入的了解。在Windows Phone 8这个平台上,我希望通过自己的努力帮助大家解决移植过程中遇到的问题。

Cocos2dx的WP8项目在中文显示这一块,一直有些问题。虽然这算不上很严重的Bug,但也足以令不少开发者费神。因为这是所有面向国内市场的游戏开发者所无法回避的问题。我总结了一下,中文显示不出主要集中在大家使用CCLabeTTF显示TrueTypeFont的中文时,会发生二个症状:一是中文显示为方块,二是中文显示乱码。如下图所示:

 

显示方块

 

显示乱码

 

更麻烦的是,Cocos2dx自带的TestCpp也有同样的问题(注1)。

下面给出解决方案:

 

中文只显示方块的问题是由于CCTableTTF所指定的TTF字体在WP8下不支持中文,比如Marker Felt 或是Arial字体。解决的办法很简单,只要替换支持中文的字体即可。这里推荐大家使用arialuni.ttf (Arial Unicode MS) 或是Cocos2dx自带的HKYuanMini.ttf(我已上传arialuni.ttf字库供大家使用)。这里提醒大家需要把新添加的字库文件放到游戏工程的Resources\fonts目录下,并确保该字库文件打入XAP包中。代码的写法类似下面所示:

auto label = LabelTTF::create("梅颖广研究Cocos2dx","fonts/arialuni.ttf",36);

 

如果出现中文乱码的情况,那就是字符编码不对的问题,这不是简单替换字库文件所能搞定的。大家所做的第一件事情,就是确认需要输出的中文是什么编码。如果大家是直接把输出的中文写在Visual Studio里,就像上述的那条语句一样,那就去查看一下Visual Studio编码方式。方法是打开File \ Advanced Save Option…菜单,弹出如下对话框。

一般可能会是三种编码方式:Unicode(UTF-8 without signature), Unicode(UTF-8 with signature), Chinese Simplified (GB2312)。其中UTF8 的signature的意思就是BOM (Byte Order Mark) ,带不带BOM的区别可以参考如下文章:

 

当然大家可以通过上述对话框切换编码。Cocos2dx处理的必须是UTF8编码的字符,如果不符合的话就会显示乱码。除此之外我还发现,如果编码选择不好,在代码中直接插入中文的时候,中文字符会和后面的标点产生干涉,发生奇怪的编译错误。比如下面这句,究和右分号会产生错误。

auto label = LabelTTF::create("Cocos2dx梅颖广研究","fonts/arialuni.ttf", 36);

 

针对这三种编码,我做了测试,请看下表的结果:

编码

是否需要Unicode到UTF8转换

是否会产生干涉

GB2312

需要转换

不会

UTF8 no signature

不需要转换

UTF8 with signature

需要转换

不会

 

从表可以看出,UTF8 no signature的编码虽然不需要做Unicode到UTF8的转换,方便快捷,但会产生字符干涉的问题,所以,我建议大家采用GB2312或者UTF8 with signature的编码,然后在使用CCLabelTTF前做Unicode到UTF8的转换。

 

下面和大家谈谈如何实现Unicode到UTF8的转换。这个转换方法很多,比如采用iconv库等。其实比较方便的是采用Cocos2dx自带的转换函数CCUnicodeToUtf8 ( CCWinRTUtils.h & cpp )。标准的写法如下所示:

auto label = LabelTTF::create( CCUnicodeToUtf8(L"梅颖广研究Cocos2dx"), "fonts/arialuni.ttf", 36);

 

 

 

如果打开CCUnicodeToUtf8 函数,大家就会发现此函数的本质是使用了WideCharToMultiByte 函数。当然,这个是Windows平台才有的函数,无法跨平台。所以如果大家想完美的话,可以采用纯C++写的函数。如下所示:

std::string WStrToUTF8(conststd::wstring& src)

{

       std::string dest;

       dest.clear();

       for (size_ti = 0; i < src.size(); i++)

       {

              wchar_tw = src[i];

              if (w <= 0x7f)

              {

                     dest.push_back((char)w);

              }

              elseif (w <= 0x7ff)

              {

                     dest.push_back(0xc0 | ((w >> 6) & 0x1f));

                     dest.push_back(0x80 | (w & 0x3f));

              }

              elseif (w <= 0xffff)

              {

                     dest.push_back(0xe0 | ((w >> 12) & 0x0f));

                     dest.push_back(0x80 | ((w >> 6) & 0x3f));

                     dest.push_back(0x80 | (w & 0x3f));

              }

              elseif (sizeof(wchar_t) > 2 && w <= 0x10ffff)

              {

                     dest.push_back(0xf0 | ((w >> 18) & 0x07));

                     dest.push_back(0x80 | ((w >> 12) & 0x3f));

                     dest.push_back(0x80 | ((w >> 6) & 0x3f));

                     dest.push_back(0x80 | (w & 0x3f));

              }

              else

                     dest.push_back('?');

       }

       return dest;

}

 

然后,完美输出中文的函数如下所示:

auto label = LabelTTF::create( WStrToUTF8(L"梅颖广研究Cocos2dx"), "fonts/arialuni.ttf", 36);

 

用这种办法,其它语言的输出也没有问题。比如:

auto label = LabelTTF::create( WStrToUTF8(L"よろしくお願い梅颖广研究"), "fonts/arialuni.ttf", 36);

 

最后一个问题,当使用CCTTFLabel自动换行功能的时候,即提供dimension参数给CCTTFLabel::create() 函数时,中文输出会无法换行。针对这个问题,其实与中文无关。即使全是英文也可能出现这种情况。主要的原因是由于Cocos2dx在实现这个功能的时候,是靠空格来判断是否超越了边界。所以只要是一大段文字,不带空格的,都会出现无法换行的情况。如何解决这个问题呢?我给大家推荐一篇文章:

最后,我默认各位读者对于Cocos2dx已是非常的熟悉。如果大家对于Cocos2dx引擎本身或如何在WP8上建立Cocos2dx开发环境不清楚的话,推荐大家去观看我在微软虚拟在线课堂上的免费课程。

 谢谢!

梅颖广

注1:Cocos2dx 3.2的 LabelTest.cpp文件的LabelTTFChinese

 

 

arialuni.ttf