触发器尼玛就是一个坑

:12: 我不得不又上来吐槽了,触发器尼玛就是一个坑!!!
单个的还行,如果要加载多个从场景编辑器中导出的json文件创建node到同一个层中就只有最后一个读取的触发器有效了,其他的都获取不到对应的节点,跟踪代码发现根源在这里
SceneReader::getInstance()->createNodeWithSceneFile方法
cocos2d-x 3.4 CCSSceneReader.cpp第67-78行:
if (file_extension == “.JSON”)
{
_node = nullptr;
rapidjson::Document jsonDict;
do {
CC_BREAK_IF(!readJson(fileName, jsonDict));
_node = createObject(jsonDict, nullptr, attachComponent);
TriggerMng::getInstance()->parse(jsonDict);
} while (0);

    return _node;
}

每次通过SceneReader::getInstance()->createNodeWithSceneFile方法创建的节点都会将单例SceneReader中的私有属性_node覆盖,而返回的节点也正是这个属性值。
然而触发器的实现完全依赖于节点的Tag值,怎么找到对应的节点呢?
在acts.cpp中看到还是用的单例SceneReader,通过SceneReader::getInstance()->getNodeByTag(_tag)来找到要执行动作的各个节点
CCSSceneReader.cpp第468-479行:
Node* SceneReader::getNodeByTag(int nTag)
{
if (_node == nullptr)
{
return nullptr;
}
if (_node->getTag() == nTag)
{
return _node;
}
return nodeByTag(_node, nTag);
}

尼玛这是在_node中寻找啊,这个_node只是最后一次使用createNodeWithSceneFile时那个json文件中的节点而已,之前的你甭想找到
这就决定了同一层中使用多个场景编辑器导出的json时只有最后一个含有的触发器有效。
:10:

另外就是触发器的扩展,扩展之后要想在Cocostudio中正常播放预览你得自己重新编译一个Cocostudio模拟器才行,尼玛到处找不到重编Cocostudio模拟器的资料。
我已经深受内伤:3:

归根到底其实就是搜索范围的问题,今天突然想到了通过扩大搜索范围来解决找不到对应节点的方法
只需要修改上面的getNodeByTag方法就可以啦
Node* SceneReader::getNodeByTag(int nTag)
{
auto parentNode = Director::getInstance()->getRunningScene();
return nodeByTag(parentNode, nTag);
}
如上,也就是将搜索范围扩大到了当前正在运行的整个场景,不过这修改了引擎中的代码
搜索了整个项目,用到getNodeByTag方法的也就只有触发器,所有我认为这么改可行
望官方更新新版引擎的时候考虑该问题,并提供触发器扩展后Cocostudio模拟器重新编译的详细教程,保证使用扩展的触发器也能正常播放预览

刚发现初始化的时候Director::getInstance()->getRunningScene()获取到的是上一个场景
因此对应的TRIGGEREVENT_INITSCENE事件用以上的改法会有问题
优化如下:
Node* SceneReader::getNodeByTag(int nTag)
{
auto node = nodeByTag(_node, nTag);
if (node == nullptr) {
auto parent = Director::getInstance()->getRunningScene();
node = nodeByTag(parent, nTag);
}
return node;
}
同样,在初始化场景事件中也只能从最后一个json中寻找,不过还好,这个事件目前基本用不上

还有个办法就是
每次发送触发器事件前给SceneReader精确指定一个搜索范围

CCSSceneReader.h中:
public:
void setParent(cocos2d::Node* parent);

private:
cocos2d::Node* _parent;

CCSSceneReader.cpp中:
void SceneReader::setParent(cocos2d::Node *parent)
{
_parent = parent;
}

Node* SceneReader::getNodeByTag(int nTag)
{
return nodeByTag(_parent, nTag);
}

使用时:
SceneReader::getInstance()->setParent(node);
sendEvent(TRIGGEREVENT_INITSCENE);