creater查找节点的便利方案,可以不使用官方的cc.find方式的查找

//逐层查找节点
cc.Node.prototype.finds = function (value) {
var me = this;

var children = [].concat(me.children);;
if (me.nodes) {
    //查询过节点后,将部分查询结果缓存到node节点的nodes上,提供便捷查询
    if (me.nodes[value]) {
        return me.nodes[value];
    } else {
        //如果未便捷找到节点,逐层查找
        return find();
    }
} else {
    return find();
}
function find() {
    if (children.length < 1) {
        return null;
    }
    var child = children.shift();
    me.nodes = me.nodes || {};
    me.nodes[child.name] = child;
    if (child.name == value) {
        return child;
    } else {
        var childchilds = [].concat(child.children);
        if (childchilds.length > 0) {
            children = children.concat(childchilds);
        }
        return find();
    }
}

};

应该可以看明白吧?
例如:Canvas.finds(‘其中的某个节点’);抑或:某个Node.finds(‘node里面的节点’);
发帖的编辑器真不好用,代码格式凑合着看吧

2赞

谢谢楼主的代码,对我来说,很有借鉴的作用。
但里面有个问题,也不算bug,那就是你把children 都改了。那么getChildren 和 getChildByName 这些函数也自然没用了。因为children 被你清空了,只能获得空数组


    /**
     * 把Node当前的节点树结构根据Node命名转成一个js对象,重名的组件会覆盖,
     * Node的name不应该包含空格键,否则将跳过
     * @param node 被遍历的Node组件
     * @param obj  绑定的js对象 (可选)
     * @param level 遍历层次数 (可选)  选择合适的层级可以提升效率
     */
    public static nodeTreeInfoLite(node: cc.Node, obj?: any, level?: number): any {
        let _level = level;
        if (isNaN(_level)) {
            _level = 99;
        }
        if (_level < 1) {
            return;
        }
        --_level;
        let treeInfo = obj || {};
        let items = node.children;
        for (let i = 0; i < items.length; i++) {
            let _node = items[i];
            if (_node.name.indexOf(" ") < 0) {
                treeInfo[_node.name] = _node;
            }
            ViewUtils.nodeTreeInfoLite(items[i], treeInfo, _level);
        }
        return treeInfo;
    }


 /**
     * 正则搜索节点名字,符合条件的节点将会返回
     * @param reg   正则表达式
     * @param node  要搜索的父节点
     * @param _nodes 返回的数组 (可选)
     */
    public static findNodes = function (reg: RegExp, node: cc.Node, _nodes?: Array<cc.Node>): Array<cc.Node> {
        let nodes: Array<cc.Node> = _nodes || [];
        let items: Array<cc.Node> = node.children;
        for (let i = 0; i < items.length; i++) {
            let _name: string = items[i].name;
            if (reg.test(_name)) {
                nodes.push(items[i]);
            }
            ViewUtils.findNodes(reg, items[i], nodes);
        }
        return nodes;
    };

1赞

这样修改下,应该就能保证不动原来的children了吧

谢谢大大对主题的贡献,解决的需求不同,看官们可以各取所需。

其实也可以

cc.Node.prototype.find = function (value) {
  return cc.find(value, this);
}
1赞

关键是:你的value需要传类似’Canvas/a/b/c’这样的路径吧?
这样其实操作满不方便,对于开发来说,要把效率放在很靠前的位置。
上面的解决方案是:只需要给value传入’a’,就可以从node上去遍历找到a返回,而不用写长串的字符串。
不知道,这是不是两者之前的差异。

1赞
public seekNodeWithName(name: string, root?: cc.Node): cc.Node {
    let child: cc.Node;
    if (name.indexOf("/") != -1) {
        child = cc.find(name, root);
    } else {
        if (!root) {
            root = cc.director.getScene();
        }
        if (root.name === name) {
            child = root;
        } else {
            child = this.findChild(name, root);
        }
    }
    if (child) {
        return child;
    } else {
        cc.warn("没有找到指定的Node, node name ====", name);
        return null;
    }
}

public seekComponentWithName<T extends cc.Component>(name: string, type: { prototype: T }, root?: cc.Node): T {
    let child: cc.Node = this.seekNodeWithName(name, root);
    if (child) {
        return child.getComponent(type);
    } else {
        cc.warn("没有找到指定的Component, Component Type ====", type);
        return null;
    }
}

private findChild(name: string, parent: cc.Node): cc.Node {
    let child: cc.Node = parent.getChildByName(name);
    if (child) {
        return child;
    } else {
        let children: cc.Node[] = parent.children;
        for (let i = 0; i < children.length; i++) {
            child = this.findChild(name, children[i]);
            if (child) {
                return child;
            }
        }
    }
    return null;
}

findchild函数可以优化成队列不用递归,不过项目中一般也不会有很长多的子节点

好像也没有脱离递归查找的范畴吧

没 我的意思这里可以优化 但实际没多大差 所以我没改了
seekNodeWithName("a");
这个样就可以查找第一个命名为a的节点,第二个参数是从哪个节点开始查找

恩,我知晓你那段代码的用法。
我提供的那个方法,是加到cc.Node的原型链上的, 这样你任何node节点都可以直接调用,找到第一个满足条件的并返回。
这样就不用传需要从哪个node上找的参数了
形象点说:你的方法,可以比喻成:找到孩子(孩子名,某节点),我的方法,比喻成:某节点的孩子(孩子名)。
是想把语言改成更自然点的方式

是的。其实你要的就是 find + cache 嘛?如果要效率的话,甚至都不应该 find…… 也不应该用字符串,字符串还有查表的开销呢…… 现在的 find 一般是初始化的时候调用一次,调用完就要自己保存起来了。初始化的过程中 cache 的价值不大,反而还可能会内存泄露。

1赞

恩,我现在面对的场景是:
一个场景中会存在多个页面,需要对页面来进行控制,当然,现在是将查询结果赋值给变量,然后再进行操作的。
可有些时候,会存在重复使用finds一个节点的情况。这时候,这样编码的方式就可以在后续节省查找效率了。
至于你说的不应该用字符串,谁让字符串直观易懂呢,便于阅读维护呀。
当然,就你说的字符串查表的开销问题,是不是可以考虑从底层来优化呢,上层开发使用字符串,在底层引擎解析的时候,是不是可以自动转成数字或者id之类的唯一标示呢?这个我就不知道说的对不对了,还要看你们的是如何应对的。

变量名写得好,也一样直观易懂。而且可以被 JIT,性能会高一些。

不懂,你是指将指定节点挂载到已命名好变量的脚本上这种方式么?
如果是:
节点少量还好说,一旦多了,感觉很不优雅,需要预先定义很多的变量,而且动态生成的变量又是无法预先加载到节点的脚本上的。
如果不是:
就不知道怎么回答了。
关于JIT,恕我小白,实在不懂。敬请指教[抱拳]

2.1版本这样子使用会报错,提示Canvas未定义

看下他最新的Canvas全局变量改成了什么,替换掉应该就可以了

你好帮我看下这个问题吧 https://forum.cocos.com/t/topic/73632