[置顶] cocos2d-x2.1.5中实现手势识别(各种形状,单击,双击)

网上搜索cocos2d-x方面的手势识别资料相对较少,即使有也是着重讲的单击以及双击的的实现方法。

而记得有一篇提及了在cocos2d下手势识别的一些工具类。其中提到了$1Unistroke Recognizer手势识别

官方网站:http://depts.washington.edu/aimgroup/proj/dollar/

参考资料:http://blog.sina.com.cn/s/blog_61ece09901017ngf.html


教程代码下载链接:http://download.csdn.net/detail/mr_dodododo/6225657

压缩包中存在文件:

官方:

GeometricRecognizer.cpp(主要手势判断逻辑类)

GeometricRecognizer.h

GeometricRecognizerTypes.h(自定义对象)

GestureTemplate.h(模板存储)

SampleGestures.h(模板)

使用:

CTouchManager.cpp

CTouchManager.h


直接开门见山的讲讲 官方提供的C++代码的实现原理:

由于官方提供代码中方法较多,我就不根据代码来一一说明,只介绍大概的方法流程,方便大家理解,具体实现,可以自行学习。

使用之前:

需要使用者自己配置形状模板的点集

初始化:

1.加载模板的各个形状

2.将各形状点集“标准化”。(若使用者不自行更改,会将其处理为128个点组成的平滑形状。可在GeometricRecognizer构造函数中更改其值)

使用判断形状:

1.获得用户所画形状的点集

2.将点集“标准化”(若使用者不自行更改,会将其处理为128个点组成的平滑形状)

3.将“标准化”后的点集与初始化时加载的形状模板进行对比。最终得到一个最佳结果返回(存在一个角度误差值,值越大形状包容性越大,值越小形状越具有唯一性,根据具体需求自行改变。主要是在setRotationInvariance(bool ignoreRotation)函数中设置angleRange的值


如何使用:

主要参见CTouchManager类中的使用方法。

CTouchManager.cpp

CTouchManager* CTouchManager::create( CCObject* pTarget, Touch_ClickCallBack callbackClick, Touch_GestureCallBack callbackGesture )
{
    CTouchManager* mTouchManager = new CTouchManager();
    if( mTouchManager && mTouchManager->init( pTarget, callbackClick, callbackGesture ) ){
        return mTouchManager;
    }
    else{
        delete mTouchManager;
        return NULL;
    }
    return NULL;
}

bool CTouchManager::init( CCObject* pTarget, Touch_ClickCallBack callbackClick, Touch_GestureCallBack callbackGesture )
{
    if ( !CCLayer::init() )
    {
        return false;
    }
    m_pClickCallBack = callbackClick;
    m_pGestureCallBack = callbackGesture;
    m_pTarget = pTarget;

    m_p2dEndTouch = new CCPoint();
    setTouchEnabled(true);
    m_pGeometricRecognizer = new GeometricRecognizer();
    m_pGeometricRecognizer->loadTemplates();
    m_bClicked = false;
    m_pEffectRoot = CCNode::create();
    this->addChild( m_pEffectRoot,10 );

    return true;
}

void CTouchManager::registerWithTouchDispatcher()
{
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, false);
}
bool CTouchManager::ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent )
{
    CCParticleSystemQuad* mpEffect = CCParticleSystemQuad::create("TouchEffect/TouchEffect.plist");
    m_pEffectRoot->addChild(mpEffect);
    mpEffect->setPosition( 0, 0 );
    EffectFollow( pTouch );
    m_lClickedTime = MilliSecondNow();
    return true;
}
void CTouchManager::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent){
    EffectFollow( pTouch );
    FingerRecord( pTouch );
}
void CTouchManager::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent){
    m_pEffectRoot->removeAllChildren();
    CCPoint mTmp = pTouch->getLocationInView();
    m_p2dEndTouch->x = mTmp.x;
    m_p2dEndTouch->y = mTmp.y;
    if( MilliSecondNow() - m_lClickedTime < 200 ){
        if ( m_bClicked ) {
            DoubleClick();
        }
        else
        {
            scheduleOnce(schedule_selector(CTouchManager::Click), 0.2f);
            m_bClicked = true;
            return;
        }
    }
    else{
        FingerJudge();
    }
    DoSomething();
}
void CTouchManager::ccTouchCancelled( CCTouch *pTouch, CCEvent *pEvent )
{
    m_bClicked = false;
    m_p2dPath.clear();
}

void CTouchManager::Click( float fTime )
{
    if( m_bClicked ){
        m_nInputKind = E_INPUT_KIND_ONE_CLICK;
        m_bClicked = false;
        DoSomething();
    }
}

void CTouchManager::DoubleClick()
{
    m_bClicked = false;
    m_nInputKind = E_INPUT_KIND_DOUBLE_CLICK;
}

long CTouchManager::MilliSecondNow()
{
    struct cc_timeval now; 
    CCTime::gettimeofdayCocos2d(&now, NULL); 
    return (now.tv_sec * 1000 + now.tv_usec / 1000); 
}

void CTouchManager::FingerJudge()
{
    if( m_p2dPath.size() < 1 ){
        m_nInputKind = E_INPUT_KIND_ONE_CLICK;
        return;
    }
    RecognitionResult mResult = m_pGeometricRecognizer->recognize(m_p2dPath);
    m_nInputKind = mResult.name;
}

void CTouchManager::FingerRecord(CCTouch* pTouch)
{
    CCPoint mLocation = pTouch->getLocationInView();
    Point2D mPoint2DTmp;
    mPoint2DTmp.x = mLocation.x;
    mPoint2DTmp.y = mLocation.y;
    m_p2dPath.push_back(mPoint2DTmp);
}

void CTouchManager::DoSomething()
{
    if( m_nInputKind == E_INPUT_KIND_ONE_CLICK || m_nInputKind == E_INPUT_KIND_DOUBLE_CLICK ){
        (m_pTarget->*m_pClickCallBack)( m_nInputKind, m_p2dEndTouch );
    }
    else{
         (m_pTarget->*m_pGestureCallBack)( m_nInputKind );
    }
    m_p2dPath.clear();
}

void CTouchManager::EffectFollow( CCTouch* pTouch )
{
    CCPoint mTmp = pTouch->getLocation();
    m_pEffectRoot->setPosition( mTmp.x, mTmp.y );
}

为了降低程序耦合度,方便大家使用,我在这里使用回调函数的方式来处理。关于回调函数的使用,不清楚的百度一下~~

简单的解释下主要函数的作用:


1.create( CCObject* pTarget, Touch_ClickCallBack callbackClick, Touch_GestureCallBack callbackGesture )以及

init( CCObject* pTarget, Touch_ClickCallBack callbackClick, Touch_GestureCallBack callbackGesture )

主要作用是初始化CTouchManager,由于其继承CCLayer,所以需要注意的是记得开启输入检测setTouchEnabled(true)

其次是我这里使用的虚函数方式。在我们需要用到手势检测的scene或者layer中,传入其对象以及其要被回调的函数来初始化CTouchManager类。


2.registerWithTouchDispatcher()

ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent )

ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)

ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)

ccTouchCancelled( CCTouch *pTouch, CCEvent *pEvent )

这几个函数就不用多说了,继承CCLayer下来的几个cocos2d-x中相应单点输入的虚函数。我们主要在这几个函数中获取输入点。

a.在Began中,我加入了一个粒子效果方便在测试的时候观察绘制轨迹。

在其中,主要还记录了输入按下的系统时间,方便单击、双击与画形状的判断

b.在Move中,这里是手势判断的重点所在,主要在这里获取玩家绘制轨迹的点集。(同时让粒子跟随移动)

void CTouchManager::FingerRecord(CCTouch* pTouch)
{
    CCPoint mLocation = pTouch->getLocationInView();
    Point2D mPoint2DTmp;
    mPoint2DTmp.x = mLocation.x;
    mPoint2DTmp.y = mLocation.y;
    m_p2dPath.push_back(mPoint2DTmp);
}
获得点集后将其压入我们自定义的对象中(其实就是个vector)

c.在End中,主要就是判断手势结果了。单击、双击、以及图形判断。这里双击、单击的判断使用了一个cocos2dx中的一个延时处理函数scheduleOnce。它的主要作用就是用来做双击和单击的判断。简单的说,就是要在单击后的200毫秒以内再次点击就算作双击,若没有,则作为单击判断。同事,若第一次按下和抬起时间大于200毫秒,则就进行手势形状的判断。(同时在这里记录了一个结束手势的点记录,主要用于单击和双击事件中可能需要得到点坐标来进行一些处理,比如移动或者向目标点释放技能等等)

手势判断最后是需要DoSomething

void CTouchManager::DoSomething()
{
    if( m_nInputKind == E_INPUT_KIND_ONE_CLICK || m_nInputKind == E_INPUT_KIND_DOUBLE_CLICK ){
        (m_pTarget->*m_pClickCallBack)( m_nInputKind, m_p2dEndTouch );
    }
    else{
         (m_pTarget->*m_pGestureCallBack)( m_nInputKind );
    }
    m_p2dPath.clear();
}
这里就是在初始化时传入的对象来调用回调函数。之后做什么事情就根据你的具体需求在对应对象中具体实现了。


基本的使用就是这样,在代码压缩包中有使用的例子。

总结:这篇手势识别主要是建立在$1Unistroke Recognizer现成的手势算法基础上的使用,其实技术含量并不高,主要是帮助大家更快速的理解和使用它。


你可能感兴趣的:(ios,移动,VS2010,手势识别,cocos2d-x)