學完cocos2dx課程的第一個項目選擇了超級瑪麗。可以說有自己的想法,簡單但是確實不簡單。
我花了一天把一份2.1版本的超級瑪麗源碼升級到了3.0,改改刪刪,參考那個源碼雖然好多不懂,但是馬虎升級成功,遊戲正常玩耍。
本着不爲把遊戲做出來而寫代碼的想法,羅列了一下這個遊戲可以使用到的知識點。數據持久化的三種方式、loading頁面、tmx地圖解析、cocosStudio場景、屏幕適配、關卡如何選擇、代碼結構的優化(各種類的抽象繼承),在基本功能出來後可以自己去設計變態關卡等。
兩天實現了loading界面、 主界面所有場景 和選關場景。
效果如下gif:
Loading場景
進度條
loadProgress = ProgressTimer::create(Sprite::create("image/loadingbar.png"));
loadProgress->setBarChangeRate(Point(1, 0));//設置進程條的變化速率
loadProgress->setType(ProgressTimer::Type::BAR);//設置進程條的類型
loadProgress->setMidpoint(Point(0, 1));//設置進度的運動方向
loadProgress->setPosition(visibleSize.width / 2, visibleSize.height / 2 );
loadProgress->setPercentage(progressPercent);
this->addChild(loadProgress);
異步加載資源
void LoadingScene::preloadResource()
{
//這裏分開寫測試, 後期如果確定是一個場景中的直接使用plist加載
std::string resouceMain = "image/mainscene/";
float count = 20;//一共加載十七張
everyAdd = 100 / count;
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"about_normal.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"about_select.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"backA.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"backB.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"background.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"bg.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"music_off.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"music_on.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"quitgame_normal.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"quitgame_select.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"Set_Music.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"Setting_n.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"setting_s.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"startgame_normal.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"startgame_select.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"wxb.jpg", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain+"zhy.jpg", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain + "sound_effect_off.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain + "sound_effect_on.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
Director::getInstance()->getTextureCache()->addImageAsync(resouceMain + "switchBg.png", CC_CALLBACK_1(LoadingScene::loadingCallback, this));
}
回調函數中這樣處理:
void LoadingScene::loadingCallback(Texture2D*)
{
progessAdd();
}
void LoadingScene::progessAdd()
{
progressPercent += everyAdd;
if (100-progressPercent <everyAdd)
{
progressPercent = 100;
auto scene = MainScene::createScene();
Director::getInstance()->replaceScene(TransitionFade::create(1, scene));
}
percentLabel->setString(StringUtils::format("%d", int(progressPercent)));//更新percentLabel的值
loadProgress->setPercentage(progressPercent);
}
在loading到100%的時候切換場景。private:
void update(float f);
std::vector<std::string> reloadResources;
int reloadNums = 0;
int curReloadNum = 0;
bool loading = true;
void imageAsyncCallback( cocos2d::Texture2D*);
cpp如下:
bool Welcome::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
/////////////////////////////
// 3. add your codes below...
auto sprite = Sprite::create("welcome.png");
// position the sprite on the center of the screen
sprite->setPosition(Point(visibleSize.width/2, visibleSize.height/2));
// add the sprite as a child to this layer
this->addChild(sprite, 0);
reloadResources = {"Themes/scene/mainscene.png","Themes/scene/mainscene_title.png"};
reloadNums = reloadResources.size();
this->scheduleUpdate();
return true;
}
void Welcome::update(float f)
{
if (loading)
{
loading = false;
if (curReloadNum >= reloadNums)
{
this->unscheduleUpdate();
auto scene = MainScene::createScene();
Director::getInstance()->replaceScene(TransitionCrossFade::create(0.6, scene));
return;
}
else
{
Director::getInstance()->getTextureCache()->addImageAsync(reloadResources[curReloadNum],
CC_CALLBACK_1(Welcome::imageAsyncCallback, this));
}
}
}
void Welcome::imageAsyncCallback(Texture2D*)
{
curReloadNum++;
loading = true;
}
這樣就做到了每次只加載一個資源,每一幀來操作一次,可以實時的進行垃圾回收。
遊戲主場景
選擇關卡
tableView = TableView::create(this, Size(spWidth, spHeight));
tableView->setDirection(ScrollView::Direction::HORIZONTAL);
tableView->setPosition(Point((winSize.width - spWidth)/2, (winSize.height-spHeight)/2));
tableView->setDelegate(this);
this->addChild(tableView);
tableView->reloadData();
void SelectLevel::tableCellTouched(TableView* table, TableViewCell* cell)
{
CCLOG("cell touched at index: %ld", cell->getIdx());
}
Size SelectLevel::tableCellSizeForIndex(TableView *table, ssize_t idx)
{
return Director::getInstance()->getVisibleSize();
}
TableViewCell* SelectLevel::tableCellAtIndex(TableView *table, ssize_t idx)
{
auto string = String::createWithFormat("%ld", idx+1);
TableViewCell *cell = table->dequeueCell();
if (!cell) {
cell = new TableViewCell();
cell->autorelease();
auto sprite = Sprite::create(StringUtils::format("image/level/select%d.jpg", idx));
sprite->setAnchorPoint(Point::ZERO);
sprite->setPosition(Point(0, 0));
cell->addChild(sprite);
auto label = Label::createWithSystemFont(string->getCString(), "Helvetica", 20.0);
label->setPosition(Point::ZERO);
label->setAnchorPoint(Point::ZERO);
label->setTag(123);
cell->addChild(label);
}
else
{
auto label = (Label*)cell->getChildByTag(123);
label->setString(string->getCString());
}
return cell;
}
ssize_t SelectLevel::numberOfCellsInTableView(TableView *table)
{
return Global::getInstance()->getTotalLevels();
}
void SelectLevel::pageViewEvent(Ref *pSender, PageViewEventType type)
{
}
千萬不要使用:void SelectLevel::pageViewEvent(Ref *pSender)
{
}
後面這種寫法編譯不報錯,但是運行的時候報錯崩潰。 pageView = PageView::create();
pageView->setSize(Size(winSize.width, winSize.height));
pageView->setPosition(Point(0,0));
for (int i = 1; i < Global::getInstance()->getTotalLevels(); i++)
{
Layout* layout = Layout::create();
layout->setSize(Size(winSize.width, winSize.height));
ImageView* imageView = ImageView::create(StringUtils::format("image/level/select%d.jpg", i));
imageView->setScale9Enabled(true);
imageView->setSize(Size(spWidth, spHeight));
imageView->setPosition(Point(layout->getSize().width / 2.0f, layout->getSize().height / 2.0f));
layout->addChild(imageView);
Text* label = Text::create(StringUtils::format("page %d", i), "fonts/Marker Felt.ttf", 30);
label->setColor(Color3B(192, 192, 192));
label->setPosition(Point(layout->getSize().width / 2.0f, layout->getSize().height / 2.0f));
layout->addChild(label);
pageView->addPage(layout);
}
pageView->addEventListenerPageView(this, pagevieweventselector(SelectLevel::pageViewEvent));
this->addChild(pageView);
auto layerr = Layer::create();
layerr->setContentSize(Size(spWidth, spHeight));
layerr->setPosition(size.width/2, size.height/2);
layerr->setZOrder(111);
this->addChild(layerr);
auto listenTouch = EventListenerTouchOneByOne::create();
listenTouch->onTouchBegan = CC_CALLBACK_2(SelectLevel::onTouchBegan, this);
//listenTouch->onTouchMoved = CC_CALLBACK_2(SelectLevel::onTouchMoved, this);
listenTouch->onTouchEnded = CC_CALLBACK_2(SelectLevel::onTouchEnded, this);
//listenTouch->onTouchCancelled = CC_CALLBACK_2(SelectLevel::onTouchCancelled, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listenTouch, layerr);
其實是做好觸摸回調:bool SelectLevel::onTouchBegan(Touch* touch, Event* event)
{
clickBeginPoint = touch->getLocation();
return true;
}
void SelectLevel::onTouchEnded(Touch* touch, Event* event)
{
int dragDistance = abs(touch->getLocation().x - clickBeginPoint.x);
//如果單擊超過5像素 切換場景
if (dragDistance < 5 )
{
Rect layerRect = Rect((winSize.width - spWidth) / 2, (winSize.height - spHeight) / 2, spWidth, spHeight);
if (layerRect.containsPoint(touch->getLocation()))
{
//要切換的關
int level = pageView->getCurPageIndex()+1;
}
}
}
到這裏PageView選擇並開始對應關卡的遊戲就搞定了。