关于setMultipleTouchEnabled

大家都知道 在ios中setMultipleTouchEnabled:YES]能控制整个游戏的单点还是多点触摸,
那么问题来了?
在cocos2d-X中有这个接口嘛?来控制整个屏幕的单点或多点

多点触控处理——在开启多点触控的游戏中,多个按钮和场景中的多个widgets可以同时被选中,从而造成多个逻辑功能同时响应的bug。
1,直接关闭多点触控:对于不存在多点操作和手势操作的游戏来说,直接关闭多点触控是最简单直接的办法。
关闭多点触控的方法分平台相关和平台无关两类,
平台无关方法是直接设置cocos引擎的最大touch数量(cocos_lib/base/CCEventTouch.h中的静态全局常量MAX_TOUCHES)==1即可,由于是编译时常量,该修改方法无法在游戏中重新的打开多点触控;
平台相关的方法则是在各个平台的native代码中设置多点触控开关,可以选择在cocos_lib/platform/CCDevice.h中添加C++静态接口,便于lua导出。
在iOS中,修改如下:
GLView* pView = Director::getInstance()->getOpenGLView();
CCEAGLView* eaglView = static_cast< CCEAGLView* >( pView->getEAGLView() );
;

在android中,修改如下:
    //1. 获取activity静态对象
    JniMethodInfo methodInfo;
    bool isHave = JniHelper::getStaticMethodInfo(methodInfo,
                                                 "org/cocos2dx/lib/Cocos2dxActivity",
                                                 "getInstance",
                                                 "()Ljava/lang/Object;");
    if (!isHave)
    {
        CCLOG("%s %d: error to get methodInfo", __FILE__, __LINE__);
        return;
    }
    jobject activityObj;
    //调用静态函数getJavaActivity,获取java类对象。
    activityObj = methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID);
    if (! JniHelper::getMethodInfo(methodInfo, "org/cocos2dx/lib/Cocos2dxActivity", "setMultipleTouchEnabled","(Z)V"))
    {
        CCLOG("%s %d: error to get methodInfo", __FILE__, __LINE__);
        return;
    }
    methodInfo.env->CallVoidMethod(activityObj, methodInfo.methodID, isEnabled);
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
        
       //2. 在cocos_lib/lib/Cocos2dxGLSurfaceView.java中过滤实现  @Override public boolean onTouchEvent() {} 中的
            case MotionEvent.ACTION_POINTER_DOWN:
            if (this.m_isMultipleTouch) 
            {
                //原代码
            }
            break;
            case MotionEvent.ACTION_POINTER_UP:
            if (this.m_isMultipleTouch) 
            {
                //原代码
            }
            break;
    部分,即根据是否支持多点实现touch事件传递。

2,手动添加touch过滤层:在需要过滤多点触控的场景中添加最高层TouchSwallowLayer,该层将过滤掉同一时间内的除第一个touch之外的其他touch,需要在touchbegan/moved/cancelled/ended中记录当前touch队列,分别设置TouchEventListener的swallow属性。该修改方法具有平台无关性,也可以在需要时才修改,但是需要添加的场景过多时比较麻烦,且不能处理多点同一个场景中既有手势操作又不支持多按钮同时响应的需求。
3,模仿CCMenuItem和CCTableView的Cell,在代码逻辑中手动添加touch标识位,仅响应touchFlag==false时的触摸事件。该方法需要和业务逻辑直接相关,添加代码较多,但能够处理同一场景内的差异化需求(有需要的,也有不需要的)。鉴于CCMenuItem和CCTableView的这一特性,一般不使用CCSrollview+自动创建的CCSprite作为Cell的table模式,否则需要添加代码过滤多个Cell同时选择的情况。

:7:好详细,受教了,第二个方法应该就是我想要的,能具体说下第二个的流程吗?先谢了

事实上如果没有同时要求有手势操作(缩放地图等)但又不能同时响应多个按钮这类需求的话,第一种方法中的setMulti属性是最方便使用的,添加的代码也最少,唯一麻烦的是native代码部分的添加。而且第二种方法的效率也相对低一些。
第二种方法的测试示例代码如下:
定义一个oneByOne类型地监视器 listen_ = EventListenerTouchOneByOne::create();
实现以下四个回调处理
bool ClipLayer::onTouchBegan(cocos2d::Touch touch, cocos2d::Event unused_event)
{
/

if (rect_.containsPoint(touch->getLocation()))
{
listen_->setSwallowTouches(false);
}
else
{
listen_->setSwallowTouches(true);
}
/
assert(touchList_.end()==std::find(touchList_.begin(), touchList_.end(), touch));
if (touchList_.size()<=0)
{
CCLOG(“First touch CAN be responsed in began”);
listen_->setSwallowTouches(false);
}
else
{
CCLOG(“Other touch CANNOT be responsed in began”);
listen_->setSwallowTouches(true);
}
touchList_.push_back(touch);
return true;
}

void ClipLayer::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event unused_event)
{
/

if (rect_.containsPoint(touch->getLocation()))
{
listen_->setSwallowTouches(false);
}
else
{
listen_->setSwallowTouches(true);
}
*/
assert(touchList_.end() != std::find(touchList_.begin(), touchList_.end(), touch));
if (touchList_.front() == touch)
{
CCLOG(“First touch CAN be responsed in moved”);
listen_->setSwallowTouches(false);
}
else
{
CCLOG(“Other touch CANNOT be responsed in moved”);
listen_->setSwallowTouches(true);
}
}

void ClipLayer::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event)
{
assert(touchList_.end() != std::find(touchList_.begin(), touchList_.end(), touch));
if (touchList_.front() == touch)
{
CCLOG(“First touch CAN be responsed in ended”);
listen_->setSwallowTouches(false);
}
else
{
CCLOG(“Other touch CANNOT be responsed in ended”);
listen_->setSwallowTouches(true);
}
touchList_.remove(touch);
}

void ClipLayer::onTouchCancelled(cocos2d::Touch *touch, cocos2d::Event *unused_event)
{
assert(touchList_.end() != std::find(touchList_.begin(), touchList_.end(), touch));
if (touchList_.front() == touch)
{
CCLOG(“First touch CAN be responsed in cancelled”);
listen_->setSwallowTouches(false);
}
else
{
listen_->setSwallowTouches(true);
CCLOG(“Other touch CANNOT be responsed in cancelled”);
}
touchList_.remove(touch);
}

即可得到只响应第一个touch的遮罩层,限制仅仅在指定区域内才可点击的需求也可以用这种方法实现——区别在于判断条件改为touch坐标是否在指定多边形内,可用于新手引导的箭头遮罩层。
注:由于记录的第一个touch在touchEnded和cancelled之后就从列表中移除了,没有继续保存,所以当前第一个touch结束之后开始响应列表中的第二个touch(之前的第二个在第一个被移除之后就变成第一个了);且存在有在没有向下传递began的情况下直接传递了moved/cancelled/ended的bug。如果想要只处理第一个,需要持续保存第一个touch指针,当队列为空时才复位。

:7:方法二已测,太赞了!完美解决。关于你说的效率问题可能是有,但我游戏项目中是希望主城中单点(因为UI界面按钮多,多点会出一些bug),战斗中是需要多来操作的。所以第一种方法就像你说的无法在游戏中重新的打开多点触控,不能满足需求