一、控件和组件神秘奥义
控件: 控件是指对数据和方法的封装。控件可以有自己的属性和方法,其中属性是控件数据的简单访问者,方法则是控件的一些简单而可见的功能、控件创建过程包括设计、开发、调试(就是所谓的3Ds开发流程,即Design、Develop、Debug)工作, 然后是控件的使用。(来自百度百科)
组件: 系统中一种物理的、可代替的部件、它封装了实现并提供了一系列可用的接口。一个组件代表一个系统中实现的物理部分,包括软件代码(源代码,二进制代码,可执行代码)或者一些类似内容,如脚本或者命令文件。简而言之,组件就是对象,是对数据和方法的简单封装。C++ Builder中叫组件,Delphi中叫部件,而在Visual BASIC中叫控件。 组件可以有自己的属性和方法。属性是组件数据的简单访问者。方法则是组件的一些简单而可见的功能。(来自百度百科)
二、UI和代码结合的弊端
1. 大量通过界面引用UI节点
如下图所示,通过脚本组件大量直接引用UI节点,会在相应的描述文件中记录相应的key和value,在一定程度上会增加相关描述文件的大小,从而影响这个描述文件加载所花费的时间。进一步影响资源加载总时间。
{ "__type__": "85d24iUBchG45eRtzPrngjE", "_name": "", "_objFlags": 0, "node": { "__id__": 1 }, "_enabled": true, "label": { "__id__": 7 }, "node1": { "__id__": 9 }, "node2": { "__id__": 12 }, "_id": ""}
通过脚本组件大量直接引用UI节点这种方式还有一个弊端,当节点上的脚本组件因某种原因丢失或者说被重置了,那么还需要被重新挂载一次,如果时间长了,可能都忘记了谁对应的谁。
2.代码中大量查找节点
第一种方式,代码中通过 getChildByName, getChildByUuid 函数查到相应的节点, 在此以 getChildByName 函数来举例。通过下面代码可以看出,正序遍历查找相应节点的名字,返回第一个查到的节点。
/** * !#en Returns a child from the container given its name. * !#zh 通过名称获取节点的子节点。 * @method getChildByName * @param {String} name - A name to find the child node. * @return {Node} a CCNode object whose name equals to the input parameter * @example * var child = node.getChildByName("Test Node"); */ getChildByName (name) { if (!name) { cc.log("Invalid name"); return null; } var locChildren = this._children; for (var i = 0, len = locChildren.length; i < len; i++) { if (locChildren[i]._name === name) return locChildren[i]; } return null; }
举个例子:如下所示,一个根节点下依次有节点A,节点B,节点C,节点D 四个子节点。现在通过 getChildByName 来获取 节点D。
-根节点 --子节点A --子节点B --子节点C --子节点D
如果现在需要查找子节点A呢:
第一次遍历: 查找子节点A,满足条件,则返回子节点A
也就是说 getChildByName 查找子节点A的时间复杂度为1。
如果现在需要查找子节点D呢:
第一次遍历: 查找子节点A,不满足条件,则继续遍历查找
第二次遍历: 查找子节点B,不满足条件,则继续遍历查找
第三次遍历: 查找子节点C,不满足条件,则继续遍历查找
第四次遍历: 查找子节点D,不满足条件,则返回子节点D
也就是说 getChildByName 查找子节点A的时间复杂度为4。
综上所述: getChildByName 查找相应的节点的时间复杂度为 N。意味得查找的节点广度大,所需要的时间越长。代码中大量通过 getChildByName , getChildByUuid 函数查到相应的节点是一个影响性能的因子。
第二种方式,代码中通过 cc.find 函数查到相应的节点,从下面代码可以看出,两层正序遍历查找相应的节点。第一层正序遍历的因子是传入的路径分割。第二层正序遍历的因子是节点的children。
/** * Finds a node by hierarchy path, the path is case-sensitive. * It will traverse the hierarchy by splitting the path using '/' character. * This function will still returns the node even if it is inactive. * It is recommended to not use this function every frame instead cache the result at startup. * * @method find * @static * @param {String} path * @param {Node} [referenceNode] * @return {Node|null} the node or null if not found */ cc.find = module.exports = function (path, referenceNode) { if (path == null) { cc.errorID(5600); return null; } if (!referenceNode) { var scene = cc.director.getScene(); if (!scene) { if (CC_DEV) { cc.warnID(5601); } return null; } else if (CC_DEV && !scene.isValid) { cc.warnID(5602); return null; } referenceNode = scene; } else if (CC_DEV && !referenceNode.isValid) { cc.warnID(5603); return null; } var match = referenceNode; var startIndex = (path[0] !== '/') ? 0 : 1; // skip first '/' var nameList = path.split('/'); // parse path for (var n = startIndex; n < nameList.length; n++) { var name = nameList[n]; var children = match._children; match = null; for (var t = 0, len = children.length; t < len; ++t) { var subChild = children[t]; if (subChild.name === name) { match = subChild; break; } } if (!match) { return null; } } return match; };
举个例子:如下所示,现在一个场景上的节点关系如下。
-场景根节点 --子节点A ---子节点A1 --子节点B --子节点C ---子节点C1 --子节点D ---子节点D1 ---子节点D2
如果现在需要查找子节点A1呢(暂不考虑第二个参数的情况下):
cc.find('子节点A/子节点A1') 开始遍历是 names = [子节点A,子节点A1]
第一层第一次遍历: 找到子节点A,满足条件,则进行第二层遍历。
第二层第一次遍历: 找到子节点A1,满足条件,则返回。
也就是说 cc.find 查找子节点A1 的时间复杂度为2。
如果现在需要查找子节点D2呢(暂不考虑第二个参数的情况下):
cc.find('子节点D/子节点D2') 开始遍历是 names = [子节点D,子节点D2]
第一层第一次遍历: 找到子节点A,不满足条件,则继续遍历。
第一层第一次遍历: 找到子节点B,不满足条件,则继续遍历。
第一层第一次遍历: 找到子节点C,不满足条件,则继续遍历。
第一层第一次遍历: 找到子节点D,满足条件,则进行第二层遍历。
第二层第一次遍历: 找到子节点D1,不满足条件,则继续。
第二层第一次遍历: 找到子节点D2,满足条件,则返回。
也就是说 cc.find 查找子节点D2的时间复杂度为6
综上所述: cc.find 查找一次节点的时间复杂度为 C(2 n)=n*(n-1)/(2*1)。意味着查找的节点广度越大和深度越深所需要的时间越长。代码中大量通过 cc.find 函数查到相应的节点是一个影响性能的因子。
无论是第一种方式还是第二种方式在代码中引用相关的节点,如果由于某种原因,修改了节点之间的关系,进而查不到相应的节点引发的错误。还需要重新修改查找相应节点的路径。
3. 大量通过界面引用UI节点 和 代码中大量查找节点
通过这种方式来查找节点,同时具有上面两种方法的弊端。更重要的是同时通过界面引用UI节点和代码中查找UI节点,是不是感觉有点乱。
关注【游戏讲坛】微信公众号,获取最新动态!
六
Reply2.4.2打开报错
Reply