cocos2d-x中文问题处理

cocos2d-x中只能识别UTF8编码的中文,如果直接在VC下输入中文,显示时会乱码,而且他也没有提供多语言的解决方案。这些问题可以通过将字符串放到一个XML字典文件中来解决。

(一)    参考安卓的字符串命名方法,在cocos2d-x工程的“Resources”目录下,新建一个文件“string-zh-rCN.xml”,在其中输入你的字符串,如:

<?xml version=”1.0″ encoding=”utf-8″?>
<dict>

<key>hello</key>
<string>您好,欢迎来到我的世界</string>
<key>info</key>
<string>这是我的第一个中文应用</string>

</dict>

然后保存成 “UTF8” 文件,且记,一定要保存成这个格式

(二)    在cocos2d-x中用CCDictionary读取对应的字符串

CCDictionary *strings = CCDictionary::createWithContentsOfFile(“string-zh-rCN.xml”);
const char *hello = ((CCString*)strings->objectForKey(“hello”))->m_sString.c_str(); CCLabelTTF * pLabel=CCLabelTTF::create(hello, “Arial”, 22);

这样,就可以正确的显示中文字符了

(三)    其他语种,只需添加一个类string-zh-rCN.xml的文件,读取时根据系统当前语言设置,采用不同的XML,就可以实现多语种自适应。

 

调用CCMenu的setHandlerPriority方法时,出现crash问题的解决方法

在一个CCLayer上,添加了很多个CCMenu, 其中有一个要处理自定义事件,首先想到的是用CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate 添加实现按键处理,将优先级设为kCCMenuHandlerPriority-1, 这样在当前CCLayer可以达到效果,但是在上面添加一层CCLayer时,上面层的事件也被拦截了。

换个角度,将上面自定义的优先级改成kCCMenuHandlerPriority,第一层其他的CCMenu 调用 setHandlerPriority(kCCMenuHandlerPriority+1),这样覆盖第2个CCLayer就不会调用第一层的响应函数,但是CCMenu 调用 setHandlerPriority时出现crash。跟踪cocos2d-x的源代码,发现是时序的问题。

我的CCMenu 是在点击一个按钮时创建的,在创建时调用setHandlerPriority。CCTouchDispatcher中有个m_bLocked标志,在按键事件触发后,调用了如下代码

void CCTouchDispatcher::touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex)
{
CCAssert(uIndex >= 0 && uIndex < 4, “”);

CCSet *pMutableTouches;
m_bLocked = true;

接下来调用创建新的CCMenu,在addChild时,会添加当前CCMenu的Delegate,代码如下:

void CCTouchDispatcher::addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches)
{
CCTouchHandler *pHandler = CCTargetedTouchHandler::handlerWithDelegate(pDelegate, nPriority, bSwallowsTouches);
if (! m_bLocked)
{
forceAddHandler(pHandler, m_pTargetedHandlers);
}
else
{
/* If pHandler is contained in m_pHandlersToRemove, if so remove it from m_pHandlersToRemove and return.
* Refer issue #752(cocos2d-x)
*/
if (ccCArrayContainsValue(m_pHandlersToRemove, pDelegate))
{
ccCArrayRemoveValue(m_pHandlersToRemove, pDelegate);
return;
}

m_pHandlersToAdd->addObject(pHandler);
m_bToAdd = true;
}
}

接着调用CCMenu的setHandlerPriority时,这个CCMenu的Delegate还放在m_pHandlersToAdd中,并没有加到m_pTargetedHandlers,所以

void CCTouchDispatcher::setPriority(int nPriority, CCTouchDelegate *pDelegate)
{
CCAssert(pDelegate != NULL, “”);

CCTouchHandler *handler = NULL;

handler = this->findHandler(pDelegate);

CCAssert(handler != NULL, “”);

if (handler->getPriority() != nPriority)
{
handler->setPriority(nPriority);
this->rearrangeHandlers(m_pTargetedHandlers);
this->rearrangeHandlers(m_pStandardHandlers);
}
}

这里会返回空:handler = this->findHandler(pDelegate);

程序出现crash

 

解决方法:

在创建完CCMenu后,添加一个定时器,this->schedule(schedule_selector(setHandlerPriority), 0.5000f);

在定时器里再调用setHandlerPriority就不会出现问题。

cocos2d-x中长按的检测

cocos2d-x的MENU只有单击事件的触发机制,如果想实现长按的检测,要自己通过继承CCTargetedTouchDelegate实现。

如果对象继承自CCNode,要实现长按,可以添加继承“public CCTargetedTouchDelegate”。然后再实现下面的虚函数。

virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);

首先,在ccTouchBegan检测点击的是不是当前对象,如果是,则设置一个定时器。

bool SpriteButton::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
CCRect rect1=pSpriteButton->rect();

//左右范围比实际少5个像素,避免叠加检测到
CCRect rect2=CCRectMake(rect1.getMinX()+5, rect1.getMinY(),
rect1.getMaxX()-rect1.getMinX()-10, rect1.getMaxY()-rect1.getMinY());

m_bTouchedMenu = rect2.containsPoint( pTouch->getLocation());
if(m_bTouchedMenu)
{
m_bLongPress=false;

启动一个定时器,0.3秒后还在则执行一次长按的事件
this->schedule(schedule_selector(SpriteButton::longPress), 0.3000f);
}
return true;
}

在ccTouchEnded和ccTouchCancelled取消掉定时器

if(m_bTouchedMenu)
{
m_bLongPress=false;
m_bTouchedMenu=false;
this->unschedule(schedule_selector(SpriteButton::longPress));
}

以上内容加好后,发现定时器始终没有启动,最后发现是没有用“CCNode::onEnter();”的缘故,这个在CCLayout的初始化中是会调用的,CCNode则必须添加调用了。跟踪cocos2d-x的源代码,发现CCNode中有

void CCNode::schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay)
{
CCAssert( selector, “Argument must be non-nil”);
CCAssert( interval >=0, “Argument must be positive”);

m_pScheduler->scheduleSelector(selector, this, interval , repeat, delay, !m_bRunning);
}

“m_bRunning”是一个暂停定时器的标志,在CCNode()的构造函数中,默认是“false”,m_bRunning(false)  。在CCNode::onEnter()中会赋值成”true”。

void CCNode::onEnter()
{
arrayMakeObjectsPerformSelector(m_pChildren, onEnter, CCNode*);

this->resumeSchedulerAndActions();

m_bRunning = true;

if (m_eScriptType != kScriptTypeNone)
{
CCScriptEngineManager::sharedManager()->getScriptEngine()->executeNodeEvent(this, kCCNodeOnEnter);
}
}

压缩游戏大小

用cocos2d-x开发了一个小游戏,内容没多少,发现总体积也达到了6M,有没有什么办法压缩到小点呢。分析发现大的内容主要是3块:图片,音乐,引擎库,从这3方面下手,成功将6M压缩到了4.5M,去掉了1/4;

1)图片:cocos2d-x 支持“图形编辑器”支持的大图,上一篇谈到了“zwoptex”的使用,用这个东西,将以前零散的图片集成,成功从3.3M变成了2.5M,减小了700K。

2)音乐:用GoldWave将128bit的mp3转换成64bit的mp3, 音质感觉不到什么变化,当体积去减少了900K。

3)引擎库:cocos2d-x的库压缩后有1.5M,这个暂时还不知道怎么压缩。

 

在cocos2d-x中使用图片编辑器

图片编辑器的功能 就是把一系列图片拼接成一张大图片,同时生成一个plist后缀的图片坐标文件。这样做的原因是,引擎在加载图片时,加载一张图片比加载N张图片的运算量要小的多,占用的内存也少很多。

生成plist文件的工具主要有:

1)zwoptex  包括2个版本,一个是免费的FLASH版本,另一个是MAC版本,收费软件。

2)TexturePacker 免费版中会自动向导出的图片中加入红色。

我这种基础级的,感觉免费的FLASH版本就已经够了。

(1) 首先去http://www.zwopple.com/zwoptex/   点击右下角的“Flash Version”下载到本地;

(2) 用浏览器打开Zwoptex.html, 选择“File”,“Import Image”,导入要组合的文件;

(3) 选择“Modify”,“Canvas Height”,”Canvas Width” 选择一个合适的长宽,能排下自己的所有图片即可;

(4)  从”Arrange”选择一个排版规则,zwoptex就会自动排版,如果排不下所有图片,重复“(3)”调整长宽即可;

(5) 从”File”, 即可“export” 出坐标文件和整张PNG格式图片。

在Cocos2d-x中使用图片,将坐标文件和整张PNG格式图片复制到Resources下

CCSpriteFrameCache *pFrameCache=CCSpriteFrameCache::sharedSpriteFrameCache();
pFrameCache->addSpriteFramesWithFile(“坐标文件名”);

CCSpriteFrame *pFrame=pFrameCache->spriteFrameByName(PicName);”);