Cocos2dx學習之-----別踩白塊V1.0,cocos2dx-----v1.0
學了點COCOS2DX,一直感覺也沒什麼大意思,所以就找個教程做個小遊戲出來,反正國慶在校也沒啥事,自娛自樂。
PS;我用的版本是cocos2dx3.2
一、建立項目
cocos new -p com.donttouchwhiteblock.xuran -l cpp -d .
二、建立block類
因為別猜白塊裡面最重要的一個元素就是“塊”,所以我們要為這個元素建立一個類,然後執行個體化一些方法,以便完成遊戲中的諸多行為
首先是gameblock.h檔案
#pragma once#include <iostream>#include <cocos2d.h>USING_NS_CC; class Block:public Sprite{public:static GameBlock* CreateWithArgs(Color3B color, Size size, std::string label, float fontsize, Color3B textcolor);virtual bool initWithArgs(Color3B color, Size size, std::string label, float fontsize, Color3B textcolor);void removeblock();private:static Vector<GameBlock*> *blocks;};
標頭檔裡面定義了三個成員函數:
第一個是建立一個block,根據所擷取到的參數
第二個是初始化一個新的塊根據參數。
第三個是移除一個塊
還有建立了一個block指標類型的數組,用於儲存我們建立的block的對象。
其次是GameBlock.cpp檔案
#include "GameBlock.h"Vector<GameBlock*> *GameBlock::blocks = new Vector<GameBlock*>();GameBlock* GameBlock::CreateWithArgs(Color3B color, Size size, std::string label, float fontsize, Color3B textcolor){auto b = new GameBlock();b->initWithArgs(color, size, label, fontsize, textcolor);b->autorelease();blocks->pushBack(b);return b;}void GameBlock::removeblock(){removeFromParent();blocks->eraseObject(this); //刪除向量中特定的對象}bool GameBlock::initWithArgs(Color3B color, Size size, std::string label, float fontsize, Color3B textcolor){Sprite::init();setContentSize(size);setAnchorPoint(Point::ZERO);setTextureRect(Rect(0,0,size.width, size.height));setColor(color);auto l = Label::create();l->setString(label);l->setSystemFontSize(fontsize);l->setColor(textcolor);addChild(l);l->setPosition(size.width/2, size.height/2);return true;}
有對block數組的初始化,一個塊的初始化函數,包括各種參數的設定以及一個建立函數,一個釋放刪除函數。
三、添加開始條
建立了塊對應的類和相應的方法之後,我們就要開始真正的一步一步來做了。首先,別踩白塊的遊戲在開始的時候是有一條黃色的部分,代表著起點。那麼首先就來為我們的遊戲添加這個黃色的起點塊。
很簡單,從HelloWorldScene檔案裡面添加一個新的函數叫做AddStartLine,函數的實現很簡單,就是通過CreateWithArgs函數建立一個塊,然後添加進來即可。
bool HelloWorld::init(){ ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } visibleSize = Director::getInstance()->getVisibleSize();AddStartLine(); return true;}void HelloWorld::AddStartLine(){auto b = GameBlock::CreateWithArgs(Color3B::YELLOW, Size(visibleSize.width, visibleSize.height/8), "Start", 20, Color3B::BLACK);addChild(b);}既然開始條是這麼添加的,那麼結束條應該也是同理,再次就不再多做介紹了。
添加完開始和結束之後,我們要添加的就是普通的黑白塊。
四、添加黑白塊
首先我們要在類裡面加入一個添加黑白塊的方法叫做AddNormal
我們一般玩的別踩白塊的版本的遊戲通常一排有四個塊,黑快是隨機的出現在這四塊中的其中一塊,其餘的都是白塊。
既然是隨機的,那麼我們很自然的就要用到隨機數。
利用rand函數產生一個0-4之間的隨機數用來確定黑塊的位置。
我們要有這樣的一個概念,程式現在面對著一塊白色的畫布,我們要按著行的順序,在按照列的順序依次把每一行的白塊和黑塊都畫好。
那麼首先,我們需要建立一個塊,究竟建立的是黑快還是白塊,由上面的隨機數來確定
void HelloWorld::AddNormal(int lineindex){int blackindex = rand()%4;GameBlock *b;for(int i = 0; i < 4; i++){auto b = GameBlock::CreateWithArgs(blackindex == i?Color3B::BLACK:Color3B::WHITE, Size(visibleSize.width/4-1, visibleSize.height/4-1), "", 20, Color3B::BLACK);addChild(b);b->setPosition(i*visibleSize.width/4, lineindex*visibleSize.height/4);b->setlineindex(lineindex);}}
每排有四個塊,我們是一排一排的進行設定。傳進來的參數代表的是排數。
設定一個for迴圈,用來設定一行中的四個快。
首先建立一個塊,因為上面產生一個blackindex的隨機數,所以黑塊的位置也就相應的指定了。添加到情境中之後,就要設定這個黑塊的準確位置。
首先我們要把我們的螢幕寬度分為四部分,設定哪一部分是靠i來決定的,隨意每一塊的x座標是用i來乘上一個塊的寬度大小,縱座標呢,當然是要設定第幾行的黑白塊,所以是靠我們傳進來的要設定第幾行來決定的。
void HelloWorld::StartGame(){AddStartLine();AddNormal(1);AddNormal(2);AddNormal(3);}
最後我們直接再添加一個開始遊戲的函數,裡面調用添加這些塊的方法,在init函數裡面運行這個即可看到效果。
五、事件互動
事件互動實際上就是你對這個遊戲,或者對螢幕上的事務做出了點擊,那麼這個事務理應給一些一些反饋。
別再白塊中的時間互動其實只有一個,就是你點黑快,遊戲繼續,你點白塊遊戲結束。就是這樣。
我們以第一行為例,首先如果是要對一些操作做出反應的話,那麼我們就需要一個監聽器,來監聽動作的發生,你可以選擇監控一個控制項是否被操作了,或者是監控整個情境是否被操作了。
首先我們要建立一個觸摸事件的監聽器
auto listener = EventListenerTouchOneByOne::create();
如果這個監聽器接受到了觸摸訊號,那麼他肯定要對這個觸摸的動作做出反應,調用一個函數進行反應操作。
listener->onTouchBegan = [this](Touch *t, Event *e){log("xuran is winner");GameBlock *b;auto bs = GameBlock::getBlocks();for(auto it = bs->begin(); it != bs->end(); it++){b = *it;if(b->getlineindex() == 1 && b->getBoundingBox().containsPoint(t->getLocation())){if(b->getColor() == Color3B::BLACK){b->setColor(Color3B::GRAY);this->movedown();}else{MessageBox("遊戲失敗", "GameOver");}}}return false;};
所以在這裡用了一個閉包函數。
這個閉包函數裡面出現了一些新的函數,比如getblocks。這個函數是在GameBlock類裡面進行定義的,他的作用就是返回儲存塊的vector,因為假如我建立了第一層,那麼第一層肯定會建立四個塊,其中一個黑塊,三個白塊,這些塊裡面都會有一個lineindex的屬性,可以表明他們是第幾行的塊,這些塊也都會加入到這個數組裡面,所以在互動的時候,肯定要判斷是觸摸了白塊還是觸摸了黑塊,所以要利用迭代器的遍曆來進行確認。
listener->onTouchBegan
也表明了這是對觸碰事件的一個回呼函數。
在遍曆數組中的塊時,因為是遊戲開始,為了能夠正常的讓遊戲開始,我們首先要確定,vector中的第一個塊是第一行的,並且這個塊的範圍是包含我們的觸點的,由於這個if已經能夠確定我們的手指觸碰到了遊戲中的塊時,接下來要判斷的就是觸摸的是白塊還是黑塊。
如果是黑快,那麼按下的黑塊會變為黑色,並且把這一行下移,如果是白塊的話,那麼提示遊戲失敗。
bool HelloWorld::init(){//////////////////////////////// 1. super init firstif ( !Layer::init() ){return false;}visibleSize = Director::getInstance()->getVisibleSize();StartGame();auto listener = EventListenerTouchOneByOne::create();listener->onTouchBegan = [this](Touch *t, Event *e){log("xuran is winner");GameBlock *b;auto bs = GameBlock::getBlocks();for(auto it = bs->begin(); it != bs->end(); it++){b = *it;if(b->getlineindex() == 1 && b->getBoundingBox().containsPoint(t->getLocation())){if(b->getColor() == Color3B::BLACK){b->setColor(Color3B::GRAY);this->movedown();}else{MessageBox("遊戲失敗", "GameOver");}}}return false;};Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this); //對當前情境進行監聽return true;}
六、設計遊戲邏輯
這個遊戲的邏輯很簡單,不比2048.無非就是點擊黑快所有的塊向下移動,一直到結束,點擊白塊遊戲失敗。
所以說,主要實現的一個函數就是movedown。movedown的功能其實也很簡單,就是把所有的塊都要向下移動。首先在helloworldscene裡面的movedown函數裡面要遍曆整個塊的數組,依次讓這些塊執行下移的動作,至於這個具體的下移方法,要在塊類的內部實現,因為這畢竟是一個塊的行為。
首先,每個移動的塊的所在行數,也就是lineindex都會隨著移動的發生而減掉1.
其次便是要為每一個塊執行一個下移動作
void GameBlock::moveon(){Size visable = Director::getInstance()->getVisibleSize();this->lineindex--; //可以向下移動的行,移動之後所在的行會減去1runAction(Sequence::create(MoveTo::create(0.1f,Point(getPositionX(), lineindex*visable.height/4)),CallFunc::create([this](){if(lineindex < 0){this->removeblock();}}),NULL)); //執行一個移動的動作,把一個塊移動到對應的位置上,間隔0.1}
這裡面的runaction函數將執行一個系列動作,執行完塊的移動之後還要調用一個函數,如果這個塊被移出了螢幕,那麼他就應該被銷毀,調用removeblock函數實現。
其次,我們要設定一個遊戲的終點,也就是說遊戲過程中如果一直沒有踩到白塊,那麼究竟什麼時候停止呢。
一般來說踩50個黑快之後應該遊戲就停止了。
所以說要在helloworldscene類裡面設定一個linecount,每次添加一行的普通塊時都要加1,在所有的塊向下移動的時候也是要判斷,如果已經踩了50塊了,那麼再添加到螢幕頂部的就不應該是正常的 黑白塊而是最終結束的綠色的遊戲介面。
並且,在下移塊的函數中,如果已經添加了一個遊戲結束的綠色塊,那麼下次就不用添加了,直接下移就可以,因為一個遊戲結束塊的尺寸是整個螢幕,不用再多添加幾個。
bool HelloWorld::init(){//////////////////////////////// 1. super init firstif ( !Layer::init() ){return false;}visibleSize = Director::getInstance()->getVisibleSize();StartGame();auto listener = EventListenerTouchOneByOne::create();listener->onTouchBegan = [this](Touch *t, Event *e){//log("xuran is winner");GameBlock *b;auto bs = GameBlock::getBlocks();for(auto it = bs->begin(); it != bs->end(); it++){b = *it;if(b->getlineindex() == 1 && b->getBoundingBox().containsPoint(t->getLocation())){if(b->getColor() == Color3B::BLACK){b->setColor(Color3B::GRAY);this->movedown();}else if (b->getColor() == Color3B::GREEN){this->movedown();}else{MessageBox("遊戲失敗", "GameOver");}break;}}return false;};Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this); //對當前情境進行監聽return true;}void HelloWorld::movedown(){if(linecount < 50){AddNormal(4); //在螢幕最上面添加一行,因為設定了螢幕內一次最多存在四行的塊}else if(!showend){showend = true;AddEndLine();}auto bs = GameBlock::getBlocks();for(auto it = bs->begin(); it != bs->end(); it++){(*it)->moveon();}}
七、添加計時器
http://blog.csdn.net/xr_acmer/article/details/38706835關於計時器的使用我這裡應該寫了一些。
我是用的是scheduleupdate這個計時器,所以自己先定義一個update函數 。
之後我們要定義兩個函數,一個是啟動計時器的函數,一個是終止計時器的函數,無論是開始計時器還是終止計時器都只需要做一次,所以用一個bool變數來控制他的運行。
void HelloWorld::update(float dt){long offet = clock()-gametime;timelabel->setString(StringUtils::format("%g", ((double)offet)/1000));}void HelloWorld::starttime(){if(!timerrunning){gametime = clock(); //擷取當前系統運行這個程式的時間。scheduleUpdate(); //開始執行計時器timerrunning = true;}}void HelloWorld::endtime(){if(timerrunning){unscheduleUpdate(); //停止執行計時器timerrunning = false;}}scheduleupdate這個計時器會根據每一幀的變化調用update函數。
因為我們要在遊戲介面中添加一個計時器,這個計時器是不斷變化的,但是隨著塊的移動很可能會覆蓋掉計時器,所以我們就想能夠把遊戲和計時器分開,把他們分別存在兩個層中,這樣一來兩這就都不影響了。
gamelayer = Node::create();addChild(gamelayer);timelabel = Label::create();timelabel->setColor(Color3B::RED);timelabel->setSystemFontSize(38);timelabel->setPosition(visibleSize.width/2, visibleSize.height-50);timelabel->setString("0.000\"");addChild(timelabel);
建立一個新的層叫做gamelayer,之後再建立一個label標籤,用於時刻顯示時間,並添加到我當前的層裡,把之前所有的遊戲裡面添加開始快結束快的函數裡面的addchild都變成gamelayer->addchild(b),也就是說把遊戲當中的元素都添加到遊戲層裡面,計時器添加到當前的層裡面。
if(b->getlineindex() == 1 && b->getBoundingBox().containsPoint(t->getLocation())){if(b->getColor() == Color3B::BLACK){if(!timerrunning){this->starttime();}b->setColor(Color3B::GRAY);this->movedown();}else if (b->getColor() == Color3B::GREEN){this->movedown();this->endtime();}else{MessageBox("遊戲失敗", "GameOver");}break;}
最後根據踩的是黑快還是綠快要確定計時器的停止和開始。
PS:第一版基本就是這樣了,算是可以玩了,但是還是有很多需要改進完善的地方,以後有心情再搞
https://github.com/Harkphoenix/DontTouchWhiteBlock 源碼在這裡,我是用vs寫的
別踩白塊怎弄
1
這個遊戲對手機的配置沒有什麼要求,不過建議用大屏手機,屏大按的地方就大嘛!這樣更方便,進入遊戲以後可以選擇遊戲的模式【經典】【街機】,進入【傳統模式】試玩一下
註:傳統模式就是過多少個白塊就完成了
街機模式就是一直在遊戲的狀態,直到你趴下。
步驟閱讀
2
進入遊戲後會顯示黑塊和白塊,不要點到白塊的地方就算是過了。點擊【開始】
步驟閱讀
3
開始遊戲就會不斷顯白塊黑塊,只需要用手點擊黑塊就可以了。建議使用雙手這樣更好的練習你的左右大腦。
步驟閱讀
4
傳統模式還是比較容易過了,剛剛玩了兩把就過了。
步驟閱讀
5
街機模式玩法差不多,玩得越多分數越高。 街機模式就是一直在遊戲的狀態,直到你趴下。
別踩白塊怎弄
傳統模式:把手機放在桌上固定好,然後食指瘋點,有炫舞或勁舞遊戲經驗的,突破七秒很容易。如果是小米手機,請開啟手套模式,這要按的時候手機反應會快一點,但是這是雙刃劍,也很容易掛掉。
街機模式:前面一百一般都能過,速度穩定下來後,大家出錯都是因為按到了白塊兒而gameover,所以我們可以默數,一豎有兩個就默數“一二”,有三個默數“一二三”,只要就能盡量降低失誤率。
PS“可以兩個手指一起按,比如第四排有連續的音符,那麼右手兩個手指同時點那一排,就可以同時消掉兩個