多点触摸

      在很多游戏中,需要使用到多点触摸功能,因此,Cocos2d-x也支持多点触摸。不过由于Cocos2d-x是跨平台的游戏引擎,所以必须考虑到不同平台的差异。由于不同平台(iPhone、iPad、Android手机等)支持的最大触摸点数不同。例如,通常Apple的设备都是支持十点触摸的,而少数Android设备也是支持十点触摸的,但很多Android设备可能并不支持十点触摸,顶多也就支持五点触摸。所以,为了尽可能满足更多的设备,Cocos2d-x将最大触摸点数限制为5。通过EventTouch::MAX_TOUCHES常量可以获取最大触摸点数。

     多点触摸需要使用EventListenerTouchAllAtOnce监听类。可以直接使用EventListenerTouchAllAtOnce:create方法创建EventListenerTouchAllAtOnce对象。然后需要设置该监听器的触摸开始(onTouchesBegan)、触摸移动(onTouchesMoved)和触摸结束(onTouchesEnded)三个监听方法。
     尽管多点触摸和单点触摸在使用方法上类似,但从触摸驱动的角度看,多点触摸并不是操作系统固有的,而是后期经过软件单独处理的,所以并不是所有的操作系统在默认状态下都支持多点触摸。例如,iOS在默认状态下就不支持多点触摸,而Android系统在默认状态下是支持多点触摸的。因此,如果在iOS上使用多点触摸,在默认状态下,只能捕捉第一个触摸点。而要想捕捉多个触摸点,需要打开AppController.mm文件,并找到didFinishLaunchingWithOptions方法,在该方法中定位到如下的代码。

CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]
                                     pixelFormat: kEAGLColorFormatRGB565
                                     depthFormat: GL_DEPTH24_STENCIL8_OES
                              preserveBackbuffer: NO
                                      sharegroup: nil
                                   multiSampling: NO
                                 numberOfSamples: 0];

    最后在该代码后面添加如下的代码即可。

[eaglView setMultipleTouchEnabled:YES];

    本节将利用多点触摸技术实现一个可以最多捕捉5个触摸点的程序。每捕捉一个触摸点,会用不同颜色在窗口上绘制一个小方块,并在水平和垂直方向经过该小方块绘制直线。效果如下图所示。

 

     实现本例的关键是在onEnter方法中创建EventListenerTouchAllAtOnce对象,并设置前面提到的三个事件监听方法。

void MultiTouchEventTest::onEnter()
{
    BasicScene::onEnter();
    auto listener = EventListenerTouchAllAtOnce::create();
    listener->onTouchesBegan = CC_CALLBACK_2(MultiTouchEventTest::onTouchesBegan, this);
    listener->onTouchesMoved = CC_CALLBACK_2(MultiTouchEventTest::onTouchesMoved, this);
    listener->onTouchesEnded = CC_CALLBACK_2(MultiTouchEventTest::onTouchesEnded, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
    
}

     接下来就是实现这三个事件监听方法,不过在实现这三个方法之前,需要先定义一个Map对象,该对象存储了触摸点ID和TouchPoint对象的关系,其中TouchPoint是自定义的一个类,描述了当前的触摸点,并负责绘制触摸点方块和垂直、水平方向的直线。我们后面再说这个类。

static Map<int, TouchPoint*> s_map;

    下面来实现onTouchesBegan方法。当触摸屏幕时会调用该方法。在该方法中会利用for循环扫描同时触摸屏幕的点数,然后获取当前触摸点的坐标后,将这些触摸点连同触摸点对应的ID存储在s_map对象中。onTouchesBegan方法的代码如下:

void MultiTouchEventTest::onTouchesBegan(const std::vector<Touch*>& touches, Event  *event)
{
    for ( auto &item: touches )
    {
        auto touch = item;
        //  创建TouchPoint对象
        auto touchPoint = TouchPoint::touchPointWithParent(this);
        //  获取当前触摸点的位置坐标
        auto location = touch->getLocation();
        //  将当前触摸点的位置坐标与TouchPoint对象关联
        touchPoint->setTouchPos(location);
        //  设置根据触摸点绘制的小方块和通过小方块的直线的颜色
        touchPoint->setTouchColor(*s_TouchColors[touch->getID()]);
        //  将TouchPoint对象添加到当前场景中
        addChild(touchPoint);
        //  将触摸点ID和对象的TouchPoint对象关联
        s_map.insert(touch->getID(), touchPoint);
     }
}

     在onTouchesBegan方法中多次使用到了TouchPoint类,而且还使用addChild方法将TouchPoint对象添加到了当前的场景中,所以可以肯定,TouchPoint是一个节点类(Node的子类),下面看一下TouchPoint类的代码。

class TouchPoint : public Node
{
public:
    //  绘制以触摸点为中心的小方块和通过小方块的水平和垂直的直线
    virtual void draw(Renderer *renderer, const Mat4 &transform, bool transformUpdated)
    {
        DrawPrimitives::setDrawColor4B(_touchColor.r, _touchColor.g, _touchColor.b, 255);
        glLineWidth(10);
        //  绘制水平直线
        DrawPrimitives::drawLine( Vec2(0, _touchPoint.y), Vec2(getContentSize().width, _touchPoint.y) );
        //  绘制垂直直线
        DrawPrimitives::drawLine( Vec2(_touchPoint.x, 0), Vec2(_touchPoint.x, getContentSize().height) );
        glLineWidth(1);
        //  将点设为30个像素大小(直径)
        DrawPrimitives::setPointSize(30);
        //  以触摸点为中心绘制小方块
        DrawPrimitives::drawPoint(_touchPoint);
    }
    
    void setTouchPos(const Vec2& pt)
    {
        _touchPoint = pt;
    }
    
    void setTouchColor(Color3B color)
    {
        _touchColor = color;
    }
    //  创建TouchPoint对象
    static TouchPoint* touchPointWithParent(Node* pParent)
    {
        auto pRet = new TouchPoint();
        pRet->setContentSize(pParent->getContentSize());
        pRet->setAnchorPoint(Vec2(0.0f, 0.0f));
        pRet->autorelease();
        return pRet;
    }
    
private:
    Vec2 _touchPoint;
    Color3B _touchColor;
};

      在onTouchesBegan方法中还使用了一个s_TouchColors数组,用于存储每个触摸点对应的颜色,TouchPoint::draw方法会使用这些颜色绘制小方块和通过小方块的直线。该数组的定义如下:

static const Color3B* s_TouchColors[EventTouch::MAX_TOUCHES] = {
    &Color3B::YELLOW,
    &Color3B::BLUE,
    &Color3B::GREEN,
    &Color3B::RED,
    &Color3B::MAGENTA
};

       接下来实现onTouchesMoved方法,该方法当触摸点在屏幕上移动时会被调用,在该方法中会根据s_map中存储的触摸点的位置重新设置TouchPoint对象的位置,TouchPoint对象的位置改变了,也就意味着与触摸点关联的小方块和通过小方块的直线会重新绘制(窗口会不断刷新,TouchPoint对象也会不断重绘,当然,draw方法也会不断调用)。这样就会使小方块和通过小方块的直线随着触摸点的移动而移动。onTouchesMoved方法的代码如下:

void MultiTouchEventTest::onTouchesMoved(const std::vector<Touch*>& touches, Event  *event)
{
    for( auto &item: touches)
    {
        auto touch = item;
        //  根据触摸点的ID获取对应的TouchPoint对象
        auto pTP = s_map.at(touch->getID());
        auto location = touch->getLocation();
        //  更新TouchPoint对象中触摸点的位置
        pTP->setTouchPos(location);
    }
}

      最后来实现一下onTouchesEnded方法,该方法当手指抬起时被调用。在该方法中会从s_map中删除抬起的触摸点(TouchPoint对象),同时会从当前场景中删除TouchPoint对象。这样与该抬起触摸点关联的小方块和通过小方块的直线也随之消失。onTouchesEnded方法的代码如下:

void MultiTouchEventTest::onTouchesEnded(const std::vector<Touch*>& touches, Event  *event)
{
    for ( auto &item: touches )
    {
        auto touch = item;
        auto pTP = s_map.at(touch->getID());
        //  从当前场景中删除TouchPoint对象
        removeChild(pTP, true);
        //  从s_map中删除TouchPoint对象
        s_map.erase(touch->getID());
    }
}

     在测试本文的例子中,应使用支持多点触摸的设备进行测试,例如,iPhone、iPad、Android手机等。

posted on 2016-10-12 22:05  银河使者  阅读(719)  评论(1编辑  收藏  举报

导航