2016年4月11日 星期一

20160411_Cocos2d-x 3.x 多指觸控實現移動、拉伸、旋轉





Cocos2d-x 3.x 多指觸控實現移動、拉伸、旋轉


http://www.shuizilong.com/house/archives/cocos2d-x-3-x-%E5%A4%9A%E6%8C%87%E8%A7%A6%E6%8E%A7%E5%AE%9E%E7%8E%B0%E7%A7%BB%E5%8A%A8%E3%80%81%E6%8B%89%E4%BC%B8%E3%80%81%E6%97%8B%E8%BD%AC/


使用心得:
在IOS上 會有問題!
1. REP(i, _pos.size()){_pot[i]->setPosition(new_pos[i]);} 會異常
2.無法同時登陸兩個touch even, 會被原本的蓋掉。
3.改為繼承Layer 一樣不行!等待日後來修改
介紹:
我們知道 Mac 中預覽圖片的時候可以使用多指觸控對圖片進行拉伸和旋轉。
在 iOS 中、我們可以讓手指在圖中的相對位置保持不變,從而將這三個操作統一起來,更方便的實現編輯功能。
演示地址:http://www.pgyer.com/Hu8O
參考資料:cpp-test 中的 multitouch
To-do: 如何支持沿著一個方向的拉伸(三指?)

代碼:

首先定義支持編輯操作的 MySprite 類。
#define PB push_back
#define REP(i,n) for(int i=0,_##i=(n);i<_##i;++i )

#define PI 3.1415296
struct MySprite : public Sprite{
     
    static MySprite* _target; static vector<Vec2> _pos; static vector<Node*> _pot;
    static double _dist2, _atan2;
     
    bool isMoveable, isZoomable, isRotatable;
    void onTouchesBegan(const std::vector<Touch*>& touches, cocos2d::Event  *event);
    void onTouchesMoved(const std::vector<Touch*>& touches, cocos2d::Event  *event);
    void onTouchesEnded(const std::vector<Touch*>& touches, cocos2d::Event  *event);
    void onTouchesCancelled(const std::vector<Touch*>& touches, cocos2d::Event  *event);
     
    void init2(bool,bool,bool);
};

注意到不同於單點觸控、多點觸控的 onTouchesBegan() 方法似乎沒有返回函數、因此我們需要手動紀錄一下 _target。
當作用在多個對象上時,只操作最上層的。
_dist2 用來記錄兩指之間的距離,用來實現放大。
_atan2 用來記錄兩隻之間的角度,用來實現旋轉。

#include "HelloWorldScene.h"
#include "cocostudio/CocoStudio.h"
#include "ui/CocosGUI.h"
#include "Audio.h"
#include "Template.h"
#include "Global.h"
USING_NS_CC;
void addAroundEdge(Sprite* t){
    Size s = t->getContentSize();
    auto e = DrawNode::create();
     
    auto radius = 1;
    auto color = Color4F(0, 0, 0, 1.0f);
     
    auto lb = Vec2(0, 0), rb = Vec2(s.width, 0);
    auto lt = Vec2(0, s.height), rt = Vec2(s.width, s.height);
     
    e->drawSegment(lb, rb, radius, color);
    e->drawSegment(lb, lt, radius, color);
    e->drawSegment(rb, rt, radius, color);
    e->drawSegment(lt, rt, radius, color);
    e->setName("edge");
    t->addChild(e);
}
/*
void MySprite::init(string s){
}
*/
vector<Vec2> MySprite::_pos;
vector<Node*> MySprite::_pot;
MySprite* MySprite::_target;
DB MySprite::_atan2, MySprite::_dist2;
  
void MySprite::init2(bool _isMoveable = 1, bool _isZoomable = 1, bool _isRotatable = 1){
     
    isMoveable = _isMoveable;
    isZoomable = _isZoomable;
    isRotatable = _isRotatable;
     
    auto listener = EventListenerTouchAllAtOnce::create();
     
    listener->onTouchesBegan = CC_CALLBACK_2(MySprite::onTouchesBegan, this);
    listener->onTouchesMoved = CC_CALLBACK_2(MySprite::onTouchesMoved, this);
    listener->onTouchesEnded = CC_CALLBACK_2(MySprite::onTouchesEnded, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener->clone(), this);
     
    addAroundEdge(this);
}
static const Color3B* s_TouchColors[5] = {
    &Color3B::YELLOW,
    &Color3B::BLUE,
    &Color3B::GREEN,
    &Color3B::RED,
    &Color3B::MAGENTA
};
struct TouchPoint : public Node
{
     
     
    TouchPoint(const Vec2 &touchPoint, const Color3B &touchColor)
    {
        DrawNode* drawNode = DrawNode::create();
        auto s = Director::getInstance()->getWinSize();
        Color4F color(touchColor.r/255.0f, touchColor.g/255.0f, touchColor.b/255.0f, 1.0f);
         
        /*drawNode->drawLine(Vec2(-s.width, touchPoint.y), Vec2(s.width, touchPoint.y), color);
        drawNode->drawLine(Vec2(touchPoint.x, -s.height), Vec2(touchPoint.x, s.height), color);
        drawNode->drawDot(touchPoint, 3, color);*/
         
        drawNode->drawLine(Vec2(-s.width, 0), Vec2(s.width, 0), color);
        drawNode->drawLine(Vec2(0, -s.height), Vec2(0, s.height), color);
        drawNode->drawDot(Vec2(0, 0), 3, color);
        addChild(drawNode);
    }
     
    static TouchPoint* touchPointWithParent(Node* pParent, const Vec2 &touchPoint, const Color3B &touchColor)
    {
        auto pRet = new (std::nothrow) TouchPoint(touchPoint, touchColor);
        pRet->setContentSize(pParent->getContentSize());
        pRet->setAnchorPoint(Vec2(0.0f, 0.0f));
        pRet->autorelease();
        return pRet;
    }
};
void MySprite::onTouchesBegan(const std::vector<Touch*>& touches, Event  *event)
{
    auto target = static_cast<MySprite*>(event->getCurrentTarget());
    for (auto &touch: touches)
    {
        auto location = touch->getLocation();
        Point locationInNode = target->convertToNodeSpace(touch->getLocation());
        Size s = target->getContentSize();
        Rect rect = Rect(0, 0, s.width, s.height);
        if (!rect.containsPoint(locationInNode)){
            return;
        }
    }
     
    if (_target != nullptr && _target != target) return;
     
    for (auto &touch: touches)
    {
        auto location = touch->getLocation();
        Point locationInNode = target->convertToNodeSpace(touch->getLocation());
         
        //點擊範圍判斷檢測
        Size s = target->getContentSize();
        Rect rect = Rect(0, 0, s.width, s.height);
         
        auto touchPoint = TouchPoint::touchPointWithParent(this, location, *s_TouchColors[touch->getID()%5]);
        touchPoint->setName("touchPoint");
        Global::game->addChild(touchPoint);
        touchPoint->setPosition(location);
         
        _pos.PB(location), _pot.PB(touchPoint);
    }
     
    _target = target;
     
    if (_pos.size() == 2){
        //log("%.2f %.2f %.2f %.2f\n", _pos[0].x, _pos[0].y, _pos[1].x, _pos[1].y);
        _dist2 = _pos[0].getDistance(_pos[1]);
        Vec2 d = _pos[1] - _pos[0];
        _atan2 = atan2(d.y, d.x);
    }
}
void MySprite::onTouchesMoved(const std::vector<Touch*>& touches, Event  *event)
{
     
    auto target = static_cast<MySprite*>(event->getCurrentTarget());
    if (target != _target) return;
    if (_pos.empty()) return;
     
    vector<Vec2> new_pos;
     
    for( auto &item: touches)
    {
         
        //if (state[item] == false) continue;
         
        auto touch = item;
        auto location = touch->getLocation();
        Point locationInNode = target->convertToNodeSpace(touch->getLocation());
        //點擊範圍判斷檢測
         
        Size s = target->getContentSize();
        Rect rect = Rect(0, 0, s.width, s.height);
        //if (!rect.containsPoint(locationInNode)) continue;
        new_pos.PB(location);
    }
     
    if (_pos.size() != new_pos.size()) return;
     
    REP(i, _pos.size()){
        _pot[i]->setPosition(new_pos[i]);
    }
     
    if (_pos.size() == 1){ // 平移...
//        log("%.2f %.2f  %.2f %.2f", target->getPosition().x, target->getPosition().y, (t->getPosition() - target->_pos).x , (t->getPosition() - target->_pos).y );
        target->setPosition(target->getPosition() + (new_pos[0] - _pos[0]));
        _pos = new_pos;
    }
    else if (_pos.size() == 2){
        auto target = static_cast<Sprite*>(event->getCurrentTarget());
        auto new_dist2 = new_pos[0].getDistance(new_pos[1]);
        target->setScale((DB)target->getScale()/_dist2*new_dist2);
        _dist2 = new_dist2;
        //log("%.2f %.2f %.2f\n", _dist2, new_dist2, target->getScale()/_dist2*new_dist2);
        Vec2 d = new_pos[1] - new_pos[0];
        DB new_atan2 = atan2(d.y, d.x);
        target->setRotation( target->getRotation() -  (new_atan2 - _atan2) / PI * 180);
        _atan2 = new_atan2;
    }
}
void MySprite::onTouchesEnded(const std::vector<Touch*>& touches, Event  *event)
{
     
    auto target = static_cast<MySprite*>(event->getCurrentTarget());
     
    while (Global::game->getChildByName("touchPoint") != nullptr){
        Global::game->getChildByName("touchPoint")->removeFromParent();
    }
    _pos.clear(); _pot.clear();
    _target = nullptr;
     
     
    /*for ( auto &item: touches )
    {
        //if (state[item] == false) continue;
         
        auto touch = item;        auto pTP = s_map.at(target);
        
        if (pTP == nullptr) continue;
         
        pTP->removeFromParent();
        s_map.erase(target);
    }*/
     
     
    /*for ( auto &item: touches ){
        state.erase(item);
    }*/
}
void MySprite::onTouchesCancelled(const std::vector<Touch*>& touches, Event  *event)
{
    onTouchesEnded(touches, event);
}
void LongTouch::upd(){
    if (counter > 0){
        if (--counter == 0){
            Audio::playSE("coin.mp3");
        }
    }
}
Scene* HelloWorld::createScene()
{
    auto scene = Scene::create();
    auto layer = HelloWorld::create();
    scene->addChild(layer);
    return scene;
}
void HelloWorld::update(float dt){
    longTouch->upd();
    return;
}
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !LayerColor::initWithColor(Color4B(255, 255, 255, 255)))
    {
        return false;
    }
     
     
    Global::game = this;
     
    longTouch = new LongTouch;
     
    auto size = Director::getInstance()->getWinSize();
     
    auto t1 = static_cast<MySprite*>(Sprite::create("star_on.png")); t1->init2();
    t1->setPosition(size / 2); addChild(t1);
     
    auto t2 = static_cast<MySprite*>(Sprite::create("star_on.png")); t2->init2();
    t2->setPosition(Vec2(size.width / 2 - 100, size.height / 2 - 100) ); addChild(t2);
     
     
    auto t3 = static_cast<MySprite*>(Sprite::create("star_on.png")); t3->init2();
    t3->setPosition(Vec2(size.width / 2 + 100, size.height / 2 + 100) ); addChild(t3);
     
     
    /*
    auto _touchListener = EventListenerTouchOneByOne::create();
    _touchListener->setSwallowTouches(true);
     
    _touchListener->onTouchBegan = [&](Touch* touch, Event* event){
         
        auto target = static_cast<Sprite*>(event->getCurrentTarget());
        Point locationInNode = target->convertToNodeSpace(touch->getLocation());
        Size s = target->getContentSize();
        Rect rect = Rect(0, 0, s.width, s.height);
         
        //點擊範圍判斷檢測
        if (rect.containsPoint(locationInNode))
        {
            longTouch->counter = 20;
            longTouch->target = target;
            return true;
        }
        return false;
    };
     
    _touchListener->onTouchEnded = [&](Touch* touch, Event* event){
        if (longTouch->counter == 0) return;
        longTouch->counter = 0;
        Audio::playSE("error.mp3");
    };
     
    _eventDispatcher->addEventListenerWithSceneGraphPriority(_touchListener->clone(), t);
    scheduleUpdate(); */
    return true;
}

沒有留言:

張貼留言

cocos2dx-lua 建立滑鼠監聽

重要關鍵字  EVENT_MOUSE_SCROLL addEventListenerWithSceneGraphPriority      if IsPc() then --建立滑鼠監聽         local listener = cc.EventListenerMouse...