STDIO
Tìm kiếm gần đây
    • Nội dung
    • QR Code
    • 0
    • 0
    • Sao chép

    Phát Triển Game Funny Halloween Pumpkins với Cocos2d-x - Phần 1

    Hướng dẫn làm game cụ thể bằng Cocos2d-x với code mẫu và game mẫu.
    27/11/2015
    21/09/2020
    26 phút đọc
    Phát Triển Game Funny Halloween Pumpkins với Cocos2d-x - Phần 1

    Giới thiệu

    Còn gì vui hơn khi có thể tự tay làm ra 1 game? Bài viết hướng dẫn tạo game bằng Cocos2d-x cho dịp Halloween, game với những trái bí ngô được khắc hình trông vui và ngộ nghĩnh.

    Game đã dừng phát hành trên cả AppStore và Google Play, bài viết vẫn giữ lại và chia sẻ cho độc giả có thể tự tay phát triển lại.

    Quả bí

    Kịch bản và cách chơi game

    Bắt đầu với 2 quả bí, quả đầu tiên sẽ xuất hiện trước, khi người chơi đã sẵn sàng chơi thì quả bí đầu tiên sẽ di chuyển sang bên trái, nhỏ dần và biến mất, trong khi đó màn hình bên phải sẽ xuất hiện quả bí thứ 2 di chuyển dần ra giữa màn hình và lớn lên khi di chuyển, nhiệm vụ của người chơi là trong thời gian giới hạn phải trả lời rằng quả bí thứ 2 có giống quả bí đầu tiên hay không? Game rất đơn giản nhưng để chơi được điểm cao thì khá là khó, vì điểm càng cao thì thời gian trả lời càng ngắn.

    Download data và source code của game

    Hiện thực

    Trước khi bắt đầu làm game với Cocos2d-x, cần chuẩn bị 1 số công cụ, phần mềm:

    • Cocos2d-x v3.4 trở lên.
    • Data: đính kèm trong 2 file Part1.zip và Part2.zip bên trên, chia làm 2 phần là data cho menu và data cho gameplay.
    • Cocos Studio: công cụ này để tạo ra file *.plist từ data ở trên, tải từ trang chủ của cocos2d-x, cũng có hướng dẫn cách để tạo ra file *.plist từ data là những file ảnh PNG. Lưu ý là khi tạo file *.plist thì tối data kich thước nên 2048x2048 hoặc nhỏ hơn, trong bài viết sử dụng 2048x2048.
    • Visual Studio 2019 hoặc lớn hơn.

    Game này sử dụng data cho màn hình 720x1280, nó sẽ tự động phóng to thu nhỏ cho tất cả các màn hình.

    Command để tạo project và build project

    cocos pumpkins -p com.gamnvl.pumpkins -l cpp -d E:\
    cocos run -d E:\pumpkins -p android --a18

    Config project

    Game có tổng cộng 4 state:

    • StateLoad: màn hình nạp game.
    • StateMainMenu: màn hình main menu.
    • StateGamePlay: màn hình chơi game.
    • StateEndGame: màn hình khi kết thúc game.

    Trong thư mục chính tạo project xong, vào thư mục Classes và dựa vào 2 file HellowordSceen.h và HellowordSceen.cpp, sao chép thành 4 cặp file mới với tên mới lần lượt là tên là:

    • StateLoad.h và StateLoad.cpp
    • StateMainMenu.h và StateMainMenu.cpp
    • StateGamePlay.h và StateGamePlay.cpp
    • StateEndGame.h và StateEndGame.cpp

    Bao gồm sửa tên class trong các file trùng với tên file.

    Tạo file config.h để khai bao cho các cấu hình, biến toàn cục cần thiết trong game, có thể xem tham khảo trong mã nguồn đính kèm của bài viết này. Nội dung trong file config không quá phức tạp, bao gồm: khai báo đường dẫn, tọa độ, tên file, ...

    Khai báo các file mới vừa thêm vào project, mở file CMakeLists.txt ngoài thư mục chính của project và tìm theo tên "AppDelegate.cpp", cập nhật thêm như sau:

    Classes/AppDelegate.cpp
    Classes/StateLoad.cpp
    Classes/StateMainMenu.cpp
    Classes/StateGamePlay.cpp
    Classes/StateEndGame.cpp
    

    Bên dưới là khai báo những file .h, cập nhật như sau:

     Classes/AppDelegate.h
     Classes/StateLoad.h
     Classes/StateMainMenu.h
     Classes/StateGamePlay.h
     Classes/StateEndGame.h
     Classes/NativeAndroidHelper.h
     Classes/config.h
    

    Proejct này có thể chạy trên Win32 và thiết bị Android, do đó cần khai báo thêm ở file "Pumpkins\proj.android\jni\Android.mk" và "Pumpkins\proj.win32\Pumpkins.vcxproj", cập nhật như sau:

    ../../Classes/AppDelegate.cpp \
    ../../Classes/StateLoad.cpp	\
    ../../Classes/StateMainMenu.cpp	\
    ../../Classes/StateGamePlay.cpp	\
    ../../Classes/StateEndGame.cpp	\
    <ClCompile Include="..\Classes\AppDelegate.cpp" />
        <ClCompile Include="..\Classes\StateLoad.cpp" />
        <ClCompile Include="..\Classes\StateMainMenu.cpp" />
        <ClCompile Include="..\Classes\StateGamePlay.cpp" />
        <ClCompile Include="..\Classes\StateEndGame.cpp" />
        <ClCompile Include="main.cpp" />
     </ItemGroup>
     <ItemGroup>
        <ClInclude Include="..\Classes\AppDelegate.h" />
        <ClInclude Include="..\Classes\StateLoad.h" />
        <ClInclude Include="..\Classes\StateMainMenu.h" />
        <ClInclude Include="..\Classes\StateGamePlay.h" />
        <ClInclude Include="..\Classes\StateEndGame.h" />
        <ClInclude Include="..\Classes\config.h" />

    Sau đó mở file AppDelegate.cpp và cập nhật dòng include Helloword.h thành StateLoad.h, config cho chạy game với màn hình 720x1280 và tự động "fit" khi chạy trên màn hình lớn hơn, mặc định FPS là 60, có thể cập nhật lại 40 để FPS ổn định và ít tốn Pin hơn.

    #include "AppDelegate.h"
    #include "StateLoad.h"
    #include "config.h"
    USING_NS_CC;
    
    static cocos2d::Size designResolutionSize = cocos2d::Size(480, 320);
    static cocos2d::Size smallResolutionSize = cocos2d::Size(480, 320);
    static cocos2d::Size mediumResolutionSize = cocos2d::Size(1024, 768);
    static cocos2d::Size largeResolutionSize = cocos2d::Size(2048, 1536);
    
    AppDelegate::AppDelegate() {
    
    }
    
    AppDelegate::~AppDelegate() 
    {
    }
    
    //if you want a different context,just modify the value of glContextAttrs
    //it will takes effect on all platforms
    void AppDelegate::initGLContextAttrs()
    {
        //set OpenGL context attributions,now can only set six attributions:
        //red,green,blue,alpha,depth,stencil
        GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};
    
        GLView::setGLContextAttrs(glContextAttrs);
    }
    
    // If you want to use packages manager to install more packages, 
    // don't modify or remove this function
    static int register_all_packages()
    {
        return 0; //flag for packages manager
    }
    
    bool AppDelegate::applicationDidFinishLaunching() {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        sdkbox::PluginFacebook::init();
    	sdkbox::PluginChartboost::init();
    
    #endif
        // initialize director
        auto director = Director::getInstance();
        auto glview = director->getOpenGLView();
        if(!glview) {
            glview = GLViewImpl::create("Funny Halloween Pumpkins");
    		glview->setFrameSize(SCREEN_WIDTH_DESIGN / 2, SCREEN_HEIGHT_DESIGN / 2); //SCREEN_WIDTH_DESIGN , SCREEN_HEIGHT_DESIGN  trong config.h
            director->setOpenGLView(glview);
        }
    
        // turn on display FPS
        director->setDisplayStats(false);
    
        // set FPS. the default value is 1.0/60 if you don't call this
        director->setAnimationInterval(1.0 / 40);
    
        // Set the design resolution
    	glview->setDesignResolutionSize(SCREEN_WIDTH_DESIGN, SCREEN_HEIGHT_DESIGN, ResolutionPolicy::EXACT_FIT);
        Size frameSize = glview->getFrameSize();
    
        register_all_packages();
    
        // create a scene. it's an autorelease object
        auto scene = StateLoad::createScene();
    
        // run
        director->runWithScene(scene);
    
        return true;
    }
    
    // This function will be called when the app is inactive. When comes a phone call,it's be invoked too
    void AppDelegate::applicationDidEnterBackground() {
        Director::getInstance()->stopAnimation();
    }
    
    // This function will be called when the app is active again
    void AppDelegate::applicationWillEnterForeground() {
        Director::getInstance()->startAnimation();
    }

    StateLoad

    logo

    Hiện thực phần StateLoad của game, chỉ cần hiển thị logo và load những data cần thiết để chạy game, trong hàm dưới sử dụng hàm update(float dt), hàm này được gọi mỗi frame của game và 1 biến để đếm thời gian load data. Mở file StateLoad.h và thêm vào như sau:

    #ifndef __STATE_LOAD_H__H__
    #define __STATE_LOAD_H__H__
    
    #include "cocos2d.h"
    
    class StateLoad : public cocos2d::Layer
    {
    public:
        // there's no 'id' in cpp, so we recommend returning the class instance pointer
        static cocos2d::Scene* createScene();
    
        // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
        virtual bool init();
        void update(float dt); //overwrite hàm cocos
        
        CREATE_FUNC(StateLoad);
    	
    private:
    	int m_loadingStep; // dem thoi gian de chuyen sang state main menu
    };
    
    #endif // __STATE_LOAD_H__H__

    Mở file StateLoad.cpp, trong hàm init(), gán m_loadingStep = 0 và cho phép hàm update() chạy bằng cách gọi this->scheduleUpdate().

    Trong nội dung của hàm update(), load tuần tự như sau.

    • Frame đầu tiên load data của menu từ file Menu0.plist được đặt tên là NamePlistMenu trong config.h
    • Frame thứ 2 và 3 load data của gameplay từ file Pumpkins0.plist, Pumpkins1.plist
    • Frame thứ 4 vẽ background và logo lên màn hình khoản 3s.
    • Frame 120 là 3s vì 40 frame là 1s, thì chuyển sang state main menu.

    Đoạn code thực hiện những việc trên sẽ như sau:

    #include "StateLoad.h"
    #include "StateMainMenu.h"
    #include "config.h"
    
    USING_NS_CC;
    
    Scene* StateLoad::createScene()
    {
    	// 'scene' is an autorelease object
    	auto scene = Scene::createWithPhysics();
    	// scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    	scene->getPhysicsWorld()->setGravity(Vect(0.0f, 0.0f));
        
    	// 'layer' is an autorelease object
    	auto layer = StateLoad::create();
    
    	// add layer as a child to scene
    	scene->addChild(layer);
    
    	// return the scene
    	return scene;
    }
    
    // on "init" you need to initialize your instance
    bool StateLoad::init()
    {
    	//////////////////////////////
    	// 1. super init first
    	if ( !Layer::init() )
    	{
    		return false;
    	}
      
    	Size winSize = Director::getInstance()->getWinSize();
    	Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    	m_loadingStep = 0;
    	this->scheduleUpdate();
    
    	return true;
    }
    
    void StateLoad::update(float dt)
    {
    	LayerColor *BG;
    	Sprite *logo;
    	switch(m_loadingStep)
    	{
    		case 0:
    			SpriteFrameCache::getInstance()->addSpriteFramesWithFile(NamePlistMenu);
    			break;
    		case 1:
    			SpriteFrameCache::getInstance()->addSpriteFramesWithFile(NamePlistPumpkins0);
    			break;
    		case 2:
    			SpriteFrameCache::getInstance()->addSpriteFramesWithFile(NamePlistPumpkins1);
    			break;
    		case 3:
    			BG = LayerColor::create(blueColor4b);
    			this->addChild(BG, zUi);
    			logo = Sprite::createWithSpriteFrameName(NameLogo);
    			logo->setPosition(SCREEN_WIDTH_DESIGN / 2, SCREEN_HEIGHT_DESIGN / 2);
    			this->addChild(logo, zUi);
    			break;
    
    		case 120:
    			Director::getInstance()->replaceScene(StateMainMenu::createScene());
    			this->unscheduleUpdate();
    			break;
    	}
    
    	m_loadingStep++;
    }

    StateMainMenu

    Main menu

    Trong StateMainMenu gồm có:

    • 1 background.
    • 20 quả bí ngô đặt ở vị trí ngẫu nhiên với độ to nhỏ khác nhau và xoay theo mọi hướng.
    • Title game Funny Halloween Pumpkins.
    • Bên dưới là nút Play game.
    • Bật tắt sound, high score và button rate.

    StateMainMenu.h

    Trong file này có các hàm như sau.

    #ifndef __STATE_MAINMENU_H__
    #define __STATE_MAINMENU_H__
    
    #include "cocos2d.h"
    #include "ui/CocosGUI.h"
    
    USING_NS_CC; 
    
    class StateMainMenu : public cocos2d::Layer
    {
    public:
    	// there's no 'id' in cpp, so we recommend returning the class instance pointer
    	static cocos2d::Scene* createScene();
    
    	// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    	virtual bool init();
    
    	void drawRandomPumpkins(); //Vẽ những trái bí ngô ở những tọa độ ngẫu nhiên khi vào state main menu
    	void drawMenu();           //Vẽ và xử lý các menu như play game, tắt mở âm thanh, xem điểm cao nhất, đánh giá game.
    	void gotoGamePlay();       //Chuyển tới state game play.
    
    	void update(float dt);     //Update mỗi frame để tính toán xử lý menu.
    	void UpdateAdsBanner();    //Xử lý hiện và ẩn banner quảng cáo.
    	void showInterstitial();   //Xử lý hiện hình quảng cáo full màn hình
    	int effectTitle;
    
    	void updateSoundBT();      //Update để vẽ thay đổi của nút âm thanh khi thay đổi trạng thái
    	void RefeshSound();        //Vẽ lại trạng thái của nút âm thanh
    	bool soundChange;
    
    	void drawHighScore();      //Vẽ điểm số cao nhất
    	bool m_showHighScore;      /Giá trị điểm số cao nhất
    	int tagHighScore;          //Lưu giá trị tag của đối tượng để xóa đối tượng khỏi màn hình khi không cần
    	void shareFaceBook();      //Chia sẽ điểm số cao nhất lên facebook
    
    	void RateGame();           //Đánh giá chất lượng game
    
    	void showDialogExit();     //Thông báo xác nhận thoát game
    	void YesConfirm();         //Chấp nhận thông báo
    	void NoConfirm();          //Từ chối thông báo
    	bool m_DialogConfirm;
    	int tagDialogConfirm;
    
    	void onKeyReleased(EventKeyboard::KeyCode keyCode, cocos2d::Event *event);   //Bắt sự kiện nhấn phím
    	void onTouchEnded(cocos2d::Touch* touches, cocos2d::Event* event);           //Bắt sự kiện kết thúc chạm man hình
    	void onTouchMoved(cocos2d::Touch* touches, cocos2d::Event* event);           // Bắt sự kiện di chuyển trên màn hình
    	bool onTouchBegan(cocos2d::Touch* touches, cocos2d::Event* event);           //Bắt sự kiện chạm màn hình
    
    	// implement the "static create()" method manually
    	CREATE_FUNC(StateMainMenu);
    
    private:
    	int countToShowBanner;
    };
    
    #endif // __STATE_MAINMENU_H__

    StateMainMenu.cpp

    Có 2 bước để làm 1 game hoàn thiện là:

    1. Tạo giao diện cho game.
    2. Gắn xử lý sự kiện cho game.

    1. Tạo giao diện cho game

    Trước tiên trong hàm init() của State này, vẽ background cho game, đặt tại vị trí tâm của màn hình bằng đoạn code sau với NameBG_MM, SCREEN_WIDTH_DESIGN, SCREEN_HEIGHT_DESIGN đã được khai báo trong config.h, có thể tham khảo thêm trong config.h được cung cấp trong mã đính kèm của bài.

    auto SpriteBG = Sprite::createWithSpriteFrameName(NameBG_MM);
    SpriteBG->setPosition(SCREEN_WIDTH_DESIGN/2, SCREEN_HEIGHT_DESIGN/2);
    this->addChild(SpriteBG, zBG);   

    Tiếp tục vẽ những quả bí ngô ở các tọa độ ngẫu nhiên trên màn hình, đặt trong hàm drawRandomPumpkins() và cũng gọi hàm này trong init(), hàm drawRandomPumpkins() như sau:

    void StateMainMenu::drawRandomPumpkins()
    {
    	Label *copyright = Label::createWithTTF(arial_font24, "@ gamenvl");    // tao text voi font 24 co noi dung @ gamenvl
    	copyright->setTextColor(whiteColor4B);                                 // set mau cho text
    	copyright->setPosition(posTitleMM.x, 20);                              // set vi tri ve ra man hinh 
    	this->addChild(copyright, zTitile);                                    // zTitile: gia tri zorder cao se ve de len doi tuong co zorder thap
    
    	for (int i = 0; i < 20; i++)                      // ve 20 trai bi ngo o cac vi tri ngau nhien trong man hinh
    	{
    		int id = i;// random(i * 2, i * 2 + 1);
    		int posX = (i%2==0 ? random(0, 360) : random(360, 720));
    		int posY = random(i/2 * 128 + (i/2>0 ?50:0), i/2 * 128 + 128);
    		int rotate = random(0,255);
    		float scaleEffect = 0.5 + sqrt((600-posX)*(600-posX) + (450-posY)*(450-posY)) * 0.5 / 800;
    		auto SpritePumpkin = Sprite::createWithSpriteFrameName(NamePumpkins[id]);     // tao trai bi ngo voi ten
    		SpritePumpkin->setPosition(posX, posY);
    		SpritePumpkin->setRotation(rotate);                               // xoay doi tuong trong gia tri 0->360
    		SpritePumpkin->setScale(0.25*scaleEffect);                        // scale doi tuong trong gia tri tu 0.5-1
    		SpritePumpkin->setOpacity(150);                                   // set do xuyen suot cua doi tuong (0-255)
    		this->addChild(SpritePumpkin, zBG);                               // dua ra man hinh
    	}
    }

    Cuối cùng vẽ menu trong hàm drawMenu() và gọi hàm này trong init() của State như sau:

    void StateMainMenu::drawMenu()
    {
    	auto btPlay = MenuItemImage::create();                                               // tao nut play game
    	btPlay->setNormalImage(Sprite::createWithSpriteFrameName(NamebtPlay));               // set anh binh thuong
    	btPlay->setSelectedImage(Sprite::createWithSpriteFrameName(NamebtPlayPress));        // set anh khi nhap vao
    	btPlay->setCallback(CC_CALLBACK_0(StateMainMenu::gotoGamePlay, this));               // goi toi ham gotoGamePlay khi nhap vao nut play
    	btPlay->setPosition(posbtPlayMM);                                                    // set vi tri dat nut play
    
    	auto btHighScoreBG = Sprite::createWithSpriteFrameName(NamebtBG);                    // tao BG nut diem cao
    	btHighScoreBG->setPosition(posbtHighScore);
    	btHighScoreBG->setOpacity(200);
    	this->addChild(btHighScoreBG, zUi);
    
    	auto btHighScore = MenuItemImage::create();                                          // tao nut diem cao
    	btHighScore->setNormalImage(Sprite::createWithSpriteFrameName(NamebtHighScore));
    	btHighScore->setSelectedImage(Sprite::createWithSpriteFrameName(NamebtHighScore));
    	btHighScore->setCallback(CC_CALLBACK_0(StateMainMenu::drawHighScore, this));         // goi toi ham ve diem cao khi nhan nut
    	btHighScore->setPosition(posbtHighScore);
    
    	auto btRateBG = Sprite::createWithSpriteFrameName(NamebtBG);                        // tuong tu nhu tren
    	btRateBG->setPosition(posbtRate);
    	btRateBG->setOpacity(200);
    	this->addChild(btRateBG, zUi);
    
    	auto btRate = MenuItemImage::create();                                             // tuong tu nhu tren
    	btRate->setNormalImage(Sprite::createWithSpriteFrameName(NamebtRate));
    	btRate->setSelectedImage(Sprite::createWithSpriteFrameName(NamebtRate));
    	btRate->setCallback(CC_CALLBACK_0(StateMainMenu::RateGame, this));
    	btRate->setPosition(posbtRate);
    
    	auto menu = Menu::create(btPlay, btHighScore, btRate, NULL);                    // tao 1 menu gom cac nut play, diem cao va nut danh gia game
    	menu->setPosition(Point::ZERO);                                                 // Luu y phai dat tai dim 0 vi da setpos o tren doi tuong roi
    	this->addChild(menu, zUi);
    }

    Phần giao diện đến đây coi như đã xong, chỉ còn thiếu nút Sound, vì nút Sound thay đổi liên tục nên vẽ trong lúc xử lý hàm update.

    2. Gắn xử lý sự kiện cho game

    Để trong game nhận được sự kiện nhấn phím thì cần active nó trong hàm init():

    this->setKeypadEnabled(true);
    

    Để game chạy hàm update() liên tục mỗi frame và nhận được các sự kiện chạm màn hình thì active nó trong hàm init().

    this->scheduleUpdate();
    
    auto dispatcher = Director::getInstance()->getEventDispatcher();
    auto listener = EventListenerTouchOneByOne::create();
    listener->setSwallowTouches(true);
    
    listener->onTouchBegan = CC_CALLBACK_2(StateMainMenu::onTouchBegan, this);
    listener->onTouchMoved = CC_CALLBACK_2(StateMainMenu::onTouchMoved, this);
    listener->onTouchEnded = CC_CALLBACK_2(StateMainMenu::onTouchEnded, this);
    
    dispatcher->addEventListenerWithSceneGraphPriority(listener, this);

    Xử lý cho nút Play. Khi nãy khi tôi tạo nút play có gán điều kiện nếu nhấn nút play thì nó sẽ gọi tới hàm gotoGamePlay(), và hàm này sẽ xử lý như sau:

    void StateMainMenu::gotoGamePlay()
    {
    	if (m_DialogConfirm || m_showHighScore)                      // 2 bien nay la dieu kien khi co thong bao thi se khong xu ly nut play
    		return;
    
    	auto audio = SimpleAudioEngine::getInstance();             // nhan bien am tham
    	audio->stopBackgroundMusic(true);                          // tat am thanh truoc khi chuyen state
    	Director::getInstance()->replaceScene(StateGamePlay::createScene());  // chuyen den state gameplay
    }

    Xử lý cho nút high score. Khi tạo nút Híghcore tôi có gán điều kiện khi nhấn vào nút thì sẽ gọi hàm drawHighScore(), hàm xử lý như sau:

    void StateMainMenu::drawHighScore()
    {
    	if (m_DialogConfirm || m_showHighScore)  // dieu kien khi dang co thong bao thi thoat khoi ham
    		return;
    
    	m_showHighScore = true;
    	while (tagHighScore > 2000)              // Luu y: nho set tagHighScore = 2000 trong init()
    	{
    		tagHighScore--;
    		this->removeChildByTag(tagHighScore); //tagHighScore de remove thong bao trc khi ve nhu vay se khong bị duplicate khi ve
    	}
    	auto userdefault = UserDefault::sharedUserDefault(); // nho #include <SimpleAudioEngine.h>  using namespace CocosDenshion; de su dung duoc
    
    	int highScore = userdefault->getIntegerForKey(HIGH_SCORE); // lay gia tri diem cao luu trong bo nho
    
    	auto bgHighScore = Sprite::createWithSpriteFrameName(NameHighScoreBG); // tao BG de ve diem len
    	bgHighScore->setPosition(posHighScoreMM);
    	bgHighScore->setTag(tagHighScore++);            // Luu y tagHighScore de quan ly tat ca cac doi tuong trong bang diem
    	this->addChild(bgHighScore, zUi);
    
    	auto textScore = Label::createWithTTF(Pumpkins_font60, "HIGH SCORE");   // tao text 
    	textScore->setPosition(bgHighScore->getPosition().x, bgHighScore->getPosition().y + bgHighScore->getContentSize().height / 2 - 100);
    	textScore->setTextColor(pinkColor4B);
    	textScore->setTag(tagHighScore++);
    	this->addChild(textScore, zUi);
    
    	auto strB = CCString::createWithFormat("%i", highScore);          // tao text mang gia tri diem cao
    	auto Bscore = Label::createWithTTF(Pumpkins_font60, strB->getCString());
    	Bscore->setPosition(bgHighScore->getPosition().x, bgHighScore->getPosition().y - 100);
    	Bscore->setTextColor(pinkColor4B);
    	Bscore->setTag(tagHighScore++);
    	this->addChild(Bscore, zUi);
    
    	auto face_btn = Sprite::createWithSpriteFrameName(NamebtBG);
    	face_btn->setPosition(bgHighScore->getPosition().x, bgHighScore->getPosition().y - bgHighScore->getContentSize().height / 2 - face_btn->getContentSize().height / 2);
    	//face_btn->setOpacity(100);
    	face_btn->setTag(tagHighScore++);
    	this->addChild(face_btn, zUi);
    
    	auto face = MenuItemImage::create();                                    // tao nut de share diem len facebook
    	face->setNormalImage(Sprite::createWithSpriteFrameName(NamebtFace));   
    	face->setSelectedImage(Sprite::createWithSpriteFrameName(NamebtFace));
    	face->setPosition(face_btn->getPosition());
    	face->setCallback(CC_CALLBACK_0(StateMainMenu::shareFaceBook, this));  // goi den ham shareFacebook khi nhan nut
    
    	auto menu = Menu::create(face, NULL);               // tao menu chua nut face
    
    	menu->setPosition(Point::ZERO);
    	menu->setTag(tagHighScore++);
    	this->addChild(menu, zUi);
    }

    Thế là xong nút HighScore nhưng trong nút HighScore lại có thêm nút shareFacebook, nên giờ chúng ta sẽ đi đến xử lý nut shareFacebook, hàm này sẽ như sau:

    void StateMainMenu::shareFaceBook()
    {
    	auto userdefault = UserDefault::sharedUserDefault();
    
    	int highScore = userdefault->getIntegerForKey(HIGH_SCORE);         // lay gia tri diem cao
    
    	auto tempText = CCString::createWithFormat("My best score is %i. Let try it to get best score with me!", highScore);
    
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)                       // Luu y phan này chi danh cho android, chúng ta se tiep can o cuoi bai nhe
    	sdkbox::PluginFacebook::requestReadPermissions({ FB_PERM_READ_PUBLIC_PROFILE, FB_PERM_READ_USER_FRIENDS });
    	sdkbox::PluginFacebook::requestPublishPermissions({ FB_PERM_PUBLISH_POST });
    	FBShareInfo info;
    	info.type = FB_LINK;
    	info.link = "https://play.google.com/store/apps/details?id=com.gamenvl.pumpkins";
    	info.title = "Funny Halloween Pumpkins";
    
    	info.text = tempText->getCString();
    	info.image = "http://i.imgur.com/vBwYKHH.png";
    	PluginFacebook::dialog(info);
    #endif
    }

    Xử lý cho nút Rate game. Ở trên khi tôi tạo nút Rate game thì có gán điều kiện sẽ gọi tới hàm RateGame() khi nhấn vào, và hàm sẽ được viết như sau:

    void StateMainMenu::RateGame()
    {
    	if (m_DialogConfirm || m_showHighScore)
    		return;
    
    	std::string link = std::string("https://play.google.com/store/apps/details?id=com.gamenvl.pumpkins");   // link game tren google play
    	Application::getInstance()->openURL(link); // Khi up game cac ban len chi thay the cai ten pack "com.gamenvl.pumpkins"
    }

    Còn lại nút sound để tắt mở sound, xử lý nút này trong hàm update của game như đã đề cập ở trên.

    void StateMainMenu::update(float dt)
    {
    	effectTitle++;           // gia tri default set = 0 trong ham Init()
    	float scale = 1.0f;
    	if (effectTitle > 80)    // doan code sau tinh toan ti le scale cua title game 40frame = 1s
    	{
    		scale = 1.03f;
    	}
    	if (effectTitle > 85)
    	{
    		scale = 1.06f;
    	}
    	if (effectTitle > 90)
    	{
    		scale = 1.09f;
    	}
    	if (effectTitle > 95)
    	{
    		scale = 1.12f;
    	}
    	if (effectTitle > 100)
    	{
    		scale = 1.0f;
    		effectTitle = 0;
    	}
    
    	this->removeChildByTag(tagTitleMM1);
    	this->removeChildByTag(tagTitleMM2);
    
    	auto titleMM = Label::createWithTTF(Pumpkins_font90, "Funny Halloween");
    	titleMM->setColor(yellowColor3B);
    	titleMM->setTag(tagTitleMM1);
    	titleMM->setScale(scale);
    	titleMM->setPosition(posTitleMM);
    	this->addChild(titleMM, zTitile);
    	auto titleMM1 = Label::createWithTTF(Pumpkins_font90, "Pumpkins");
    	titleMM1->setColor(yellowColor3B);
    	titleMM1->setScale(scale);
    	titleMM1->setTag(tagTitleMM2);
    	titleMM1->setPosition(posTitleMM.x, posTitleMM.y - 100);
    	this->addChild(titleMM1, zTitile);
    
    	if (soundChange)
    		updateSoundBT();    // Ham xu ly sound, soundChange nen default la true trong Init()
    	
    	UpdateAdsBanner();    // cai nay quang cao toi se noi toi o cuoi game nhe
    }

    Như vậy trong hàm update gọi hàm updateSoundBT(), xử lý hàm này như sau:

    void StateMainMenu::updateSoundBT()
    {
    	auto userdefault = UserDefault::sharedUserDefault();
    	int soundEnable = userdefault->getIntegerForKey(SoundEnable);
    
    	this->removeChildByTag(tagbtSoundBG, true);
    	this->removeChildByTag(tagbtSound, true);
    
    	auto btSoundBG = Sprite::createWithSpriteFrameName(NamebtBG);
    	btSoundBG->setPosition(posbtSound);
    	btSoundBG->setOpacity(200);
    	btSoundBG->setTag(tagbtSoundBG);
    	this->addChild(btSoundBG,zUi);
    
    	auto btSound = MenuItemImage::create();                                                // tao nut sound
    	if (soundEnable == soundOn)
    	{
    		btSound->setNormalImage(Sprite::createWithSpriteFrameName(NamebtSoundOn));        // set anh binh thuong neu sound on
    		btSound->setSelectedImage(Sprite::createWithSpriteFrameName(NamebtSoundOff));	  // set anh tat sound neu chon
    	}
    	else
    	{
    		btSound->setNormalImage(Sprite::createWithSpriteFrameName(NamebtSoundOff));     // nguoc lai
    		btSound->setSelectedImage(Sprite::createWithSpriteFrameName(NamebtSoundOn));
    	}
    	btSound->setPosition(posbtSound);
    	btSound->setCallback(CC_CALLBACK_0(StateMainMenu::RefeshSound, this));             // goi toi ham RefeshSound khi nhap vap
    
    	auto menu = Menu::create(btSound, NULL);
    	menu->setPosition(Point::ZERO);
    	menu->setTag(tagbtSound);
    	this->addChild(menu, zUi);
    
    	soundChange = false;
    }

    Trong hàm updateSoundBT() lại gọi tới hàm RefeshSound(), vào hàm này tiếp tục.

    void StateMainMenu::RefeshSound()
    {
    	if (m_DialogConfirm || m_showHighScore)
    		return;
    
    	auto userdefault = UserDefault::sharedUserDefault();
    	int soundEnable = userdefault->getIntegerForKey(SoundEnable);
    	if (soundEnable == soundOn)
    	{
    		auto audio = SimpleAudioEngine::getInstance();
    		audio->stopBackgroundMusic();
    
    		userdefault->setIntegerForKey(SoundEnable, soundOff);                 // Luu trang thai sound on or off vao bo nho
    		userdefault->flush();
    	}
    	else
    	{
    		auto audio = SimpleAudioEngine::getInstance();
    		audio->playBackgroundMusic("sounds/BG_Pumpkins.wav", true);
    
    		userdefault->setIntegerForKey(SoundEnable, soundOn);                // Luu trang thai sound on or off vao bo nho
    		userdefault->flush();
    	}
    
    	soundChange = true;
    }

    Nút sound được tách làm 2 hàm vì 1 hàm để vẽ, hàm còn lại để xử lý thay đổi khi nhấn phím.

    StateGamePlay

    Gameplay

    StateGamePlay.h

    #ifndef __STATE_GAMEPLAY__H__
    #define __STATE_GAMEPLAY__H__
    #include "cocos2d.h"
    
    USING_NS_CC;
    
    class StateGamePlay : public cocos2d::Layer
    {
    public:
    	// there's no 'id' in cpp, so we recommend returning the class instance pointer
    	static cocos2d::Scene* createScene();
    
    	// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    	virtual bool init();
        
    	void update(float dt);
    	void drawClock(float dt);                            // ve dong ho tinh thoi gian luot choi
    	void drawScore(float dt);                            // ve diem so hien tai 
    	void drawPumpkins();                                 // ve trai bi ngo
    	void drawTextGP();                                   // ve chu thich ra man hinh huong dan nguoi choi
    	void drawAnswer();                                   // ve cau tra loi cho nguoi dung chon
    	void AnswerNo();                                     // xu ly nguoi dung chon no
    	void AnswerYes();                                    // xu ly khi nguoi dung chon yes
    	void actionFinish(cocos2d::Node * sender);           // remove doi tuong ra khoi man hinh khi thuc hien xong hanh dong
    	void checkAnswer(bool ans);                          // kiem tra cau tra loi, neu dung se den cau tiep theo, sai se ket thuc game
    	void gotoEndGame();                                  // chuyen sang state end game
    	void UpdateAdsBanner();                              // quang cao banner
    	void showDialogBonusTime();                          // thong bao thoi gian them khi xem video
    	void YesConfirm();                                   // chap nhan thong bao
    	void NoConfirm();                                    // khong chap nhan thong bao
    
    	void onKeyReleased(EventKeyboard::KeyCode keyCode, cocos2d::Event *event);
    	void onTouchEnded(cocos2d::Touch* touches, cocos2d::Event* event);
    	void onTouchMoved(cocos2d::Touch* touches, cocos2d::Event* event);
    	bool onTouchBegan(cocos2d::Touch* touches, cocos2d::Event* event);
    
    	// implement the "static create()" method manually
    	CREATE_FUNC(StateGamePlay);
    
    private:
    	int countToShowBanner;
    
    	ProgressTimer *m_timer;
    	float m_currentTime;
    	bool gameReady;
    	bool gameTimeOut;
    	bool gameAnswer;
    	int Score;
    	int scaleTextBegin;
    	int PumpkinCurrentID;
    	int PumpkinPreviousID;
    	Sprite *PumpkinCurrent;                           // sprite trai bi ngo hien hanh
    	Sprite *PumpkinPrevious;                          // sprite trai bi ngo truoc
    
    	float maxTime ;                              
    	float timeTotal;
    
    	bool gameOver;
    	int tagDialogBonus;
    	bool m_DialogBonusShow = true;
    };
    
    #endif // __STATE_GAMEPLAY__H__

    StateGamePlay.cpp

    Trong init() của StateGamePlay, để game nhận được key và touch thì cần active hàm update giốnng bên trên:

    this->scheduleUpdate();             // active ham update() chay lien tuc moi frame
    
    	this->setKeypadEnabled(true);     // active key pad bat su kien khi nhan key
    
    	auto dispatcher = Director::getInstance()->getEventDispatcher();     // khai bao bat su kien touch
    	auto listener = EventListenerTouchOneByOne::create();
    	listener->setSwallowTouches(true);
    
    	listener->onTouchBegan = CC_CALLBACK_2(StateGamePlay::onTouchBegan, this);
    	listener->onTouchMoved = CC_CALLBACK_2(StateGamePlay::onTouchMoved, this);
    	listener->onTouchEnded = CC_CALLBACK_2(StateGamePlay::onTouchEnded, this);
    
    	dispatcher->addEventListenerWithSceneGraphPriority(listener, this);
    

    Tiếp theo do background của phần game play không thay đổi, nên vẽ 1 lần ở phần init():

    auto spriteBG = Sprite::createWithSpriteFrameName(NameBG_GP);
    spriteBG->setPosition(posMidleScreen);
    this->addChild(spriteBG, zBG);

    Tới phần chính của game play là vẽ quả bí ngô ra màn hình, làm giống như là phần tóm tắt game play ở phía trên. Hàm drawPumpkins() gọi trong init(), nội dung của hàm drawPumpkins() như sau:

    void StateGamePlay::drawPumpkins()
    {
    	if (PumpkinCurrentID == -1)                     // gan PumpkinCurrentID = -1 o dau ham Init() de khoi tao trai bi dau tien
    	{
    		PumpkinCurrentID = random(0,19);                   // trai bi dau tien se ngau nhien trong data 20 trai bi
    	}
    	else                                                  // khi bat dau choi game se vao phan nay
    	{
    		PumpkinPrevious = PumpkinCurrent;                                               // toi gan doi tuong sprite hien hanh thanh sprite truoc
    		PumpkinPrevious->runAction(MoveTo::create(0.1f, posPumpkinEnd));                // thuc hien hanh dong di chuyen sang ben trai man hinh
    		PumpkinPrevious->runAction(Sequence::create(ScaleTo::create(0.1f, 0.1f),        // scale nho doi tuong con 10% trong 0.1s
    													CCCallFuncN::create(CC_CALLBACK_1(StateGamePlay::actionFinish, this)), 
    													NULL                               //goi toi ham actionFinish khi xong hanh dong tren
    													)
    									);
    		if (random(0, 2) == 1)                         // toi cho xac suat 33%
    		{
    			PumpkinPreviousID = PumpkinCurrentID;      // trai bi sau se giong trai bi truoc va cau tra loi dung se la YES
    			gameAnswer = true;
    		}
    		else                                         // xac suat 66% la sai
    		{
    			PumpkinPreviousID = PumpkinCurrentID;   // chuyen id trai bi hien hanh
    			int temp = random(0, 19);
    			while (temp == PumpkinCurrentID)
    			{
    				temp = random(0, 19);
    			}
    			PumpkinCurrentID = temp;              // gan gia tri id moi va khac id trai bi truoc do
    			gameAnswer = false;                   // cau tra loi dung se la NO
    		}
    	}
    
    	auto spritePumpkin = Sprite::createWithSpriteFrameName(NamePumpkins[PumpkinCurrentID]);
    	spritePumpkin->setPosition(posPumpkinBegin);                       // set vi tri trai bi moi se xuat hien ben phai man hinh
    	spritePumpkin->setScale(0.1f);                                     // set do lon chi bang 10% trai bi goc
    	auto moveto = MoveTo::create(0.1f, posPumpkinMid);                 // tao hanh dong di chuyen ra giua man hinh
    	auto scaleto = ScaleTo::create(0.1f, 1.0f);                        // tao hanh dong scale lon 100% trai bi trong 0.1s
    	spritePumpkin->runAction(moveto);                                  // gan hanh dong di chuyen cho doi tuong sprite la trai bi
    	spritePumpkin->runAction(scaleto);                                 // gan hanh dong scale
    	this->addChild(spritePumpkin, zUi);                                // hien thi len man hinh
    
    	PumpkinCurrent = spritePumpkin;                                   // Luu lai doi tuong kieu con tro de su dung
    	timeTotal = float(maxTime - float(0.025*Score/2));                // thoi gian tra loi cau hoi se giam dan khi diem cang cao
    	if (timeTotal < 1.0f)                                             // thoi gian nho nhat la 1s
    		timeTotal = 1.0f;
    	m_currentTime = timeTotal;
    
    	auto userdefault = UserDefault::sharedUserDefault();
    	int soundEnable = userdefault->getIntegerForKey(SoundEnable);
    	if (soundEnable == soundOn)
    	{
    		auto audio = SimpleAudioEngine::getInstance();
    		audio->playEffect("sounds/wind.wav", false);
    	}
    }

    Bên trong hàm vẽ bí ngô trên có thêm hàm actionFinish(), hàm này có tác dụng là xóa đối tượng khỏi màn hình để không còn tốn bộ nhớ quản lý vì càng quản lý nhiều đối tượng thì game xử lý chậm, và hàm này sẽ được viết như sau:

    void StateGamePlay::actionFinish(cocos2d::Node* sender)
    {
    	auto sprite = (Sprite*)sender;	
    	this->removeChild(sprite, true);  // xoa doi tuong khoi man hinh
    }

    Tiến hành phần xử lý game, tất cả được đặt trong hàm update(), viết hàm update() như sau:

    void StateGamePlay::update(float dt)
    {
    	drawClock(dt);          // ve dong ho tinh thoi gian 
    	drawScore(dt);          // ve diem so hien co
    	drawTextGP();           // ve text huong dan choi
    	drawAnswer();           // ve cau tra loi
    	UpdateAdsBanner();      // update hien baner quang cao.
    }

    1. drawClock(dt)

    Hàm này tính toán thời gian và vẽ đồng hồ báo giờ, hàm này sẽ được viết như sau:

    void StateGamePlay::drawClock(float dt)
    {
    	if (gameReady)                         // khai bao gameReady = false o ham Init(), se bat len tru khi nguoi choi touch man hinh
    	{
    		this->removeChildByTag(tagClock);   //  xoa doi tuong dong ho cu khoi man hinh de lat toi cai moi (giong nhu toi refesh)
    		this->removeChildByTag(tagClock1);   
    		this->removeChildByTag(tagClockTimer); 
    
    		auto ClockBG = Sprite::createWithSpriteFrameName(NameClockWhite);
    		ClockBG->setPosition(posClock);
    		ClockBG->setColor(yellowColor3B);
    		ClockBG->setScale(1.1);
    		ClockBG->setTag(tagClock);
    		this->addChild(ClockBG, zUi);
    
    		auto ClockBG1 = Sprite::createWithSpriteFrameName(NameClockBlue);
    		ClockBG1->setPosition(posClock);
    		ClockBG1->setTag(tagClock1);
    		this->addChild(ClockBG1, zUi);
    
    		m_timer = ProgressTimer::create(Sprite::createWithSpriteFrameName(NameClockWhite));
    		m_timer->setReverseDirection(true);
    		m_timer->setPosition(posClock);
    		m_timer->setTag(tagClockTimer);
    		this->addChild(m_timer, zUi);
    
    		m_currentTime -= dt;
    		if (m_currentTime < 0)            // khi het gio thi se bao nguoi choi de ket thuc game
    		{
    			m_currentTime = 0;
    			gameTimeOut = true;              // active biet time out
    			checkAnswer(false);              // goi den check answer voi tham so la false de ket thuc game
    		}
            // cang gan het gio thi dong ho se chuyen sang mau do
    		m_timer->setColor(Color3B(255 * (1 - m_currentTime / (timeTotal)), 255 * m_currentTime / (timeTotal), 50));
    		m_timer->setPercentage(m_currentTime * 100 / (timeTotal));   // hien thi phan thoi gian con lai
    	}
    }
    

    Ở trên có biến gameReady = false khi init(), khi người chơi đã sẵn sàng chơi, chạm vào màn hình thì game sẽ gán gameReady = true trong hàm onTouchEnded() như sau.

    void StateGamePlay::onTouchEnded(cocos2d::Touch* touches, cocos2d::Event* event)
    {
    	if (m_DialogBonusShow)   // dang co thong bao thi kho xu ly
    		return;
    	if (gameReady == false)  // neu game chua san sang thi se san sang
    	{
    		gameReady = true;
    		drawPumpkins();     // ve trai bi ngo thu 2 de bat dau choi
    	}
    }

    2. drawScore(dt)

    Lấy điểm hiện tại và vẽ ra màn hình.

    void StateGamePlay::drawScore(float dt)
    {
    	this->removeChildByTag(tagScoreBG);    // xoa khỏi man hinh de ve cai moi
    	this->removeChildByTag(tagScore);
    
    	auto ScoreBG = Sprite::createWithSpriteFrameName(NameScoreBGGP);
    	ScoreBG->setPosition(posScoreGP);
    	ScoreBG->setTag(tagScoreBG);
    	this->addChild(ScoreBG, zUi);
    
    	auto strScore = CCString::createWithFormat("%d", Score);                        // tao bien string de gan diem so vao
    	auto lbScore = Label::createWithTTF(Pumpkins_font90, strScore->getCString());   // tao label co gia tri la diem so
    	lbScore->setTextColor(yellowColor4B);
    	lbScore->setTag(tagScore);
    	lbScore->setPosition(posScoreGP.x, posScoreGP.y - 5);
    	this->addChild(lbScore, zUi);                              // dua ra man hinh
    }
    

    3. drawTextGP()

    Hàm này dùng để vẽ text hướng dẫn người chơi và sẽ được thực hiện như sau.

    void StateGamePlay::drawTextGP()
    {
    	scaleTextBegin++;                    // bien scale theo thời gian, dung de lam cho game sinh dong hon
    	if (scaleTextBegin > 100)
    		scaleTextBegin = 0;
    
    	this->removeChildByTag(tagTextRem);
    	this->removeChildByTag(tagTextBegin);
    
    	if (gameReady == false)                  
    	{
    		auto textRemember = Label::createWithTTF(Pumpkins_font30, "Remember This Pumpkin");
    		textRemember->setTextColor(pinkColor4B);
    		textRemember->setPosition(posTextRemember);
    		textRemember->setTag(tagTextRem);
    		this->addChild(textRemember, zUi);
    
    		auto textBegin = Label::createWithTTF(Pumpkins_font30, "Tap To Begin");   // game chua san sang se hien text huong dan bat dau choi
    		textBegin->setTextColor(pinkColor4B);
    		textBegin->setScale(float(1.0f + scaleTextBegin / 100.0f));              // scale text theo thoi gian de gay su chu y
    		textBegin->setPosition(posTextRemember.x, posTextRemember.y - 50);
    		textBegin->setTag(tagTextBegin);
    		this->addChild(textBegin, zUi);
    	}
    	else
    	{
    		auto textRemember = Label::createWithTTF(Pumpkins_font30, "Does The Current Pumpkin");
    		textRemember->setTextColor(pinkColor4B);
    		textRemember->setPosition(posTextRemember);
    		textRemember->setTag(tagTextRem);
    		this->addChild(textRemember, zUi);
    
    		auto textBegin = Label::createWithTTF(Pumpkins_font30, "Match The Previous One?");
    		textBegin->setTextColor(pinkColor4B);
    		textBegin->setPosition(posTextRemember.x, posTextRemember.y - 50);
    		textBegin->setTag(tagTextBegin);
    		this->addChild(textBegin, zUi);
    		
    		if (gameTimeOut)             // bien nay duoc active trong ham drawClock() khi het gio
    		{
    			if (this->getChildByTag(tagTimeOut) == NULL)
    			{
    				auto textTimeOut = Label::createWithTTF(Pumpkins_font90, "Time Out");     // hien thi text bao het gio
    				textTimeOut->setTextColor(redColor4B);
    				textTimeOut->setTag(tagTimeOut);
    				textTimeOut->setPosition(posMidleScreen);
    				textTimeOut->setScale(0.1);
    				textTimeOut->runAction(ScaleTo::create(0.5f, 1.0f));
    				this->addChild(textTimeOut, zUi);
    			}
    		}
    	}
    }
    

    4. drawAnswer()

    Hàm này có nhiệm vụ vẽ 2 câu trả lời là YESNO để hỏi người chơi xem bí ngô hiện tại có giống quả bí ngô trước đó không, nếu giống thì trả lời YES và khác thì trả lời NO bằng cách nhấn chọn.

    void StateGamePlay::drawAnswer()
    {
    	if (gameReady)    // game da san sang
    	{
    		if (this->getChildByTag(tagAnswer) == NULL)    // kiem tra de chi ve cau tra loi 1 lan dau tien ma thoi
    		{
    			auto btNo = MenuItemImage::create();
    			btNo->setNormalImage(Sprite::createWithSpriteFrameName(NamebtNo));
    			btNo->setSelectedImage(Sprite::createWithSpriteFrameName(NamebtNoS));
    			btNo->setCallback(CC_CALLBACK_0(StateGamePlay::AnswerNo, this));            // goi den ham AnswerNo khi nhan tra loi NO
    			btNo->setPosition(posbtNo);
    
    			auto btYes = MenuItemImage::create();
    			btYes->setNormalImage(Sprite::createWithSpriteFrameName(NamebtYes));
    			btYes->setSelectedImage(Sprite::createWithSpriteFrameName(NamebtYesS));
    			btYes->setCallback(CC_CALLBACK_0(StateGamePlay::AnswerYes, this));          // goi den ham AnswerYes khi nhan tra loi YES
    			btYes->setPosition(posbtYes);
    
    			auto answer = Menu::create(btNo, btYes, NULL);                             // dua vao menu
    			answer->setPosition(Point::ZERO);
    			answer->setTag(tagAnswer);
    			this->addChild(answer, zUi);                                      // dua menu ra man hinh
    		}
    	}
    }
    AnswerNo()
    void StateGamePlay::AnswerNo()
    {
    	if (gameOver)  // neu da ket thuc game thi khong vao nua
    		return;
    	if (!gameAnswer)  // nguoi choi chon tra loi NO va cau tra loi NO la dung
    	{
    		Score++;    // them diem cho nguoi choi
    		checkAnswer(true);  // chuyen den ham checkAnswer() voi gia tri la true dong nghia voi tra loi dung
    	}
    	else
    	{
    		checkAnswer(false);  // nguoi choi tra loi NO nhung cau tra loi la dung, chuyen den ham checkAnswer voi gia tri false de ket thuc game
    	}
    }
    
    AnswerYes()
    void StateGamePlay::AnswerYes()
    {
        if (gameOver)
            return;
    
        if (gameAnswer)
        {
            Score++;
            checkAnswer(true);
        }
        else
        {
            checkAnswer(false);
        }
    }
    checkAnswer()

    Hàm này có 2 giá trị để xử lý, đó là giá trị falsetrue, nếu giá trị là false thì vẽ dấu check sai và kết thúc game, nếu giá trị đưa vào là true thì vẽ dấu check đúng và tiếp tục đi đến câu kế tiếp. Đoạn code được thực hiện như sau.

    void StateGamePlay::checkAnswer(bool ans)
    {
    	if (ans && gameAnswer)   // tra loi dung va cau tra loi la giong nhau
    	{
    		auto userdefault = UserDefault::sharedUserDefault();
    		int soundEnable = userdefault->getIntegerForKey(SoundEnable);
    		if (soundEnable == soundOn)
    		{
    			auto audio = SimpleAudioEngine::getInstance();
    			audio->playEffect("sounds/right.wav", false);
    		}
    
    		auto right = Sprite::createWithSpriteFrameName(NamebtTrue);       // tao ra dau check bao nguoi choi da tra loi dung
    		right->setPosition(posbtYes);
    		right->setScale(0.1f);
    		right->runAction(Sequence::create(ScaleTo::create(0.5f,1.0f), 
    											CCCallFuncN::create(CC_CALLBACK_1(StateGamePlay::actionFinish,this)),
    											NULL
    										)  
    						); // thuc hien hanh dong scale tu 10% den 100% kich thuoc hinh anh trong thoi gian 0.5s, sau do se huy dau check.
    		this->addChild(right, zUi);
    
    		drawPumpkins();                 // ve cau hoi tiep theo
    	}
    	else if (ans && !gameAnswer)       // tra loi dung va cau tra loi la khac nhau
    	{
    		auto userdefault = UserDefault::sharedUserDefault();
    		int soundEnable = userdefault->getIntegerForKey(SoundEnable);
    		if (soundEnable == soundOn)
    		{
    			auto audio = SimpleAudioEngine::getInstance();
    			audio->playEffect("sounds/right.wav", false);
    		}
    
    		auto right = Sprite::createWithSpriteFrameName(NamebtTrue);
    		right->setPosition(posbtNo);
    		right->setScale(0.1f);
    		right->runAction(Sequence::create(ScaleTo::create(0.5f, 1.0f),
    			CCCallFuncN::create(CC_CALLBACK_1(StateGamePlay::actionFinish, this)),
    			NULL
    			)
    			);
    		this->addChild(right, zUi);
    
    		drawPumpkins();
    	}
    	else if (!ans)                          // tra loi sai
    	{
    		auto userdefault = UserDefault::sharedUserDefault();
    		int soundEnable = userdefault->getIntegerForKey(SoundEnable);
    		if (soundEnable == soundOn)
    		{
    			auto audio = SimpleAudioEngine::getInstance();
    			audio->playEffect("sounds/wrong.wav", false);
    		}
    
    		if (!gameTimeOut && gameAnswer)  // tra loi sai khong phai do het gio, se hien thi dau check sai bao nguoi dung
    		{
    			auto wrong = Sprite::createWithSpriteFrameName(NamebtFalse);
    			wrong->setPosition(posbtNo);
    			wrong->setScale(0.1f);
    			wrong->runAction(Sequence::create(ScaleTo::create(0.5f, 1.0f),
    											CCCallFuncN::create(CC_CALLBACK_1(StateGamePlay::actionFinish, this)),
    											NULL
    											)
    							);
    			this->addChild(wrong, zUi);
    		}
    		else if (!gameTimeOut && !gameAnswer) // tra loi sai khong phai do het gio, se hien thi dau check sai bao nguoi dung
    		{
    			auto wrong = Sprite::createWithSpriteFrameName(NamebtFalse);
    			wrong->setPosition(posbtYes);
    			wrong->setScale(0.1f);
    			wrong->runAction(Sequence::create(ScaleTo::create(0.5f, 1.0f),
    										CCCallFuncN::create(CC_CALLBACK_1(StateGamePlay::actionFinish, this)),
    										NULL
    										)
    							);
    			this->addChild(wrong, zUi);
    		}
    
    		if (soundEnable == soundOn)
    		{
    			auto audio = SimpleAudioEngine::getInstance();
    			audio->playEffect("sounds/gameOver.wav", false);
    		}
    		gameOver = true;  // ket thuc game
    		this->runAction(Sequence::create(DelayTime::create(1.0f),
    						CCCallFuncN::create(CC_CALLBACK_0(StateGamePlay::gotoEndGame, this)),
    						NULL
    						)
    					);  // thuc hien hanh dong chuyen sang state game play sau 1s
    	}
    }

    Phần chính của game đó là StateGamePlay được hướng dẫn hiện thực trong bài này, và cuối cùng sẽ là StateEndGame được hướng dẫn trong bài sau.

    Bài chung series

    0 Bình luận
    Lập Trình Game

    Lập Trình Game

    Kiến thức, kỹ thuật, kinh nghiệm lập trình game.

    Đề xuất

    Phát Triển Game Funny Halloween Pumpkins với Cocos2d-x - Phần 2
    Bài viết hướng dẫn các bạn mới muốn học làm game bằng Cocos2d-x. Thông ...
    Thành Phần Hoá Các Đối Tượng Trong Game Với Cocos2d-x
    Hướng dẫn thành phần hoá các đối tượng trong game với Cocos2d-x

    Khám phá

    Hướng Dẫn Viết Game Zero Với Cocos2d-x - Phần 2: Hiện Thực LoadScene
    Hướng dẫn cách để hiện thực một scene trong game, cũng như cách để ...
    Hướng Dẫn Viết Game Zero Với Cocos2d-x - Phần 5: Hiện thực GameScene - Vẽ Suit
    Hướng dẫn khởi tạo một layer mới trong game và vẽ các suit với Cocos2d-x
    Trí Tuệ Nhân Tạo trong Games - Phần 1: Thiết Kế AI với FSM
    Khái niệm, vai trò của trí tuệ nhân tạo trong games và hiện thực một AI ...
    Hướng Dẫn Viết Game Zero Với Cocos2d-x - Phần 6: Hiện thực GameScene - Thêm thời gian
    Hướng dẫn add thêm thời gian vào game để đếm ngược mỗi lần chơi của game ...
    Hướng Dẫn Viết Game Zero Với Cocos2d-x - Phần 7: Hiện thực GameScene - Button
    Hướng dẫn gắn thêm các button vào game, và hiện thực hàm resetButton cho ...
    Tạo High Score với Cocos2d-x 3.x.x
    Bài viết hướng dẫn lưu dữ liệu với các kiểu interger, float, double hay ...
    Cài Đặt Cocos2d-js Và Tạo Project HelloWorld
    Cocos2d-x là Engine phát triển game được sử dụng khá rộng rãi, giúp việc ...
    Khi bạn nhấn vào liên kết sản phẩm do STDIO đề xuất và mua hàng, STDIO có thể nhận được hoa hồng. Điều này hỗ trợ STDIO tạo thêm nhiều nội dung hữu ích.. Tìm hiểu thêm.
    STDIO
    Trang chính
    Công ty TNHH STDIO

    30, Trịnh Đình Thảo, Hòa Thạnh, Tân Phú, Hồ Chí Minh
    +84 28.36205514 - +84 942.111912
    developer@stdio.vn

    383/1 Quang Trung, Phường 10, Quận Gò Vấp, Hồ Chí Minh
    Số giấy phép ĐKKD: 0311563559 do sở Kế hoạch và Đầu Tư TPHCM cấp ngày 23/02/2012

    ©STDIO, 2013 - 2020