Search code examples
androidc++cocos2d-xcocos2d-x-3.0cocos2d-android

Cocos2d-x onTouchMoved called when no movement is occurring


I'm writing a cocos2d-x 3.71 cross-platform app (ios, android, windows10) and am having trouble with my implementation of touch and hold detection on Android only (ios and windows work just fine). I have tracked the reason for this down to the fact that when holding a touch down and not moving, onTouchMoved() is firing off constantly. I have incorporated a work-around, but this seems like a bug and I'm wondering if anyone has any ideas.

I recreated the problem in a minimal app based on the cocos2d-x HelloWorld example. I simply derived a class from cocos2d::Node (testNode) and created it and added it as a child to the HelloWorldScene. In testNode, I override the update function and schedule it with scheduleUpdate() from onTouchBegan(). In the update() function, I simply count time until I reach 0.25s and then disable the update with unscheduleUpdate(). I had another unscheduleUpdate() call in onTouchMoved() to stop counting hold time in case the touch moved. The problem is, on Android, onTouchMoved() starts firing constantly while the touch is standing still.

testNode.h: 
#pragma once
#include "cocos2d.h"

class testNode : public cocos2d::Node
{
public:
    testNode() {}
    ~testNode() {}
    CREATE_FUNC(testNode);

    virtual bool init();
    virtual void update(float dt);

    bool onTouchBegan(cocos2d::Touch*, cocos2d::Event*);
    void onTouchMoved(cocos2d::Touch*, cocos2d::Event*);
    void onTouchEnded(cocos2d::Touch*, cocos2d::Event*);
    void onTouchCancelled(cocos2d::Touch*, cocos2d::Event*);

private:
    cocos2d::DrawNode* d;
    int id;
    bool touchIsDown;
    float touchTime;
    cocos2d::Label *messageLabel;
    cocos2d::Vec2 center;
};

testNode.cpp:

#include "testNode.h"
USING_NS_CC;

bool testNode::init()
{

    if (!Node::init())
    {
        return false;
    }
    setContentSize(Size(50, 50));

    Size size = Director::getInstance()->getWinSize();
    Size visibleSize = Director::getInstance()->getVisibleSize();
    setPosition(Vec2(size.width / 2.0f, size.height / 2.0f));
    center.x = size.width / 2.0f;
    center.y = size.height / 2.0f;

    d = DrawNode::create();
    addChild(d, 10);
    d->drawCircle(Vec2(0, 0), 50, (float)M_PI * 2, 50, false, Color4F::RED);

    auto touchListener = EventListenerTouchOneByOne::create();
    touchListener->setSwallowTouches(true);
    touchListener->onTouchBegan = CC_CALLBACK_2(testNode::onTouchBegan, this);
    touchListener->onTouchEnded = CC_CALLBACK_2(testNode::onTouchEnded, this);
    touchListener->onTouchMoved = CC_CALLBACK_2(testNode::onTouchMoved, this);
    touchListener->onTouchCancelled = CC_CALLBACK_2(testNode::onTouchCancelled, this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);

    messageLabel = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 18);

    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    messageLabel->setPosition(Vec2(0,0));
    addChild(messageLabel, 1);
    return true;
}

void testNode::update(float deltaTime)
{
    // When update is scheduled with scheduleUpdate(), this will increment the touchtimer
    // used for differenciating different touch actions.
    if (touchIsDown)
    {
        touchTime += deltaTime;
        if (touchTime > 0.25)
        {
            unscheduleUpdate();
        }
    }
}

bool testNode::onTouchBegan(Touch* touch, Event* event)
{
    Vec2 touchPoint = touch->getLocation();
    float startDistFromCenter = center.getDistance(touchPoint);
    touchTime = 0.0f;

    // Handle touch only if inside the radius of the circle
    if (startDistFromCenter <= 50)
    {
        // Track how long the touch is held without moving or releasing
        touchIsDown = true;
        scheduleUpdate();
        return true;
    }
    else
        return false;
}
void testNode::onTouchMoved(Touch* touch, Event* event)
{
    Vec2 pos = touch->getLocation();
    log("moving...pos = %f , %f", pos.x, pos.y);
    touchIsDown = false;
    unscheduleUpdate();
}
void testNode::onTouchEnded(Touch* touch, Event* event)
{
    touchIsDown = false;
    unscheduleUpdate();

    log("Hold Time: %f", touchTime);
}

void testNode::onTouchCancelled(Touch* touch, Event* event)
{
}

Typical output from a touch/hold event:

09-23 23:42:39.852: D/cocos2d-x debug info(32508): startDistFromCenter: 33.753014 09-23 23:42:39.902: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:39.902: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:39.920: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:39.935: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:39.969: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:39.969: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:39.989: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:39.999: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:40.027: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:40.043: D/cocos2d-x debug info(32508): moving...pos = 246.732849 , 126.925316 09-23 23:42:40.043: D/cocos2d-x debug info(32508): Hold Time: 0.042597

As you can see, the position is not actually moving....what gives?

For reference, I am developing on Windows 10, Visual Studio 2015, cocos2d-x 3.71, and testing on Android 5.1.1 (OnePlusOne phone)


Solution

  • I tested your code and the issue is related to android lollipop. On kitkat everything is working (testing device Huawei Ascend P7 android 4.4.2):

    09-24 11:51:25.351: D/cocos2d-x debug info(1058): Hold Time: 0.252129
    09-24 11:51:27.361: D/cocos2d-x debug info(1058): Hold Time: 0.251298
    09-24 11:51:28.711: D/cocos2d-x debug info(1058): Hold Time: 0.252115
    09-24 11:51:30.101: D/cocos2d-x debug info(1058): Hold Time: 0.251961
    

    I tried also on lollipop device (LG, but I don't remember exactly model) and it's like you said. Spam of touch moved with the same position.

    As a workaround you can do like Alex G said or remember last position (remember it at touch began) and ignore events with exactly the same position.

    Btw I'm using Mac OS X Yosemite and Cocos2d-x 3.8.

    You should start an issue on cocos2d-x github page.