懒骨头(http://blog.csdn.net/iamlazybone QQ:124774397 青岛)
以后有的忙了
抽空先来一发笔记
网上找了个demo:LOGO叫奇怪的大冒险(应该是@熊同学的demo,感谢:)
源码下载:http://download.csdn.net/detail/iamlazybone/7032991
================================
VS2013+git
先容许我吐槽下vs+git,搞了半天不是文件太大就是项目导入不好用,再不然就是文件被占用不能操作。
尝试了无数次,终于ok了,下面简单列一下步骤:
VS2013+Git 步骤:
1 vs里加入远程git地址,本地git地址
2 添加cocos2dx新建项目,写好gitignore,避开几个比较大的目录,提交
3 vs里Tools->Options->Text Editor->C/C++->Advanced,在 Fallback Location 的属性组中,将"Always Use Fallback Location"设置为 true,将"Do Not Warn If Fallback Location Used" 设置为 true ,然后删除解决方案目录下的 sdf 文件和 ipch 目录
4 项目修改附加目录,新增前三个,仅适用于骨头的项目:$(EngineRoot);$(EngineRoot)cocos;$(EngineRoot)cocos\editor-support;$(EngineRoot)cocos\audio\include;$(EngineRoot)external;$(EngineRoot)external\chipmunk\include\chipmunk;$(EngineRoot)extensions;..\Classes;..;%(AdditionalIncludeDirectories)
================================
其实骨头现在也不知道这到底是个什么demo,怎么玩
报环境:vs2013+cocos2dx 2.2 抱着学习代码的态度,就不移植到3.0了
开始搞起!
================================
首先是手绘风的欢迎界面:HelloWorldScene.cpp类:
1 播放背景音乐:
CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("Audio_bgm_startup_cg.mp3",true);
CCFadeIn* fadein = CCFadeIn::create(1.0f); CCFadeIn* fadein2 = CCFadeIn::create(0.1f); //sprite1->runAction(fadein); CCSpriteFrame * frame0 = CCSpriteFrame::create("StageCG_bkg1_zh.png", CCRectMake(0, 0, 800, 480)); CCSpriteFrame * frame1 = CCSpriteFrame::create("StageCG_bkg2_zh.png", CCRectMake(0, 0, 800, 480)); CCSpriteFrame * frame2 = CCSpriteFrame::create("StageCG_bkg3_zh.png", CCRectMake(0, 0, 800, 480)); CCSpriteFrame * frame3 = CCSpriteFrame::create("StageCG_bkg4_zh.png", CCRectMake(0, 0, 800, 480)); CCArray* array = CCArray::createWithCapacity(4); array->addObject(frame0); array->addObject(frame1); array->addObject(frame2); array->addObject(frame3); CCAnimation* animation = CCAnimation::createWithSpriteFrames(array, 2.0f); CCFiniteTimeAction* actionMoveDone = CCCallFuncN::create(this, callfuncN_selector(HelloWorld::animateFinished)); sprite1->runAction(CCSequence::create(CCSpawn::createWithTwoActions(fadein, CCAnimate::create(animation)), actionMoveDone, NULL));3 加入一个menu菜单,里面有个跳过按钮:
CCMenuItemImage *skipItem = CCMenuItemImage::create( "StageCG_skip_normal_zh.png", "StageCG_skip_pressed_zh.png", this, menu_selector(HelloWorld::skipCallback));4 跳过和动画结束方法里的方法是一样的,都是切换MenuScene:
CCScene *gameMenu = GameMenu::scene(); CCDirector::sharedDirector()->replaceScene(CCTransitionJumpZoom::create(1.0f, gameMenu));
下面看下GameMenu.cpp类:菜单类:
1 这里面就是简单的贴图:
CCSprite* title = CCSprite::create("Startup_lbl_title_zh.png"); title->setPosition(ccp(size.width / 2, size.height - 135)); addChild(title); CCSprite* photo_border = CCSprite::create("Startup_photo_border.png"); photo_border->setPosition(ccp(230, 200)); addChild(photo_border); CCSprite* saveher = CCSprite::create("Startup_lbl_saveher.png"); saveher->setPosition(ccp(230, 240)); addChild(saveher); CCSprite* herface = CCSprite::create("Startup_herface1.png"); herface->setPosition(ccp(230, 180));
CCMenuItemImage *startItem = CCMenuItemImage::create( "Startup_lbl_start_zh.png", "Startup_lbl_start_pressed_zh.png", this, menu_selector(GameMenu::startButtonCallback)); CC_BREAK_IF(!startItem); // Place the menu item bottom-right conner. startItem->setPosition(ccp(435, 250)); //this->addChild(skipItem); CCMenu* pMenu = CCMenu::create(startItem, NULL); pMenu->setPosition(CCPointZero); addChild(pMenu); //,,,,,,,,,,,,,,,,,,,,,, CCMenuItemImage *aboutItem = CCMenuItemImage::create( "Startup_lbl_about_zh.png", "Startup_lbl_about_pressed_zh.png", this, menu_selector(GameMenu::aboutItemCallback)); CC_BREAK_IF(!aboutItem); // Place the menu item bottom-right conner. aboutItem->setPosition(ccp(360, 150)); //this->addChild(skipItem); CCMenu* aboutItemMenu = CCMenu::create(aboutItem, NULL); aboutItemMenu->setPosition(CCPointZero); addChild(aboutItemMenu);三个按钮分别跳转到:设置,选关,关于三个界面
void GameMenu::startButtonCallback(CCObject* pSender) { CCScene *stageSelect = StageSelect::scene(); //CCDirector::sharedDirector()->replaceScene( CCTransitionJumpZoom::create(1.0f,stageSelect)); CCDirector::sharedDirector()->pushScene((CCTransitionSlideInR::create(1, stageSelect))); } void GameMenu::aboutItemCallback(CCObject* pSender) { CCScene *setting = AboutScene::scene(); CCDirector::sharedDirector()->pushScene(setting); } void GameMenu::optionItemCallback(CCObject* pSender) { CCScene *setting = SettingScene::scene(); CCDirector::sharedDirector()->pushScene(setting); }
CCSprite* herface = CCSprite::create("Startup_herface1.png"); herface->setPosition(ccp(230, 180)); addChild(herface, 2); herface->runAction(CCRepeatForever::create(CCAnimate::create(sAnimationMgr->getAnimation(biqiSmile))));
没什么特别的,比如资源统一管理:
if (isMusicSwitchOn) { musicSwitch->setDisplayFrame(sAnimationMgr->getSpritFrame(onkey)); } else { musicSwitch->setDisplayFrame(sAnimationMgr->getSpritFrame(offkey)); } if (isEffectSwitchOn) { effectSwitch->setDisplayFrame(sAnimationMgr->getSpritFrame(onkey)); } else { effectSwitch->setDisplayFrame(sAnimationMgr->getSpritFrame(offkey)); }还有这种根据按钮位置自己判断是否按下的方式,骨头觉得这样比较麻烦:
void SettingScene::ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent) { CCSize size = CCDirector::sharedDirector()->getWinSize(); CCTouch *touch = (CCTouch *)pTouches->anyObject(); CCPoint location = touch->getLocationInView(); // touch==NULL; location = CCDirector::sharedDirector()->convertToGL(location); if (location.x > size.width / 2 + 100 - 65 && location.x<size.width / 2 + 100 + 65 && location.y>210 && location.y < 270) { if (isMusicSwitchOn) { CocosDenshion::SimpleAudioEngine::sharedEngine()->stopBackgroundMusic(); musicSwitch->setDisplayFrame(sAnimationMgr->getSpritFrame(offkey)); isMusicSwitchOn = false; sGlobal->isMusicOn = false; }================================
这个类也主要是贴图,按钮,无它。
主要看看是怎样记录闯关记录的。
找了一遍,没找到加六个按钮的地方,一看资源,懂了。
那就看下回调方法吧:一个是返回按钮,一个是开始第一关按钮。
void StageSelect::backItemCallback(CCObject* pSender) { CCDirector::sharedDirector()->popScene(); } void StageSelect::stageOneItemCallback(CCObject* pSender) { CCScene* game_1_1 = Game_1_1::scene(); CCDirector::sharedDirector()->pushScene((CCTransitionSlideInR::create(0.3, game_1_1))); }================================
第一关是Game_1_1.cpp,走着
一个控制按钮层,一个tmx地图层,然后按键处理,然后update里碰撞检测,最后表现在界面上。
GameMap *gameMap=GameMap::gameMapWithTMXFile("TMX_1_5.tmx");
添加英雄和控制层:
hero=Hero::heroWithinLayer(); addChild(hero); hero->setPosition(ccp(46*3,46*9)); ControlLayer *controlLayer=ControlLayer::create(); addChild(controlLayer); controlLayer->setPosition(ccp(0,0));
if (sGlobal->isMusicOn) { CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("Audio_bgm_1.mp3",true); }主角动画:加载张大图,然后一帧帧
CCAnimation* Game_1_1::createAnimationByState(State direction) { CCTexture2D *heroTexture=CCTextureCache::sharedTextureCache()->addImage("hero.png"); // CCSpriteFrame *frame0,*frame1,*frame2,*frame3; CCSpriteFrame *frame0=CCSpriteFrame::createWithTexture(heroTexture,CCRectMake(32*0, 32*direction, 32, 32)); CCSpriteFrame *frame1=CCSpriteFrame::createWithTexture(heroTexture,CCRectMake(32*1, 32*direction, 32, 32)); CCSpriteFrame *frame2=CCSpriteFrame::createWithTexture(heroTexture,CCRectMake(32*2, 32*direction, 32, 32)); CCSpriteFrame *frame3=CCSpriteFrame::createWithTexture(heroTexture,CCRectMake(32*3, 32*direction, 32, 32)); CCArray *animFrames=CCArray::createWithCapacity(4); animFrames->addObject(frame0); animFrames->addObject(frame1); animFrames->addObject(frame2); animFrames->addObject(frame3); CCAnimation *animation =CCAnimation::createWithSpriteFrames(animFrames,0.07f); //animation->initWithAnimationFrames(animFrames,0.2f,1); animFrames->release(); return animation; }在update方法里有个产生便便和导弹并且有相关的碰撞检测检测方法。
应该是不经意间出来的东西,想起了女流的《变态人生大冒险》,还有特别的叫声。
如果主角的位置在某某某,并且还活着,那么产生一个便便对象,并且主角碰上便便就得死。
//产生便便 if (sGlobal->hero->getPositionX() + 50 >= 22 * 48 && shicount == 0 && sGlobal->hero->isDead == false) { shi = CCSprite::create("shi.png", CCRectMake(0, 0, 47, 57)); shicount++; shi->setAnchorPoint(ccp(0, 0)); shi->setPosition(ccp(22 * 48, 480)); this->addChild(shi); } if (shicount == 1) { shi->setPosition(ccp(22 * 48, shi->getPositionY() - 6)); if (shi->getPositionY() < -50) { shi->setPosition(ccp(22 * 48, 480)); } CCRect herRect = CCRectMake(sGlobal->hero->getPositionX(), sGlobal->hero->getPositionY(), 45, 45); if (herRect.intersectsRect(CCRectMake(shi->getPositionX() + 6, shi->getPositionY() + 6, 47, 57))) { sGlobal->hero->isDead = true; shicount = 0; } }
然后看下地图类:Gamemap.cpp
看看h文件里的定义:
//静态方法,用于生成GameMap实例 static GameMap* gameMapWithTMXFile(const char *tmxFile); //TiledMap和cocos2d-x坐标系相互转换的方法 CCPoint tileCoordForPosition(CCPoint position); CCPoint positionForTileCoord(CCPoint tileCoord); void hideBlockAnimate(CCPoint target); void blockMoveFinished(CCNode *sender); void removeGold(CCNode *sender); //动画移除砖块 void removeFloor(CCPoint point); protected: //TiledMap额外的初始化方法 void extraInit(); //开启各图层的纹理抗锯齿 void enableAnitiAliasForEachLayer();
GameMap::GameMap(void) { sGlobal->gameMap=this; }
像素点跟tile的索引之间的转换
//从cocos2d-x坐标转换为Tilemap坐标 CCPoint GameMap::tileCoordForPosition(CCPoint position) { int x = position.x / this->getTileSize().width; int y = (((this->getMapSize().height) * this->getTileSize().height) - position.y) / this->getTileSize().height; return ccp(x, y); } //从Tilemap坐标转换为cocos2d-x坐标 CCPoint GameMap::positionForTileCoord(CCPoint tileCoord) { CCPoint pos = ccp((tileCoord.x * this->getTileSize().width), ((this->getMapSize().height - tileCoord.y) * this->getTileSize().height)); return pos; }移走地板方法:removeFloor,骨头猜是地图块下沉方法,待确定
void GameMap::removeFloor(CCPoint point) { int maxy = sGlobal->gameMap->getMapSize().height; int heroheight = sGlobal->hero->getContentSize().height; for (int y = maxy; y >= point.y; y--) { int id = platformDynamicLayer->tileGIDAt(point); if (id) { CCSprite *sprite = platformDynamicLayer->tileAt(point); float s = (maxy - point.y)*this->getTileSize().height + heroheight;//要走的距离 CCMoveTo *ccmoveto = CCMoveTo::create(s / 9.0f, ccp(point.x*this->getTileSize().width, -heroheight)); sprite->runAction(ccmoveto); platformDynamicLayer->removeTileAt(point); } } }
看看hero.cpp英雄类里都有什么
下面是定义文件里的。
//静态方法,用于创建勇士实例 static Hero *heroWithinLayer(); //让勇士向指定方向移动一格 void move(int i); void jump(); void animateDone(CCNode *sender); bool isHeroMoving; bool isJumpDone; float hspeed; float vspeed; void setLayerEmpty(CCPoint start,int width,int height); bool isanimate; //初始化方法 bool heroInit(); bool isDead; bool isWin; //CollisionType checkCollision(CCPoint targetosition); void setViewpointCenter(CCPoint p); CollisionType checkCollisionOnly(CCPoint heroPosition); CollisionType checHeadkCollision(CCPoint heroPosition); private: //用于显示勇士形象的精灵 CCSprite *heroSprite; //临时保存目标的Tilemap坐标 CCPoint targetTileCoord; CCPoint targetPosition; float speed;
有些方法看起来有点费劲,代码懂,但不知道游戏里是什么效果。
由于骨头用模拟器运行这个游戏,控制不方便,所以一开始就死
所以,先改改代码作作弊,才能更好的理解代码。
比如,屏蔽掉Hero.cpp类中189行
//sGlobal->gameMap->removeFloor(targetTileCoord);
主角就掉不下去了。
再来看看这个方法:让英雄处于屏幕中间位置, void Hero::setViewpointCenter(CCPoint p)
这个方法很常用,在mtx地图中可能都会用的到。
void Hero::setViewpointCenter(CCPoint p) { CCSize size = CCDirector::sharedDirector()->getWinSize(); float x = MAX(p.x, size.width / 2); float y = MAX(p.y, size.height / 2); x = MIN(x, (sGlobal->gameMap->getMapSize().width*sGlobal->gameMap->getTileSize().width) - size.width / 2); y = MIN(y, (sGlobal->gameMap->getMapSize().height*sGlobal->gameMap->getTileSize().height) - size.height / 2); CCPoint actualPosition = ccp(x, y); CCPoint centerOfView = ccp(size.width / 2, size.height / 2); CCPoint viewPoint = ccpSub(centerOfView, actualPosition); int x1 = viewPoint.x; int y1 = viewPoint.y; int herox = p.x; int heroy = p.y; int thisx = this->getPositionX(); int htisy = this->getPositionY(); int mapSize = sGlobal->gameMap->getMapSize().width; int TileSize = sGlobal->gameMap->getTileSize().width; sGlobal->game_1_1->setPosition(viewPoint); int x2 = 0 - viewPoint.x; int y2 = 0 - viewPoint.y; //sGlobal->controlLayer->setPosition(ccp((float)x2,(float)y2)); //controlSprite->setPosition(ccp((float)x2,(float)y2)); }在移动方法里: void Hero::move(int i)
根据水平速度来判断向左右还是向右走,然后根据key来运行左右移动动画,
并且有一个结束回调方法,来复位hero的图片
CCAnimation *animation = sAnimationMgr->getAnimation(key); CCFiniteTimeAction* actionMoveDone = CCCallFuncN::create(this, callfuncN_selector(Hero::animateDone)); heroSprite->runAction(CCSequence::create(CCAnimate::create(animation), actionMoveDone, NULL));
void Hero::animateDone(CCNode *sender) { isanimate = false; heroSprite->setDisplayFrame(sAnimationMgr->getSpritFrame(heronormalkey)); }====================================================
再来看看这两个箱子:其中右边是隐藏的。
hero.cpp里
CollisionType Hero::checHeadkCollision(CCPoint heroPosition) { CCPoint targetTileCoord = sGlobal->gameMap->tileCoordForPosition(heroPosition); int hideblockId = sGlobal->gameMap->getHideBlockLayer()->tileGIDAt(targetTileCoord); if (hideblockId) { CCPoint startposition; if (sGlobal->gameMap->getHideBlockLayer()->tileGIDAt(ccp(targetTileCoord.x - 1, targetTileCoord.y))) { startposition = ccp(targetTileCoord.x - 1, targetTileCoord.y); } else { startposition = targetTileCoord; } int id = sGlobal->gameMap->getPlatformLayer()->tileGIDAt(ccp(0, 0)); sGlobal->gameMap->getPlatformLayer()->setTileGID(id, startposition); sGlobal->gameMap->getPlatformLayer()->setTileGID(id, ccp(startposition.x + 1, startposition.y)); sGlobal->gameMap->getPlatformLayer()->setTileGID(id, ccp(startposition.x + 1, startposition.y - 1)); sGlobal->gameMap->getPlatformLayer()->setTileGID(id, ccp(startposition.x, startposition.y - 1)); sGlobal->gameMap->hideBlockAnimate(startposition); return khideblock; } return kNone; }jump方法里:如果向上跳,速度大于0,并且检测到碰到的是khideblock:即隐藏的砖块,则停止往上跳,并且播放音效。
if (vspeed > 0) { if (checHeadkCollision(ccp(i, targetY2)) == khideblock) { vspeed = -0.4; if (sGlobal->isEffectOn) { CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("Audio_gold.mp3", false); } return; } }
====================================================
控制类:ControlLayer.cpp
首先打开触摸监听 this->setTouchEnabled(true);
void ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent); void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent); void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent); void update(float delta);然后初始化按钮
jumpSprite = CCSprite::createWithSpriteFrame(sAnimationMgr->getSpritFrame(controlJumpkey)); jumpSprite->setPosition(ccp(size.width - 70, 45)); addChild(jumpSprite);
this->scheduleUpdate();监听按键,直接在y<90的情况下,跟据x来判断按下哪个按键。
void ControlLayer::ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent) { CCTouch *touch = (CCTouch *)pTouches->anyObject(); CCPoint location = touch->getLocationInView(); // touch==NULL; location = CCDirector::sharedDirector()->convertToGL(location); //左按钮 if (location.x < 90 && location.y<90) { leftSprite->setDisplayFrame(sAnimationMgr->getSpritFrame(controlLeftPressedkey)); leftSpriteTouched = true; } else if (location.x>90 && location.x < 180 && location.y<90)//右按钮 { rightSprite->setDisplayFrame(sAnimationMgr->getSpritFrame(controlRightPressedkey)); rightSpriteTouched = true; } if (location.x>size.width - 140 && location.y < 90)//跳 { jumpSprite->setDisplayFrame(sAnimationMgr->getSpritFrame(controlJumpPressedkey)); jumpSpriteTouched = true; } }====================================================
结束层:FailedLayer.cpp
想想也就是贴图,一个返回按钮。
出现时使用movedby方法
CCFiniteTimeAction *actionMove = CCMoveBy::create(0.9f, ccp(0, -(size.height / 2 + 200)));哭脸动画使用 repertForever动画
crySprite = CCSprite::createWithSpriteFrame(sAnimationMgr->getSpritFrame(cryFramekey)); crySprite->setPosition(ccp(boxSprite->getPositionX() - 113, boxSprite->getPositionY() - 50)); crySprite->runAction(CCRepeatForever::create(CCAnimate::create(sAnimationMgr->getAnimation(heroCry))));
好了,收工
其实游戏里还有很多值得学的东西,骨头就不细细研究了,再次感谢原作者熊同学。
最近有不少“正”事,游戏相关的暂缓下。
“所有的看似毫不相关的努力都不白费,总会在某个时间发挥作用的” —— 骨头
Cocos2dx游戏开发笔记23:《奇怪的大冒险》源码学习,附下载,布布扣,bubuko.com
Cocos2dx游戏开发笔记23:《奇怪的大冒险》源码学习,附下载
原文:http://blog.csdn.net/iamlazybone/article/details/20357391