首页 » 技术分享 » cocos2d-x入门学习(五)跨版本代码移植实例之【梦幻连连看】

cocos2d-x入门学习(五)跨版本代码移植实例之【梦幻连连看】

 

《梦幻连连看》源代码来自原作者kuloveyouwei,他的版本是cocos2d-x 2.1.2,

他在博客中详细解读了源代码,其博客地址http://  blog.csdn.net/kuloveyouwei/article/details/12912875
源代码和素材下载地址http://    download.csdn.net/detail/kuloveyouwei/7158157

 

下面我们要示范的是如何把cocos2d-x不同版本代码移植,

本案例是从cocos2d-x 2.1.2+mac环境到cocos2d-x-2.2.2+VS2010。

 

1、创建工程。用VS2010打开编译运行,使得HelloWorld界面正常显示。
2、下载本游戏所需的资源,将下载的images和Sounds文件夹资源放置新建项目的"Resources"目录下。
3、把下载的Resources文件夹的资源放置到新建项目的"Resources"目录下。
4、把下载的Classes里(除了AppDelegate的.h和.cpp外)的.cpp和.h、.pch文件放置到新建项目的Classes目录下。

5、把下载的Classes里的《levelinfo.plist》文件放置到新建项目的"Resources"目录下。
6、右键——添加——现有项目,把我们项目的Classes目录下的.cpp和.h、.pch文件都添加进去。

7、代码调整:

关于Unicode 格式

编译运行后,提示很多这个warning:
该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失。
之前忽视了warning,后来才发现这个是大问题,因为会影响到回车符。
以前在LINUX或其他的跨平台下写的,拷到VS2010后常常会提示如此,这是因为不同平台的换行符所造成的。VS中有unicode字符 ,打开后再保存, vs会自动保存为unicode格式.但有时unicode字符后的"未正确识别导致字符串不能结束,unicode字符后的换行未正确识别导致//后的注释不能正常结束。
但问题怎么解决呢?我再才找到源文件,复制了一份,全部一一保存为Unicode 格式。操作方法如下:在VS打开该文件前,先用电脑中记事本打开,然后文件--另存为,在弹出的对话框的下页的编码中选择unicode编码和保存类型为所有文件即可。(风险:交叉编译到安卓的时候,gcc的编译器可能会不认识Unicode 格式,如果可以的话,先尝试找1 2个把格式另存为ANSI看看能否成功。)
修改好后复制到项目的class目录并覆盖掉原来的问题,记得不用复制AppDelegate的.h和.cpp。(此外,我备份了一个,以防后续要用)

 

关于随机函数和随机种子

错误提示:srandom 找不到标识符,
原因是库函数中没有srandom()、random(), 只有srand()跟rand()函数
把srandom()用srand()函数代替就没有这个错误了。

报错提示:helloworldscene.cpp(403): error C2660: “srand”: 函数不接受 0 个参数
我们通过srand((unsigned)time(NULL));或者srand(int(time(0)));种子函数解决,即要求一个无符号的整形参数置随机数生成器的启动值
报错地方一:
int times=arc4random()%5+1;改为
srand((unsigned)time(NULL));
int times=rand()%5+1;
报错地方二:
float yoffset=arc4random()%40+1; 改为:
srand((unsigned)time(NULL));
float yoffset=rand()%40+1;

 

关于函数参数
报错提示:“类型转换”: 无法从“void (__thiscall PauseLayer::* )(void)”转换为“cocos2d::SEL_MenuHandler”

参考资料1:http://   blog.csdn.net/xuzhuang2008/article/details/9280889
参考资料2:http://   my.eoe.cn/599048/archive/24801.html
解决方法如下:
我们先从容易理解的开始。
例如这句this->scheduleOnce(schedule_selector(GameLayer::playSound1), 65);
找到 .cpp文件中找到void GameLayer::playSound1()函数进行修改为void GameLayer::playSound1(float dt)
.h文件中找到void playSound1(void);修改为 void playSound1(float dt);

例如this->scheduleOnce(schedule_selector(GameLayer::playSound2), 60);
找到 .cpp文件中找到void GameLayer::playSound2()函数进行修改为void GameLayer::playSound2(float dt)
.h文件中找到void playSound2(void);修改为 void playSound2(float dt);

例如这句this->scheduleOnce(schedule_selector(GameLayer::playSound3), 62);
找到 .cpp文件中找到void GameLayer::playSound3()函数进行修改为void GameLayer::playSound3(float dt)
.h文件中找到void playSound3(void);修改为 void playSound3(float dt);

同理修改以下各行,找到对应的函数修改参数为(float dt)
this->schedule(schedule_selector(HelloWorld::wavesAnimation), 10);
this->schedule(schedule_selector(HelloWorld::birdsselfAnimation), 5);
this->schedule(schedule_selector(GameLayer::update), 1);

报错提示:“类型转换”: 无法从“void (__thiscall GameLayer::* )(void)”转换为“cocos2d::SEL_MenuHandler”
1>指向成员的指针有不同的表示形式;无法在它们之间进行类型转换
再看报错的这行CCMenuItemSprite *item=CCMenuItemSprite::create(pauseSprite, pauseSprite_s, this, menu_selector(GameLayer::pausePressed));
我们尝试在.cpp和.h文件中找到pausePressed函数,修改参数(CCObject* pSender)
void GameLayer::pausePressed()修改为void GameLayer::pausePressed(CCObject* pSender)
void pausePressed(void);修改为void pausePressed(CCObject* pSender);

报错提示:“类型转换”: 无法从“void (__thiscall PauseLayer::* )(cocos2d::CCNode *)”转换为“cocos2d::SEL_MenuHandler”
指向成员的指针有不同的表示形式;无法在它们之间进行类型转换
跟上面的同理,修改以下各行报错,都把其参数修改为(CCObject* pSender)

CCMenuItemSprite *restartitem=CCMenuItemSprite::create(restartSprite, restartSprite_s, this, menu_selector(PauseLayer::restartPressed));
CCMenuItemSprite *helpitem=CCMenuItemSprite::create(helpSprite, helpSprite_s, this, menu_selector(PauseLayer::helpPressed));
CCMenuItemSprite *musicitem=CCMenuItemSprite::create(musicSprite, musicSprite_s, this, menu_selector(PauseLayer::musicPressed));
CCMenuItemSprite *sounditem=CCMenuItemSprite::create(soundSprite, soundSprite_s, this, menu_selector(PauseLayer::soundPressed));
CCMenuItemSprite *item=CCMenuItemSprite::create(playSprite, playSprite_s, this, menu_selector(HelloWorld::playBack));
pauseLayer->backitem->setTarget(this, menu_selector(GameLayer::backPressed));
pauseLayer->startitem->setTarget(this, menu_selector(GameLayer::startPressed));

 

“GameLayer::playSound1”: 函数不接受 0 个参数
这个是上面playSound1函数修改了,但这儿报错未修改所以报错。
把this->playSound1();修改为this->playSound1(float dt);
但是结果还是报错,再次尝试修改为   this->playSound1(1);
 

再次编译,生成成功,但是调试报错。

这时能够听到HelloWorldScene的背景音乐,但是没有HelloWorldScene的画面出来。

 

关于修改屏幕尺寸
在main.cpp把eglView->setFrameSize(480, 320);
修改为eglView->setFrameSize(1136,640);
暂时我们用(1136,640)作为开发用。

 

关于音乐格式

先注释掉背景音乐的后一行this->initUI();
再次编译运行,发现音乐播放正常,但是关闭的时候会有错误。
提示异常Game003.exe 中的 0x7cfacb3d 处有未经处理的异常: 0xC0000005: 读取位置 0x0be8d8c0 时发生访问冲突
quartz.dll 7cfacb3d
没有为quartz.dll加载符号
解决方法:
用软件《格式工厂》把那4个mp3的音频都转为wav格式,因为win系统中用mp3格式会报错,暂时先改为wav格式。

(后续如果要弄到安卓里面,可以试试改回mp3格式的文件,以节省内存)
SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("class.mp3");
SimpleAudioEngine::sharedEngine()->playBackgroundMusic("class.mp3", true);
改为:
SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("class.wav");
SimpleAudioEngine::sharedEngine()->playBackgroundMusic("class.wav", true);

 

关于CCString::createWithFormat

我们把this->initUI();这行,取消注释。
找到initUI函数的代码,把可能导致错误的部分都先注释掉,然后再慢慢逐部分地调试。(初学者可以尝试把大部分都先注释掉,再逐部分地调试)经调试发现:
关于船,注释掉以下
// this->addBoat();
找到CCString *string=CCString::createWithFormat("sailing_boat%d.png");
修改为:CCString *string=CCString::createWithFormat("sailing_boat%d.png",i);

关于鸟
//   this->schedule(schedule_selector(HelloWorld::birdsselfAnimation), 5);

关于开始菜单
//CCSprite *playSprite=CCSprite::create("button_play-hd.png");
//CCSprite *playSprite_s=CCSprite::create("button_play_s-hd.png");
//CCMenuItemSprite *item=CCMenuItemSprite::create(playSprite, playSprite_s, this, menu_selector(HelloWorld::playBack));
//CCMenu *menu=CCMenu::create(item,NULL);
//item->setAnchorPoint(ccp(0.5, 0.5));
//menu->setPosition(ccp(wSize.width/2,110*2));
//this->addChild(menu, 1);
这些注释掉以后,调试正常,没有报错。

我们可以看到白云倒影的效果,是通过翻转、设置透明度、位置错开从而增加真实感。

 

关于鸟,我们取消该行注释this->schedule(schedule_selector(HelloWorld::birdsselfAnimation), 5);
运行看到报错cccarray,

Assertion failed!

File:  \cccarray.cpp

line:122 

Expression:object !=0

我们看到createWithFormat参数不止1个,

找到CCString *string=CCString::createWithFormat("seagull%d.png");修改为:
CCString *string=CCString::createWithFormat("seagull%d.png",i);
同理修改船,取消该行注释this->addBoat();

我们尝试取消注释的开始菜单,编译后运行发现,点击Play可以看到场景跳转时旋转动画的界面,但是新的场景没有正常显示。
所以我们推测,是GameLayer.cpp的init函数里面出了问题。

 

————————
到这里HelloWorld的问题已经处理完毕,下面我们开始看跳转到GameLayer.cpp。

 

同样是音乐格式问题

我们看init函数,找到并注释掉这行this->loadingAnimation();
我们顺着playSound1函数,分别找到SimpleAudioEngine::sharedEngine()->playBackgroundMusic("Back2new.mp3", false);改为wav。
找到SimpleAudioEngine::sharedEngine()->playBackgroundMusic("Back3new.mp3", false);也改为wav
找到SimpleAudioEngine::sharedEngine()->playBackgroundMusic("MainMenu.mp3", false);也改为wav
编译运行后,点击Play可以看到HelloWorld场景已经正常跳转到新的画面,可以看到initLoadingUI正常显示,并且有音乐背景。

我们找到这行this->loadingAnimation();,取消这行注释
编译运行又再看到cccarray报错。

 

我们顺着loadingAnimation,看到removeLoadSprite,又看到initUI这里正如刚刚HelloWorld里面的需要修改。
找到initUI函数的代码,把可能导致错误的部分都先注释掉,然后再慢慢逐部分地调试。

我们暂时注释掉(这里后面是需要修改的!!)
//this->functionSpriteAnimation(CCPoint(leisureSprite->getContentSize().width/2+10*2, wSize.height+leisureSprite->getContentSize().height/2+5*2) ,CCPoint(leisureSprite->getContentSize().width/2+10*2, wSize.height+leisureSprite->getContentSize().height/2));
//this->functionPauseSpriteAnimation(CCPoint(pausemenu->getPosition().x, -pauseSprite->getContentSize().height/2-5*2), CCPoint(pausemenu->getPosition().x, -pauseSprite->getContentSize().height/2));
//levelSprite->setVisible(false);
//progressbgSprite->setVisible(false);
//progress->setVisible(false);
//numsTTF->setVisible(false);
//CCMoveTo *ac1=CCMoveTo::create(0.15, CCPoint(-44*2, pauseLayer->getPosition().y));
//CCMoveTo *ac2=CCMoveTo::create(0.15, CCPoint(-100, pauseLayer->getPosition().y));
//CCSequence *seq=CCSequence::create(ac1,ac2,CCCallFunc::create(this, callfunc_selector(GameLayer::gamePause)),NULL);
//pauseLayer->runAction(seq);
  

经调试发现,暂时注释掉以下内容,可以正常编译:

暂停菜单的this->dialogAnimaton();
关于设置监听事件注释掉pauseLayer->startitem->setTarget(this, menu_selector(GameLayer::startPressed));
关于 mapLayer,注释掉:
mapLayer=MapLayer::create();
mapLayer->setPosition(120,20);
this->addChild(mapLayer, 1);
mapLayer->setVisible(false);

取消之前暂停菜单的注释this->dialogAnimaton();
我们顺着dialogAnimaton看到dialogAnimaton2,看到dialogAnimaton3,看到removeDialogLayer
在removeDialogLayer函数中,注释掉以下的部分(这里后面是需要修改的!!)
//mapLayer->setVisible(true);
//this->functionSpriteAnimation(CCPoint(leisureSprite->getPosition().x, wSize.height-leisureSprite->getContentSize().height/2-15*2) ,CCPoint(leisureSprite->getPosition().x, wSize.height-leisureSprite->getContentSize().height/2-10*2));
//this->functionPauseSpriteAnimation(CCPoint(pausemenu->getPosition().x, 34*2), CCPoint(pausemenu->getPosition().x, 29*2));
//系统刷新
//this->scheduleUpdate();
//this->schedule(schedule_selector(GameLayer::update), 1);

我们找到关于设置监听事件的
 //pauseLayer->startitem->setTarget(this, menu_selector(GameLayer::startPressed));取消该行注释
我们找到startPressed函数,看到gameRestart,看到resume,代码均没有什么问题。
调试运行,可以看到正常没有报错。

————————————
我们取消之前注释的
mapLayer=MapLayer::create();
mapLayer->setPosition(120,20);
this->addChild(mapLayer, 1);
mapLayer->setVisible(false); 
然后在removeDialogLayer函数中找到mapLayer->setVisible(true);取消注释
再找到MapLayer.cpp 暂时注释掉init函数里面的
this->initData();
this->initUI();
这时候运行是会发现报错,提示错误的地方如下:
MapLayer::~MapLayer()
{
 CC_SAFE_DELETE_ARRAY(mapArray);
}
这是MapLayer的析构函数报错,暂时先把这行CC_SAFE_DELETE_ARRAY(mapArray);注释掉。(这里后面是需要修改的!!)

我们现在处理GameLayer.cpp的最后一项工作,找到removeDialogLayer函数,把之前的这段取消注释
this->functionSpriteAnimation(CCPoint(leisureSprite->getPosition().x, wSize.height-leisureSprite->getContentSize().height/2-15*2) ,CCPoint(leisureSprite->getPosition().x, wSize.height-leisureSprite->getContentSize().height/2-10*2));
this->functionPauseSpriteAnimation(CCPoint(pausemenu->getPosition().x, 34*2), CCPoint(pausemenu->getPosition().x, 29*2));
//系统刷新
this->scheduleUpdate();
this->schedule(schedule_selector(GameLayer::update), 1);
我们找到GameLayer::update  看到代码均没有什么问题。
调试运行,可以看到正常没有报错。

关于中文显示错误,我们这里先用改为英文这个最简单的方式解决。
把    dialogLevelString=CCString::create("第一关");
    dialogTishiString=CCString::create("王子大人");
修改为:
dialogLevelString=CCString::create("Round 001");
    dialogTishiString=CCString::create("Linking Game");

到这里GameLayer.cpp的问题已经处理完毕,下面我们开始看开始处理mapLayer.cpp。

经过一番的调试,发现只需要修改一个地方。
我们在cocos2d-x自带的官方TestCpp中看到,现在已经没有用CC_SAFE_DELETE_ARRAY而是用CC_SAFE_RELEASE。
把取消该行注释,//CC_SAFE_DELETE_ARRAY(mapArray);
并修改为CC_SAFE_RELEASE(mapArray);

再次编译运行,我们看到游戏画面如下:

终于!最后,编译运行测试正常。游戏可以玩了哦,心情真是鸡冻!

从cocos2d-x 2.1.2+mac环境弄到cocos2d-x-2.2.2+VS2010真不容易啊~!~瓦咔咔,搞定,收工!!!

——————
——————
PS,题外话1:
mapLayer.cpp的调试,一开始没有发现是这个版本CC_SAFE_DELETE_ARRAY取消,改为CC_SAFE_RELEASE。
进而导致这个找错误的过程,我浪费了好多精神。
以下记录一下,这些看似无用功,白搭的事情,却让自己进步学习了的小小经历。
我们进入到mapLayer.cpp文件中。
再尝试this->initData();该行取消注释
编译运行,再次看到Debug Assertion Failed!
我找到initData函数的代码,把可能导致错误的部分都先注释掉,然后再慢慢逐部分地调试。
经调试发现,暂时注释掉以下内容,可以正常编译:
//mapArray=CCArray::create();
//mapArray->retain();

//mapArray->addObject(node);

//mapArray->addObject(array2->objectAtIndex(i));

添加touch监听
//CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 1, true);

——————
this->initUI();该行取消注释
把以下这段注释掉
/* if (this->imageFilename(index))
{
CCSprite *sprite=CCSprite::createWithSpriteFrameName(this->imageFilename(index)->getCString());
sprite->setScale(1.0);
sprite->setPosition(ccp(OFFSET_X * x + (SIZE_W / 2) + SIZE_W * x, OFFSET_Y * y + (SIZE_H / 2) + SIZE_H * y));
this->addChild(sprite, 1, TAG_START_SPRITE+index);               
}*/
_____________
把以下几行取消注释
mapArray=CCArray::create();
mapArray->retain();
mapArray->addObject(node);     
mapArray->addObject(array2->objectAtIndex(i));
经调试发现,可以正常编译。

再将添加touch监听
//CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 1, true);取消注释,
我们这时候发现情况比较奇怪,有时候报错,有时候并不报错。发现在显示关卡的时候,不点击的话,一般是不会报错。
报错内容为:Game003.exe 中的 0x0041d59e 处有未经处理的异常: 0xC0000005: 读取位置 0x00000000 时发生访问冲突
指向代码:spritecurrent->setScale(1.3);

暂时先不管它了,initData这里先就此结束。
我们看到initUI。
我们取消注释
/* if (this->imageFilename(index))
{
CCSprite *sprite=CCSprite::createWithSpriteFrameName(this->imageFilename(index)->getCString());
sprite->setScale(1.0);
sprite->setPosition(ccp(OFFSET_X * x + (SIZE_W / 2) + SIZE_W * x, OFFSET_Y * y + (SIZE_H / 2) + SIZE_H * y));
this->addChild(sprite, 1, TAG_START_SPRITE+index);               
}*/
编译看到游戏可以玩。

题外话2:我们留意到//暂停界面
pauseLayer=PauseLayer::create();
pauseLayer->setPosition(ccp(-pauseLayer->getContentSize().width, wSize.height/2-pauseLayer->getContentSize().height/2));
this->addChild(pauseLayer, 1);
//设置监听事件
pauseLayer->backitem->setTarget(this, menu_selector(GameLayer::backPressed));
找到PauseLayer.cpp的init函数,进而看到initUI函数,看到restartPressed和helpPressed、musicPressed、soundPressed这4个函数里面都是空的。
作者并没有写这4个函数,从测试情况可以看到这导致左侧的按钮这4个没法使用。当然,这些细节的功能,并不妨碍游戏的正常操作。

如果想完善这几个函数,可以参考该帖子http://  blog.csdn.net/oyangyufu/article/details/24736711

————————————————————————

———————————————————————— 

最后附上,修改后的cocos2d-x 2.2.2版本代码下载地址:http://  download.csdn.net/detail/u013174689/7394611

说明里面不包含资源文件,资源文件请移步到原作者源代码和素材下载地址http://    download.csdn.net/detail/kuloveyouwei/7158157

转载自原文链接, 如需删除请联系管理员。

原文链接:cocos2d-x入门学习(五)跨版本代码移植实例之【梦幻连连看】,转载请注明来源!

0