cocos2d / cocos2d-x

Cocos2d-x is a suite of open-source, cross-platform, game-development tools utilized by millions of developers across the globe. Its core has evolved to serve as the foundation for Cocos Creator 1.x & 2.x.
https://www.cocos.com/en/cocos2d-x
18.26k stars 7.06k forks source link

onTouchCancelled doenst null camera? Also touches freezes. #19393

Open kkafaseb opened 5 years ago

kkafaseb commented 5 years ago

I noticed that touchEnded has 1 extra functions not in touchCancelled: _selectedWithCamera = nullptr;

In case of touchCancelled why _selectedWithCamera is not set to nullptr?

screenshot 2019-02-13 at 2 53 31 pm

I am asking because I have a case where:

  1. User touches down button
  2. User keeps touching down
  3. Ad starts
  4. After ad finishes, buttons are disabled because _state is not WAITING.

screenshot 2019-02-13 at 1 56 27 pm

They way to reproduce freeze of ccmenu is the same as this issue reported by some other user: http://discuss.cocos2d-x.org/t/issue-with-touch-critical-bug/37569

One solution i found was to manually call _menu->onTouchEnded(nullptr, nullptr); but i dont know if i should call Ended or Cancelled.

In addition, if i manually call any of them and actaully the system does call event i might get crash as: CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchCancelled] -- invalid state");

There are cases like what i described about where calling touchesCancelled might be necessary to fix freezes from ads. Any other workaround?

@minggo maybe you have quick answer about this implementation?

minggo commented 5 years ago

I think they should have the same logic.

kkafaseb commented 5 years ago

Yeap, i agree cancelledTouched needs _selectedWithCamera = nullptr; the only thing it doenst need is activate item, the rest should be the same.

I also think that setting MAX_TOUCHES to 15 and then freezing the whole game is unacceptable solution. It's not hard to reproduce as all user has to do is have touch down on some button before video appears and thats +1 invalid touch. There should be a proper way to deal with invalid touches. It has been verified for sure with admob and unityads. Can't you just discard invalid touches? It's better than freezing game. Maybe game developer can clean this invalid touches by callign a function, or they can be cleaned when they reach 15 since anw the game will freeze.

slackmoehrle commented 5 years ago

@minggo we seem to be having more users affected by this. Is this a bug, do you think?

minggo commented 5 years ago

Yep, i think it is an issue. But could somebody provide a demo? I am not familiar with ads.

ebouchard88 commented 5 years ago

Hi,

I have the same issue that the onTouchEnded, or onTouchCancelled are not sent when the key is pressed and the interstitial (admob in my case) loads. The behavior is slightly different on Android vs IOS. Android , the event is not sent, but it can recover the g_indexBitsUsed and so there is a way to modify my code to force the onTouchEnd to be called. However, on IOS, not only the event is not sent, but the g_indexBitsUsed is not released and so after a while, all 15 get used. I tried calling: Director::getInstance()->getOpenGLView()->handleTouchesEnd(1, &TouchID, &X, &Y); but even with this, the indexbit is not released. It is quite a blocking to add interstitial video in my games. I wonder what everyone else is doing.

minggo commented 5 years ago

I have the same issue that the onTouchEnded, or onTouchCancelled are not sent when the key is pressed and the interstitial (admob in my case) loads.

What's the key? Could you please describe it in more detail? And could you please provide a demo?

ebouchard88 commented 5 years ago

Sorry, I meant as the touch is pressed. In my case, the interstitial loads as you change "zone" in the game. But to change zone, you are basically touching the screen to move the character. So when the interstitial appears, you have your finger touched on the screen and usually the id 0 touchEvent is "active". As the interstitial finish, you come back and this ID 0 is still "active" so the character keeps moving and no way to remove this event ID 0 as active. The character will continue forever to move forward regardless what you do (unless you kill the app)

As for the demo I will see if I can create a hello world app that can reproduce it, not sure how long it will take, there are a few things to put in place first.

ebouchard88 commented 5 years ago

So, I investigated a lot this issue today. I still do not know the root cause of why the onTouchEnded or Cancelled is never called, but I was able to get a fix. I created a new function in CCGLView to cancel all pending touch events. I don't know if this the proper way of doing it, but it has been tested on both Android and IOS and it works like a charm. I just call it as soon as I show an ad. That way I make sure nothing is active when coming back from the ad.:

void GLView::cancelAllTouchesEvent() { EventTouch touchEvent; intptr_t id = 0; float x = 0.0f; float y = 0.0f;

std::map<intptr_t, int>::iterator it = g_touchIdReorderMap.begin();
while (it != g_touchIdReorderMap.end())
{
    auto iter = it;
    /* Add to the set to send to the director */
    Touch* touch = g_touches[iter->second];
    if (touch)
    {
        CCLOGINFO("Ending touches with id: %d, x=%f, y=%f", (int)id, x, y);
        touch->setTouchInfo(iter->second, (x - _viewPortRect.origin.x) / _scaleX,
            (y - _viewPortRect.origin.y) / _scaleY);

        touchEvent._touches.push_back(touch);

        g_touches[iter->second] = nullptr;
        removeUsedIndexBit(iter->second);

        it = g_touchIdReorderMap.erase(it);
    }
    else
        ++it;
}

if (touchEvent._touches.size() == 0)
{
    CCLOG("touchesEnded or touchesCancel: size = 0");
    return;
}

touchEvent._eventCode = EventTouch::EventCode::CANCELLED;
auto dispatcher = Director::getInstance()->getEventDispatcher();
dispatcher->dispatchEvent(&touchEvent);

for (auto& touch : touchEvent._touches)
{
    // release the touch object.
    touch->release();
}

}

minggo commented 5 years ago

Yep, i don't know why touch end/cancel message missed, i can not find any documentation mentions it.

You can add a function to cancel all events. But how do you know when to cancel all events?

ebouchard88 commented 5 years ago

I just call it whenever I show an advertisement. So when user comes back from it, everything is reset and ready to go like new.

minggo commented 5 years ago

Yep, i think it is a method to get around the issue.