懶骨頭(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);
2 把四張圖片載入成幀動畫,2秒一張,並且讓sprite1同事進行倆動畫:切換和飛入動畫,並且動畫結束時調用本類的animateFinished方法
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));
2 還有三個按鈕:可能因為位置原因,分三個menu容器
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);}
3 還有個變臉動畫:最最簡單的幀動畫,
CCSprite* herface = CCSprite::create("Startup_herface1.png");herface->setPosition(ccp(230, 180));addChild(herface, 2);herface->runAction(CCRepeatForever::create(CCAnimate::create(sAnimationMgr->getAnimation(biqiSmile))));
================================
看下設定頁:SettingScene.cpp
沒什麼特別的,比如資源統一管理:
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();CCPointlocation = 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;}================================
下面看下選關類:StageSelect.cpp
這個類也主要是貼圖,按鈕,無它。
主要看看是怎樣記錄闖關記錄的。
找了一遍,沒找到加六個按鈕的地方,一看資源,懂了。
那就看下回調方法吧:一個是返回按鈕,一個是開始第一關按鈕。
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");
GameMap類待會仔細看看。
添加英雄和控制層:
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();
構造方法裡,把自己的指標賦值給全域的sGlobal
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();CCPointlocation = 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))));
============================================================
源碼下載
好了,收工
其實遊戲裡還有很多值得學的東西,骨頭就不細細研究了,再次感謝原作者熊同學。
最近有不少“正”事,遊戲相關的暫緩下。
“所有的看似毫不相關的努力都不白費,總會在某個時間發揮作用的” —— 骨頭