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