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__
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 instance
bool 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();
}