【已解决】Cocos根节点是Node的BUG

我们项目使用了cocos2d-x 3.4 +cocos2.2.5版本来开发。使用过程中发现了一个十分严重的bug。
在使用cocos的scrollview时候,发现在scrollview的子项上添加接收后,scrollview的滚动无法响应了。查了一下源代码,发现根本原因是因为根节点是Node引起的!!
在UIWidget中,有一个propagateTouchEvent函数
他是这样的


void Widget::propagateTouchEvent(cocos2d::ui::Widget::TouchEventType event, cocos2d::ui::Widget *sender, cocos2d::Touch *touch) {
    Widget* widgetParent = getWidgetParent();
    if (widgetParent) {
        widgetParent->interceptTouchEvent(event, sender, touch);
    }
}

就是收到点击后,找自己的父节点,如果父节点是widget,则传递给widget。由于cocos里面的根节点是node,故无法传递。
无奈之下,我写了这辈子最烂的一段c++代码
如下


void Widget::propagateTouchEvent(cocos2d::ui::Widget::TouchEventType event, cocos2d::ui::Widget *sender, cocos2d::Touch *touch) {
    Node* target = this;
    while(true) {
        Node* m_parent = target->getParent();
        if (m_parent) {
            Widget* widgetParent =dynamic_cast<Widget*>(m_parent);
            if (widgetParent) {
                widgetParent->interceptTouchEvent(event, sender, touch);
                break;
            } else {
                target = m_parent;
            }
        } else {
            break;
        }
     }
}

就是在循环的找父节点,直到是widget为止,再把点击传过去。
好吧,这样是解决了上述的问题,scrollview动起来了。
然后又发现了更加扯的问题,被剪裁区域外的item项居然可以点击?!!!!!
WTF!
又查,然后发现和上面问题是一样的。widget中有一个isClippingParentContainsPoint方法
就截取头几句就可以了


bool Widget::isClippingParentContainsPoint(const Vec2 &pt)
{
    _affectByClipping = false;
    Widget* parent = getWidgetParent();
    Widget* clippingParent = nullptr;
    while (parent)

同样的问题,取父类的时候,发现父类不是widget,就结束了。。。。

1赞

为啥不把根节点类型改成Widget

说的是啊,为啥Cocos不能把根节点改成Layout呢:3:

因为在cocostudio中设计UI的时候,可添加元素除了基本的Widget,还有音乐等元素,估计就基于这个原因,才把Root节点设置为Node类型吧:7:

:12::12:然后就可以不理会根节点不是widget引发的bug咯?

同意同意同意同意同意

另外根节点不是Widget就无法clone,这个也很严重啊!

是啊,我也遇到过类似的问题,Widget的触摸事件只能在父节点也是Widget的情况下才传递,这导致了很多不便。比如需要将ui::Button作为子节点加入exteon::ScrollView时,就会发生触摸被截住而滑动框不能滑动的问题。

不是Widget就无法clone这个问题
做列表式界面的时候,比如背包
就会让界面非常卡。:12::12::12::12:

做背包时必须循环调用读取文件IO操作来初始化每个道具,比如CSLoader::createNode(“xxxItem.csb”);
必然导致非常的卡,cocos犯这种低级的错误实在是不应该

感谢大家反馈,已经收集,我们尽快在下个版本修正这个问题,再次感谢大家。

加载csb文件时能否添加一个缓存参数,让用户自己决定是否缓存当前读取到的文件数据,便于下次快速生成对应的Node
比如类似:
Node* CSLoader::nodeWithFlatBuffersFile(const std::string &fileName, const ccNodeLoadCallback &callback, bool cacheFile)
{
std::string fullPath = FileUtils::getInstance()->fullPathForFilename(fileName);

CC_ASSERT(FileUtils::getInstance()->isFileExist(fullPath));

Data buf;
// 首先从缓存中查找数据
std::map<std::string, Data>::iterator it = _cacheFlatData.find(fullPath);
if (it != _cacheFlatData.end()) {
    buf = it->second;
} else {
    // 未找到则读取文件
    buf = FileUtils::getInstance()->getDataFromFile(fullPath);
    // 加入文件缓存中
    if (cacheFile) {
        _cacheFlatData = buf;
    }
}

… …

setSwallowTouches(false)
对在scrollview内部的接受事件的控件设置成不吞噬事件就可以了

大神啊,我遇到的两个大难题跟你这帖完全一样:10::9::6:
第二个难题为什么不能用addchild的z值来避免点击行为?我现在下方的按钮区域的z值比scrollview的z值大
虽然你说出了解决方法,但我的项目的线上的了,不能动cocos底层,cocos版本是3.0,还有没有方法在lua层面上补救?
3.0没有那个吞噬触摸的函数也是个坑:6: