Cocos2dx遊戲教程(六):“見縫插針”,菜單Menu與C++11新特性

上一節我們已經實現了第二個場景界面,最後按下按鈕發現沒有反應是不是,這一節將會給大家介紹下Cocos2dx另一個重要的對象,菜單Menu。

一、Menu介紹

提到Menu,不得不提到MenuItem,MenuItem繼承自Node,所以它的子類菜單項都可以使用Node的相關操作。
MenuItem是所有菜單項的父類,建議不要直接使用該類,因爲它並不包含具體顯示的功能。
作爲其它菜單項的父類,主要提供了一下三個功能:
(1)提供了基本按鈕的狀態:正常、選中、禁用。
(2)爲按鈕實現了基本的回調函數機制。點擊按鈕後,就會調用執行相應的回調函數,上文兩個新增的函數就是回調函數哦。

//開始遊戲
void onStartBtnPressed(Ref* pSender);
//選擇關卡
void onSelectBtnPressed(Ref* pSender);

菜單項的子類可以分成三類,總共六個:
(1)文字菜單項:MenuItemLabel、MenuItemAtlasFont、MenuItemFont;
(2)圖片菜單項:MenuItemSprite、MenuItemImage;
(3)切換菜單項:MenuItemToggle。
而關於各個菜單項的區別大家可以參考cocos2dx的源代碼,源代碼是最好的老師~

我們在上篇教程中有如下的代碼,實現方式如下

auto selectBtn = MenuItemImage::create("menu/select.png", "menu/select.png");
selectBtn->initWithCallback(CC_CALLBACK_1(GameMenuScene::onSelectBtnPressed, this));
selectBtn->setPosition(visibleSize.width / 2 + 20, visibleSize.height - 660);

auto menu = Menu::create(startBtn, selectBtn, NULL);
menu->setPosition(Point::ZERO);
this->addChild(menu);

注意如下兩行

selectBtn->initWithCallback(CC_CALLBACK_1(GameMenuScene::onSelectBtnPressed, this)); //回調方法
auto menu = Menu::create(startBtn, selectBtn, NULL);//注意最後一定要加個空項,告訴他沒了可以開始創建了。

我沒在回調方法裏面添加輸出信息看一下

//開始遊戲
void GameMenuScene::onStartBtnPressed(Ref* pSender) {
	//需要跳轉到遊戲界面
	CCLOG("Start Game");
}

//選擇關卡
void GameMenuScene::onSelectBtnPressed(Ref* pSender) {
	//需要跳轉到選擇關卡界面
	CCLOG("Go to select layer");
}

運行結果如下,我們們點擊開始遊戲和選擇關卡,在控制檯是不是可以看到輸出信息了
在這裏插入圖片描述
那麼具體的機制是什麼?
CC_CALLBACK_1指的是什麼?

二、C++11新特性之 std::function、std::bind、std::placeholders

上面提到了CC_CALLBACK_1,我們不得不提和它幾個相關的定義,如:
CC_CALLBACK_0,
CC_CALLBACK_1,
CC_CALLBACK_2,
CC_CALLBACK_3
這些具體是什麼,相信大家都已經看過定義了吧,Cocos2d-x 3.x版本用如下的宏來定義回調方法類的。

// new callbacks based on C++11
#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)

原來是C++11引入了boost庫的function和bind,bind也被3.x版本用來作爲回調的綁定。
__selector__是綁定回調的方法
__target__是調用方法的對象
##__VA_ARGS__是可變的參數列表
std::placeholders::_1是佔位符
std::placeholders::_2是佔位符
std::placeholders::_3還是佔位符

下面我們結合下C++11的新特性給大家介紹下CC_CALLBACK_X吧

1.std::function

提到std::function我們不得不提一下C++的函數指針
C++ 函數指針和函數類型
函數指針指向的是函數而非對象。和其他指針類型一樣,函數指針指向某種特定類型。
函數類型由它的返回值和參數類型決定,與函數名無關。

int add(int a, int b)

上述函數類型是:int (int a, int b);
上述函數指針fp:int (*fp)(int a, int b);

我們來比較一下這兩個定義

int (*p)(int a, int b); //p是一個指向函數的指針變量,所指函數的返回值類型爲整型
int *p(int a, int b); //p是函數名,此函數的返回值類型爲整型指針

我們通常定義一個函數指針

int(*fp)(int,int);
//可以直接省略fp
int(*)(int,int);

或者

typedef int(*FP)(int,int); 
Fp fp;

函數指針定應用

#include <iostream>
using namespace std;
 
int max(int x, int y); //求最大數
int min(int x, int y); //求最小數
int add(int x, int y); //求和
void process(int i, int j, int (*p)(int a, int b)); //應用函數指針
 
int main()
{
    int x, y;
    cin>>x>>y;
 
    cout<<"Max is: ";
    process(x, y, max);
 
    cout<<"Min is: ";
    process(x, y, min);
 
    cout<<"Add is: ";
    process(x, y, add);
 
    getch();
    return 0;
}
 
int max(int x, int y)
{
    return x > y ? x : y;
}
 
int min(int x, int y)
{
    return x > y ? y : x;
}
 
int add(int x, int y)
{
    return x + y;
}
 
void process(int i, int j, int (*p)(int a, int b))
{
    cout<<p(i, j)<<endl;
}

我們來看一下如何使用std::function

// 普通函數
int add1(int a, int b){return a + b;} 
// lambda表達式
auto add2 = [](int a, int b){ return a + b;}
// 函數對象類
class add3{
    int operator()(int a, int b){
        return a + b;
    }
};

上述三種可調用對象雖然類型不同,但是共享了一種調用形式:

int(int, int)

std::function就可以將上述類型保存起來,如下:

std::function<int(int ,int)>  a = add1; 
std::function<int(int ,int)>  b = add2; 
std::function<int(int ,int)>  c = add3(); 

我們看下這樣的定義是不是比原來更簡單了呢

2.std::bind
有了std::function,我們不得不提一下std::bind
std::bind 主要用於綁定生成目標函數,一般用於生成的回調函數,cocos2d 3.x的回調函數都是通過std::bind和std::function實現的
在Cocos2d-x中,我們可以看到,使用std::bind生成一個可調用對象,這個對象可以直接賦值給std::function對象;在類中有一個std::function的變量,這個std::function由std::bind來賦值,而std::bind綁定的可調用對象可以是Lambda表達式或者類成員函數等可調用對象,這個是Cocos2d-x中的一般用法。
如我們定義一個方法:

int add(int a, int b) {
	return a + b;
}

上面的方法使用std::bind怎麼實現呢

auto f1 = std::bind(add, 2, 3);
auto result = f1();

auto f2 = std::bind(add, std::placeholders::_1, 3);
result = f2(2);

auto f3 = std::bind(add, std::placeholders::_1, std::placeholders::_2);
result = f3(2, 3);

這裏面有三種使用方式,std::placeholders具體指的又是什麼呢

3.std::placeholders
std::placeholders,佔位符
我們來看一下上面的宏定義,以CC_CALLBACK_0爲例

#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)

很多人是不是看到CC_CALLBACK_0會認爲回調的方法是0個參數呢?
其實不是這樣的
CC_CALLBACK_0中的0是代表要回調的方法綁定第0個參數之後的參數。
如同上面的定義,如果有CC_CALLBACK來寫應該如何處理呢

//默認指定了第一個參數是2,第二個參數是3。
auto f1 = CC_CALLBACK_0(add, this, 2, 3);
auto result = f1();

//默認指定了第二個參數是3,調用的時候只需要指定第一個參數就可以了。
auto f2 = CC_CALLBACK_1(add, this,3);
result = f2(2);

//都爲指定參數,調用時都要傳入。
auto f3 = CC_CALLBACK_2(add, this);
result = f3(2, 3);

到這大家對新特性有一定的瞭解麼?編譯的時候不要忘了加-std=c++11哦~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章