Cocos2d-x学习笔记
事件处理机制
事件处理机制一般都有三个重要的角色:事件、事件源和事件处理者。事件源是事件发生的场所,通常就是各个视图或控件,事件处理者是接受事件并对其进行处理的一段程序。
1.事件
事件类是Event,它的子类有EventTouch(触碰事件)、EventMouse(鼠标事件)、EventKeyboard(键盘事件)、EventAcceleration(加速度事件)和EventCustom(自定义),以及EventCustom的子类PhysicsContact(物理引擎中接触事件)。
2.事件源
事件源是Cocos2d-x中的精灵、层、菜单等节点对象。
3.事件处理者
Cocos2d-x中的事件处理者是事件监听器类EventListener,它的子类有EventListenerTouchOneByOne(单点触摸事件监听器)、EventListenerTouchAllOnce(多点触摸事件监听器)、EventListenerKeyboard(键盘事件监听器)、EventListenerMouse(鼠标事件监听器)、EventListenerAccelerate(加速度事件监听器)、EventListenerController(游戏手柄监听器)、 (焦点事件监听器)和EventListenerCustom(自定义事件监听器)、以及EventListenerCustom的子类EventListenerPhysicsContact(物理引擎中接触事件监听器)。
事件分发器
事件监听器和事件具有对应关系。它们之间需要在程序中建立关系,这种关系的建立过程被称为“注册监听器”。在Cocos2d-x中提供一个事件分发器(EventDispatcher)负责管理这种关系,起责任是注册监听器、注销监听器和事件分发。
事件分发器(EventDispatcher类)采用单例设计模式,通过Director::getInstance()->getEventDispatcher()
语句获得语句分发器对象。
EventDispatcher类中注册事件监听器到事件分发器函数如下:
void addEventListenerWithFixedPriority(EventListener * listener, int fixedPriority);
指定固定的事件优先级注册监听器,事件优先级决定事件响应的优先级别,值越小优先级越高。void addEventListenerWithSceneGraphPriority(EventListener * listener, Node * node);
:把精灵显示优先级作为事件优先级。参数node是要触摸精灵对象。
当不再进行事件响应的时候,我们应该注销事件监听器,主要的注销函数如下:
void removeEventListener(EventListener * listener);
:注销指定的事件监听器。void removeCustomEventListeners(const std::string& cunstomEventName);
:注销自定义事件监听器。void removeAllEventListeners();
注销所有事件监听器,需要注意的是,使用该函数后,菜单不能响应事件了,因为它也需要接受触摸事件。
触摸事件
触摸事件可以从时间和空间两个方面考虑。
1.触摸事件的时间方面
触摸事件在时间上可以有“按下”、“移动”和“抬起”等阶段,表示触摸是否刚刚开始、是否正在移动或处于静止状态,以及何时结束。
触摸事件有两个事件监听器:EventListenerTouchOneByOne和EventListenerTouchAllAtOnce,分别对应单点触摸和多点触摸。
EventListenerTouchOneByOne中的触摸事件响应属性:
std::function<bool(Touch *, Event *)>onTouchBegan
:当一个手指触摸屏幕时回调该属性所指定的函数。如果函数返回值为true,则可以回调后面两个属性(onTouchMoved和onTouchEnded)所指定的函数,否则不回调。std::function<void(Touch *, Event *)>onTouchMoved
:当一个手指在屏幕移动是回调该属性所指定的函数。std::function<void(Touch *, Event *)>onTouchEnded
:但一个手指离开屏幕时回调该属性所指定的函数。std::function<void(Touch *, Event *)>onTouchCancelled
:但单点触摸事件取消时回调该属性所指定的函数。
EventListenerTouchAllAiOnce中的触摸事件响应属性:
std::function<void(const std::vector<Touch *>&, Event *)onTouchesBegan
:但多个手指触摸屏幕时回调该属性所指定的函数。std::function<void(const std::vector<Touch *>&, Event *)onTouchesMoved
:但多个手指屏幕上移动时回调该属性所指定的函数。std::function<void(const std::vector<Touch *>&, Event *)onTouchesEnded
:但多个手指离开屏幕时回调该属性所指定的函数。std::function<void(const std::vector<Touch *>&, Event *)onTouchesCancelled
:但多点触摸事件被取消时回调该属性所指定的函数。
示例
auto listener = EventListenerTouchOneByOne::create();//创建单独触摸事件监听对象 listener->onTouchBagen = CC_CALLBACK_2(HelloWorld::touchBegan, this);//设置属性 ··· bool HelloWorld::touchBegan(Touch * touch, Event * event){ ··· return false; }
2.触摸事件的空间方面
空间方面就是每个触摸点(Touch)对象包含了当前的位置信息,以及之前的位置信息(如果有的话)。
下面的函数可以获得触摸暗点之前的位置信息:
Vec2 getPreviousLocationInView()
//UI坐标Vec2 getPreviousLocation()
//OpenGL坐标
下面的函数可以获得触摸点当前的位置信息:
Vec2 getLocationInView()
//UI坐标Vec2 getLocation()
//OpenGL坐标
实例
HelloWorld.h文件
#ifndef __HELLOWORLD_SCENE_H__#define __HELLOWORLD_SCENE_H__#include "cocos2d.h"typedef enum{ kBoxA_TAG = 102, kBoxB_TAG, kBoxC_TAG }SpriteTags; class HelloWorld : public cocos2d::Layer {public: static cocos2d::Scene* createScene(); virtual bool init(); virtual void onEnter();//将在该函数中注册监听器和初始化设置 virtual void onExit();//将在该函数中注销监听器和释放一些资源 // a selector callback bool touchBegan(cocos2d::Touch * touch, cocos2d::Event * event); void touchMoved(cocos2d::Touch * touch, cocos2d::Event * event); void touchEnded(cocos2d::Touch * touch, cocos2d::Event * event); // implement the "static create()" method manually CREATE_FUNC(HelloWorld); };#endif // __HELLOWORLD_SCENE_H__1234567891011121314151617181920212223242526272829303132
HelloWorld.cpp文件
#include "HelloWorldScene.h"USING_NS_CC; Scene* HelloWorld::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; }// on "init" you need to initialize your instancebool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); //贴图的纹理图片宽高必须是2的n次幂 auto bg = Sprite::create("BackgroundTile.png", Rect(0, 0, visibleSize.width, visibleSize.height)); //贴图的纹理参数,水平重复平铺,垂直重复平铺 Texture2D::TexParams tp = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT };//Texture2D::TexParams是一个结构体 bg->getTexture()->setTexParameters(tp);//将参数设置到背景精灵的纹理上 bg->setPosition(origin + Vec2(visibleSize.width / 2, visibleSize.height / 2)); addChild(bg, 0); Sprite * boxA = Sprite::create("BoxA2.png"); boxA->setPosition(origin + Vec2(visibleSize.width / 2, visibleSize.height / 2) + Vec2(-120, 120)); addChild(boxA, 10, kBoxA_TAG); Sprite * boxB = Sprite::create("BoxB2.png"); boxB->setPosition(origin + Vec2(visibleSize.width / 2, visibleSize.height / 2)); addChild(boxB, 20, kBoxB_TAG); Sprite * boxC = Sprite::create("BoxC2.png"); boxC->setPosition(origin + Vec2(visibleSize.width / 2, visibleSize.height / 2) + Vec2(120, 160)); addChild(boxC, 30, kBoxC_TAG); return true; }void HelloWorld::onEnter() { Layer::onEnter(); log("HelloWorld onEnter"); auto listener = EventListenerTouchOneByOne::create(); listener->setSwallowTouches(true);//设置吞没事件,true的话不会传给下一个node对象 listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::touchBegan, this); listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::touchMoved, this); listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::touchEnded, this); //注册监听器 EventDispatcher * eventDispatcher = Director::getInstance()->getEventDispatcher(); eventDispatcher->addEventListenerWithSceneGraphPriority(listener, getChildByTag(kBoxA_TAG));//使用精灵显示优先级注册时间监听器 //listener->clone()获得listener对象,使用clone()函数因为每一个事件监听器只能被注册一次 eventDispatcher->addEventListenerWithSceneGraphPriority(listener->clone(), getChildByTag(kBoxB_TAG)); eventDispatcher->addEventListenerWithSceneGraphPriority(listener->clone(), getChildByTag(kBoxC_TAG)); } bool HelloWorld::touchBegan(Touch * touch, Event * event) { //获取事件所绑定的target //static_cast<Sprite *>强制类型转换 event->getCurrentTarget()返回的是Node类型 auto target = static_cast<Sprite *>(event->getCurrentTarget()); Vec2 locationInNode = target->convertToNodeSpace(touch->getLocation());//获取当前触摸点相对于target对象的模型坐标 Size s = target->getContentSize();//获得targer对象的尺寸 Rect rect = Rect(0, 0, s.width, s.height);//通过targer对象的尺寸创建Rect变量 //点击范围判断检测 if (rect.containsPoint(locationInNode)) { log("sprite x = %f, y = %f", locationInNode.x, locationInNode.y); log("sprite tag = %d", target->getTag()); target->runAction(ScaleBy::create(0.06f, 1.06f)); return true; } return false; }void HelloWorld::touchMoved(Touch * touch, Event * event) { log("onTouchMoved"); auto target = static_cast<Sprite *>(event->getCurrentTarget());//static_cast<Sprite *>强制类型转换 target->setPosition(target->getPosition() + touch->getDelta()); }void HelloWorld::touchEnded(Touch * touch, Event * event) { log("onTouchEnded"); auto target = static_cast<Sprite *>(event->getCurrentTarget());//static_cast<Sprite *>强制类型转换 log("sprite onTouchesEnded"); Vec2 locationInNode = target->convertToNodeSpace(touch->getLocation()); Size s = target->getContentSize(); Rect rect = Rect(0, 0, s.width, s.height); //点击范围判断检测 if (rect.containsPoint(locationInNode)) { log("sprite x = %f, y = %f", locationInNode.x, locationInNode.y); log("sprite tag = %d", target->getTag()); target->runAction(ScaleTo::create(0.06f, 1.0f)); } }void HelloWorld::onExit() { Layer::onExit(); log("HelloWorld onExit"); Director::getInstance()->getEventDispatcher()->removeAllEventListeners(); }