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哦~~

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