Search…

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

21/09/202026 min read
Hướng dẫn làm game cụ thể bằng Cocos2d-x với code mẫu và game mẫu.

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

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024