使用cocos2dx-v3实现简单地图缩放

来源:互联网 发布:域名注册通 吾爱破解 编辑:程序博客网 时间:2024/06/05 16:15

  之前使用cocos2dx 3.x做了一个简单的地图缩放功能,也遇到了很多坑,在此发表出来希望看到文章的人以后避免遇到一些类似的坑,写的不好大家不要喷啊。多的不说了,上代码。

  首先是.h文件:

#ifndef __HELLOWORLD_SCENE_H__

#define __HELLOWORLD_SCENE_H__


#include "cocos2d.h"


class HelloWorld : public cocos2d::Layer

{

    HelloWorld();

public:

    // there's no 'id' in cpp, so we recommend returning the class instance pointer

    staticcocos2d::Scene* createScene();

    

    CREATE_FUNC(HelloWorld);


    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone

    virtualbool init()override;

    

private:

    //添加触摸事件

    void addTouchEvent(cocos2d::Node*);

    

    //回滚

    void scrollBack();

    

    //多点触摸move事件

    void onTouchesBeginCallback(conststd::vector<cocos2d::Touch*>&,cocos2d::Event*);

    void onTouchesMovedCallback(conststd::vector<cocos2d::Touch*>&,cocos2d::Event*);

    void onTouchesEndedCallback(conststd::vector<cocos2d::Touch*>&,cocos2d::Event*);

    void onTouchesCancelCallback(conststd::vector<cocos2d::Touch*>&,cocos2d::Event*);

    

    void touchScale(cocos2d::Vec2,cocos2d::Vec2);

private:

    cocos2d::Sprite* m_pView =nullptr;//地图节点

    float m_fMinScale =0.0f;//最小缩放值

    float m_fMaxScale =1.0f;//最大缩放值

    float m_fScale =0.0f;//当前缩放值

    float m_fDistance =0.0f;//位移

    

    cocos2d::Vec2 m_pMiddlePoint;//触摸中点

    cocos2d::Vec2 m_pBoderOrigin;//左下角的边界点

    cocos2d::Vec2 m_pBoderMax;//右上角的边界点

    

    std::map<int,cocos2d::Vec2> m_touchIdPointMap;//触摸点的ID:pos map

    std::vector<int> m_touchIdVec;//触摸点ID列表

};


#endif

  其次是.cpp文件:

#include "HelloWorldScene.h"


USING_NS_CC;


HelloWorld::HelloWorld()

{

}


Scene* HelloWorld::createScene()

{

    auto scene =Scene::create();

    auto layer =HelloWorld::create();

    scene->addChild(layer);

    return scene;

}


bool HelloWorld::init()

{

    if ( !Layer::init() )

    {

        returnfalse;

    }

    

    Size visibleSize =Director::getInstance()->getVisibleSize();

    Vec2 origin =Director::getInstance()->getVisibleOrigin();

    

    auto sprite =Sprite::create("HelloWorld.png");

    

    m_pView = sprite;

    

    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    

    auto scaleX = visibleSize.width / sprite->getContentSize().width;

    auto scaleY = visibleSize.height / sprite->getContentSize().height;

    auto scale =std::max(scaleX, scaleY);

    sprite->setScale(scale);

    

    this->addChild(sprite,0);

    

    m_pBoderOrigin = origin;

    m_pBoderMax = visibleSize;

    

    //设置最小缩放值和最大缩放值

    m_fScale = sprite->getScale();

    m_fMinScale =m_fScale;

    m_fMaxScale =m_fScale*10.0f;

    

    addTouchEvent(sprite);

    

    returntrue;

}


void HelloWorld::addTouchEvent(cocos2d::Node* pNode)

{

    if(nullptr == pNode )return;

    

    auto pEventListener =EventListenerTouchAllAtOnce::create();

    pEventListener->setEnabled(true);

    pEventListener->onTouchesBegan =CC_CALLBACK_2(HelloWorld::onTouchesBeginCallback,this);

    pEventListener->onTouchesMoved =CC_CALLBACK_2(HelloWorld::onTouchesMovedCallback,this);

    pEventListener->onTouchesEnded =CC_CALLBACK_2(HelloWorld::onTouchesEndedCallback,this);

    pEventListener->onTouchesCancelled =CC_CALLBACK_2(HelloWorld::onTouchesCancelCallback,this);

    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(pEventListener, pNode);

}


//回滚,当超出边界的时候需要朝相应方向移动到边界处,这里面主要是清楚坐标系的转换就很好做了

void HelloWorld::scrollBack()

{

    if(nullptr ==m_pView)return;

    

    auto origin =m_pView->convertToWorldSpace(Vec2::ZERO);

    origin = m_pView->getParent()->convertToNodeSpace(origin);

    

    auto curBorderMax =m_pView->convertToWorldSpace(Vec2::ZERO+m_pView->getContentSize());

    curBorderMax = m_pView->getParent()->convertToNodeSpace(curBorderMax);

    

    auto newPosition =m_pView->getPosition();

    

    if (origin.y >m_pBoderOrigin.y)

    {

        newPosition.y =m_pBoderOrigin.y + (curBorderMax.y - origin.y) *m_pView->getAnchorPoint().y;

    }

    

    if(curBorderMax.y <m_pBoderMax.y)

    {

        newPosition.y =m_pBoderMax.y - (curBorderMax.y - origin.y)*(1.0f -m_pView->getAnchorPoint().y);

    }

    

    if(origin.x >m_pBoderOrigin.x)

    {

        newPosition.x =m_pBoderOrigin.x + (curBorderMax.x - origin.x) *m_pView->getAnchorPoint().x;

    }

    

    if(curBorderMax.x <m_pBoderMax.x)

    {

        newPosition.x =m_pBoderMax.x - (curBorderMax.x - origin.x)*(1.0f -m_pView->getAnchorPoint().x);

    }

    m_pView->runAction(MoveTo::create(0.2f, newPosition));

}

void HelloWorld::onTouchesBeginCallback(conststd::vector<cocos2d::Touch*>& pTouches,cocos2d::Event*)

{

    if(m_pView)

    {

        m_pView->stopAllActions();

    }

    for (auto i =0; i < pTouches.size(); ++i)

    {

        auto pTouch = pTouches[i];

        m_touchIdVec.push_back(pTouch->getID());

        m_touchIdPointMap[pTouch->getID()] = pTouch->getLocation();

    }

    if(m_touchIdVec.size() >=2)

    {

        auto touchPoint =m_touchIdVec.begin();

        auto mPoint1 =m_touchIdPointMap[(*touchPoint)];

        ++touchPoint;

        auto mPoint2 =m_touchIdPointMap[(*touchPoint)];

        

        if(m_fDistance ==0)

        {

            m_fDistance = mPoint1.distance(mPoint2);

            m_pMiddlePoint = mPoint1.getMidpoint(mPoint2);

        }

    }

}


//缩放或者移动前的判断

void HelloWorld::onTouchesMovedCallback(conststd::vector<cocos2d::Touch*>& pTouches,cocos2d::Event*)

{

    if(pTouches.size() >=2)

    {

        for(auto iter : pTouches)

        {

            if(std::find(m_touchIdVec.begin(),m_touchIdVec.end(), iter->getID()) ==m_touchIdVec.end())

            {

                m_touchIdVec.push_back(iter->getID());

            }

            

            m_touchIdPointMap[iter->getID()] = iter->getLocation();

        }

        

        auto touchPoint =m_touchIdVec.begin();

        auto mPoint1 =m_touchIdPointMap[(*touchPoint)];

        ++touchPoint;

        auto mPoint2 =m_touchIdPointMap[(*touchPoint)];

        

        if(m_fDistance ==0)

        {

            m_fDistance = mPoint1.distance(mPoint2);

            m_pMiddlePoint = mPoint1.getMidpoint(mPoint2);

            return;

        }

        

        touchScale(mPoint1, mPoint2);

    }

    elseif(pTouches.size() ==1)

    {

        auto iter = pTouches.cbegin();

        auto endPoint =m_pView->getParent()->convertTouchToNodeSpace(*iter);

        

        if(std::find(m_touchIdVec.begin(),m_touchIdVec.end(), (*iter)->getID()) ==m_touchIdVec.end())

        {

            m_touchIdVec.push_back((*iter)->getID());

            m_touchIdPointMap[(*iter)->getID()] = (*iter)->getLocation();

            return;

        }

        

        if(m_touchIdVec.size() ==1)

        {

            auto pos1 =m_touchIdPointMap[(*iter)->getID()];

            m_touchIdPointMap[(*iter)->getID()] =(*iter)->getLocation();

            auto pos = endPoint - pos1 +m_pView->getPosition();

            m_pView->setPosition(pos);

            m_fDistance =0;

        }

        else if(m_touchIdVec.size() >=2)

        {

            m_touchIdPointMap[(*iter)->getID()] =(*iter)->getLocation();

            auto touchPoint =m_touchIdVec.begin();

            auto mPoint1 =m_touchIdPointMap[(*touchPoint)];

            ++touchPoint;

            auto mPoint2 =m_touchIdPointMap[(*touchPoint)];

            touchScale(mPoint1, mPoint2);

        }

    }

}


//点触摸结束要及时删除掉

void HelloWorld::onTouchesEndedCallback(conststd::vector<cocos2d::Touch*>& pTouches,cocos2d::Event*)

{

    for(auto iter : pTouches)

    {

        auto touchID = iter->getID();

        if(m_touchIdPointMap.find(touchID) !=m_touchIdPointMap.end() )

        {

            m_touchIdPointMap.erase(touchID);

        }

        auto idIter =std::find(m_touchIdVec.begin(),m_touchIdVec.end(), touchID );

        if( idIter !=m_touchIdVec.end() )

        {

            m_touchIdVec.erase( idIter );

        }

    }

    if(m_touchIdPointMap.empty())

    {

        m_fDistance =0;

        scrollBack();

    }

    elseif(m_touchIdPointMap.size() >=2)

    {

        auto touchPoint =m_touchIdVec.begin();

        auto mPoint1 =m_touchIdPointMap[*touchPoint];

        ++touchPoint;

        auto mPoint2 =m_touchIdPointMap[*touchPoint];

        m_fDistance = mPoint1.distance(mPoint2);

        m_pMiddlePoint = mPoint1.getMidpoint(mPoint2);

    }

}


void HelloWorld::onTouchesCancelCallback(conststd::vector<cocos2d::Touch*>& pTouches,cocos2d::Event*)

{

    scrollBack();

    m_fDistance =0;

    m_touchIdVec.clear();

    m_touchIdPointMap.clear();

}


//缩放的核心代码,效果还行,虽然不是特别理想,但是还能用,核心思想其实就是计算两点之间的距离跟上一次的距离的比例,再根据这个比例计算出新的的缩放值,还有

//就是两点之间的中心点有移动,这时候还要加上移动的相关处理,否则地图缩放的很硬,效果不好

void HelloWorld::touchScale(cocos2d::Vec2 mPoint1,cocos2d::Vec2 mPoint2)

{

    auto newdis = mPoint1.distance(mPoint2);

    

    m_fScale = newdis *m_fScale /m_fDistance;

    

    auto midPoint = mPoint1.getMidpoint(mPoint2);

    auto curMidNodePoint =m_pView->getParent()->convertToNodeSpace(midPoint);

    auto oldMidNodePoint =m_pView->getParent()->convertToNodeSpace(m_pMiddlePoint);

    auto newPosition =2 * curMidNodePoint - oldMidNodePoint;


    midPoint = m_pView->convertToNodeSpace(midPoint);

    auto newAnchorPoint =Vec2(midPoint.x/m_pView->getContentSize().width, midPoint.y/m_pView->getContentSize().height);

    m_fScale =std::max(m_fScale,m_fMinScale);

    auto newScale =m_pView->getScale() + (m_fScale -m_pView->getScale()) *0.25f;//0.25是我调试的值,可以根据需要自己调整

    if(std::abs(newScale-m_pView->getScale()) > 0.01f)

    {

        m_pView->setScale(newScale);

    }

//要注意锚点改变的话,如果位置不改变图会跳,千万注意,至于为什么要改变锚点是为了让他缩放看起来是在两点之间缩放的而不是在其他的点

//这个理解锚点的定义应该都知道

    m_pView->setPosition(newPosition);

    m_pView->setAnchorPoint(newAnchorPoint);

    m_pMiddlePoint = mPoint1.getMidpoint(mPoint2);

    m_fDistance = newdis;

}

代码贴完了,说一说遇到的坑吧:

1. 刚开始没意识到一点是多点触摸的onTouchesBeginCallback回调的那个数组传过来的点是不确定的,不是多个,后来一想也是,你按下去一个手指,肯定也要传过来啊,不能等有俩手指的时候才调这个回调吧。

2. 安卓和iOS的onTouchesMovedCallback回调不统一,兼容有问题,安卓的话他是有几个手机触摸就传递来几个手指,但是iOS不是,他是你移动了那个手指他才传递哪个手指的数据,比如有两个手指按压在触摸屏上,安卓会传过来俩点,但是iOS只传递移动的那个手指的数据,即使另外一个手指没离开屏幕,注意这是很重要的一个点,这也是为什么有的人在缩放的时候图片会跳的原因(当然也可能是其他原因造成的,这个原因也是一个调试方向)。

3. onTouchesEndedCallback回调跟onTouchesCancelCallback回调的情况跟onTouchesBeginCallback类似,都是你同时离开屏幕几个手指就有几个点的数据传递来。

4. 还有就是边缘检测的问题了,移动或者缩放都可能导致图片不能完全包裹整个边界,我的做法是哪个边界超出了,就紧贴哪个边界,如下图所示


好了,讲到这还有源码,基本的地图缩放功能讲完了,还有遇到的大坑,希望看到的人能少遇到点坑。
0 0
原创粉丝点击