cocos2d-3.0较之前的版本一个很大的区别就是事件分发的机制不一样了,3.0将事件处理逻辑独立出来,有各种不同的事件监听器用来监听不同的事件类型,如触摸事件、键盘响应事件、加速度计事件、鼠标事件、自定义事件,这些事件对应不同的事件监听器,而当一个节点收到了事件以后有一个事件分发器_eventDispatcher专门来分发这些不同的事件。触摸是经常用到的,所以我们来看一下3.0中如何为一个层注册触摸事件。整体的思路是这样的,首先我们要有一个触摸事件的监听器,这个监听器需要和层的一些触摸处理的函数绑定起来,然后设置一下这个触摸监听器的属性,最后把这个监听器添加到分发器中。下面的代码中,我具体的阐述了这些用法。

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }

	auto size = Director::getInstance()->getWinSize();

	auto sprite = Sprite::create("sprite.png");
	sprite->setPosition(CCPoint(size.width/2,size.height/2));
	this->addChild(sprite);

	//创建一个单点触摸的事件监听器,用来监听单点触摸事件
	auto listener = EventListenerTouchOneByOne::create();
	//指定触摸的回调函数
	listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan,this);
	listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved,this);
	listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded,this);
	//设置是否吞噬触摸
	listener->setSwallowTouches(true);
	//_eventDispatcher是事件分发器,用来分发不同类型的事件,这些类型包括触摸、键盘响应、加速度计、鼠标响应
	//_eventDispatcher是Node的一个属性,属于保护成员,用来管理节点的事件分发,是一个单例对象,可以通过如下的方法获取
	//Director::getInstance()->getEventDispatcher();
	//将接受触摸的精灵和监听对象绑定起来,在node的析构函数中移除监听器,如果是fixedPriority因为没有绑定,需要手动移除
	//_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,sprite);
	//这里是固定优先级,而上边是显示优先级,显示优先级就是根据显示的顺序分发触摸事件,比如精灵1在精灵2的上面,则
	//精灵1将首先接受到触摸消息,固定优先级的值越小优先级越高
	_eventDispatcher->addEventListenerWithFixedPriority(listener,100);

    return true;
}

bool HelloWorld::onTouchBegan(Touch * touch,Event * pEvent)
{
	CCLOG("began");
	return true;
}

void HelloWorld::onTouchMoved(Touch * touch,Event * pEvent)
{
	CCLOG("moved");
}

void HelloWorld::onTouchEnded(Touch * touch,Event * pEvent)
{
	CCLOG("ended");
}

将事件监听器添加到分发器中的时候有俩个函数可以使用,一个是addEventListenerWithSceneGraphPriority,另一个是addEventListenerWithFixedPriority,除了参数的不同,还有俩点不同。SceneGraphPriority使用这个函数添加事件监听器的时候,它的优先级是根据精灵的显示顺序来确定的,如果精灵的位置靠前,将首先接受到触摸。而FixedPriority是根据你传递进去的值来确定的,值越小,优先级越高。还有就是事件监听器的移除,前者不需要我们去移除,在node的析构函数中会移除掉的,而后者需要我们自己来移除事件监听器。

//实现点击精灵移动的效果
bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }

	auto size = Director::getInstance()->getWinSize();

	//创建一个精灵
	auto sprite = Sprite::create("sprite.png");
	sprite->setPosition(CCPoint(size.width/2,size.height/2));
	this->addChild(sprite);

	//创建一个单点触摸事件的监听器,和回调函数绑定
	auto listener = EventListenerTouchOneByOne::create();
	listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan,this);
	listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved,this);
	listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded,this);
	//设置吞噬
	listener->setSwallowTouches(true);

	//添加到分发器中,第二个参数代表希望得到触摸的节点
	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,sprite);

    return true;
}

bool HelloWorld::onTouchBegan(Touch * touch,Event * pEvent)
{
	//getCurrentTarget获取当前的触摸目标
	//static_cast < type-id > ( expression )将表达式转化为type-id类型
	auto sprite = static_cast<Sprite *>(pEvent->getCurrentTarget());
	auto rect = sprite->getBoundingBox();
	auto point = touch->getLocation();
	//判断点击点是否在精灵上
	if(rect.containsPoint(point))
	{
		return true;
	}

	return false;
}

void HelloWorld::onTouchMoved(Touch * touch,Event * pEvent)
{
	auto sprite = static_cast<Sprite *>(pEvent->getCurrentTarget());
	//getDelta返回触摸的坐标的增量,有了这个函数处理起来方便多了
	sprite->setPosition(sprite->getPosition()+touch->getDelta());
}

void HelloWorld::onTouchEnded(Touch * touch,Event * pEvent)
{
	CCLOG("end!");
}

3.0的触摸机制

以上的代码实现了拖动精灵移动的效果,下面来看看和触摸优先级有关的用法。

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }

	auto size = Director::getInstance()->getWinSize();

	//创建精灵
	auto sprite = Sprite::create("sprite.png");
	sprite->setPosition(CCPoint(size.width/2,size.height/2));
	this->addChild(sprite);

	auto sprite2 = Sprite::create("sprite2.png");
	sprite2->setPosition(CCPoint(size.width/2,size.height*0.6));
	this->addChild(sprite2);

	auto sprite3 = Sprite::create("sprite.png");
	sprite3->setPosition(CCPoint(size.width/3,size.height/3));
	this->addChild(sprite3);

	//创建一个单点触摸事件的监听器,和回调函数绑定
	auto listener = EventListenerTouchOneByOne::create();
	listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan,this);
	listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved,this);
	listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded,this);
	//设置吞噬
	listener->setSwallowTouches(true);

	//添加到分发器中,第二个参数代表希望得到触摸的节点
	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,sprite);
	//一个事件监听器只能被添加一次,我们要想使用一个事件监听器,就得使用它的clone方法,这个方法是克隆一个一模一样
	//的监听器,这里传入的第二个参数变为了sprite2,代表sprite2希望得到触摸事件
	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener->clone(),sprite2);

    return true;
}

回调函数没有发生改变。我们创建了三个精灵,sprite,sprite2,sprite3,前俩个精灵在将触摸监听器添加到事件分发器中的时候传入了自己,作为第二个参数,所以在回调函数的处理中,获得的就是自己了,然后就实现了拖动的效果,sprite3拖动是没有效果的。在2.x的时候我们一般将希望接受触摸的精灵作为成员函数,然后在回调函数中去判断,3.0采用了这个方法来进行判断,感觉不错。

3.0的触摸机制

如果使用addEventListenerWithFixedPriority设置优先级,那么就得使用原来的方法来拖动精灵了,可以给精灵设置一个tag,在回调函数中获得精灵,或者将要拖动的精灵作为成员变量。addEventListenerWithSceneGraphPriority的默认优先级是0。可以使用_eventDispatcher->removeEventListener(listener);来移除事件监听器。最后很多人问我的那个黑窗口是怎么出来的,我是改动了一下main中的代码,大家复制一下我的以下这段代码,替换一下你的main函数,你要是老想出来这个黑窗口,就替换一下引擎的模板文件中的main函数,以后你新建工程的时候,工程就都有了这个dos窗口。

#include "main.h"
#include "AppDelegate.h"
#include "cocos2d.h"

USING_NS_CC;

//如果注释掉了以下这段话就不会出现dos窗口了,所以很方便
 #define USE_WIN32_CONSOLE

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

#ifdef USE_WIN32_CONSOLE
    AllocConsole();
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stdout);
    freopen("CONOUT$", "w", stderr);
#endif

    AppDelegate app;

    int ret = Application::getInstance()->run();

#ifdef USE_WIN32_CONSOLE
    FreeConsole();
#endif

	return ret;
}