第九章用户事件-触摸事件

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.触摸事件的时间方面

触摸事件在时间上可以有“按下”、“移动”和“抬起”等阶段,表示触摸是否刚刚开始、是否正在移动或处于静止状态,以及何时结束。
触摸事件有两个事件监听器:EventListenerTouchOneByOneEventListenerTouchAllAtOnce,分别对应单点触摸和多点触摸。
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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值