在很多游戏中,需要使用到多点触摸功能,因此,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手机等。
本文转自银河使者博客园博客,原文链接http://www.cnblogs.com/nokiaguy/articles/5954587.html如需转载请自行联系原作者
银河使者