如何优雅的获取脚本组件?

这里第一个组件有可能是ui组件,这就有一些规则限定了,在多人开发的时候会带来一些成本

这就错了,一般这种脚本是不会被require的,都是挂到节点上的

你可以定义一个组件 Script,项目里所有需要快速获取的组件,都继承自这个组件,这个组件注册为插件脚本,保证全局变量会被先注册

window.Script = cc.Class({
  extends: cc.Component
});
cc.js.get(cc.Node.prototype, 'script', function () {
  return this.getComponent(Script);
});

然后把项目里继承自 cc.Component 的地方全部改成 Script。(为了让你避免获取到 UI 组件)


需要获取组件时,

var node = cc.instantiate(prefab);
node.script.init();

这样就行啦,零 require,零所谓的硬代码

我十分认同 @zxh19821 的 uikiller 这类封装,但是不太赞同把这种封装这么早集成到引擎里。 正如大家所回复的,JS 是门十分灵活的语言,一千个人可能有一千种封装,封装的越抽象可能副作用越大,大家各取所需即可。如果社区将来摸索出了一套统一的,经受住时间考验的框架(类似 jQuery),那引擎也是会欢迎作者提交 PR 集成进来的。

4赞

赞同这个,没有统一的标准,弄的太乱,让新手更摸不着头脑

更喜欢这样的代码,动态语言的优雅。

刚测试了下,可行,原理其实就是把getComponent函数注册到node上,通过这种方式来快速获取脚本组件,这种方案目前来看确实是最优的

这个方法只能获得一个脚本。局限性比较大。其实方法还有很多,大家可以自己变通。

1赞

上面说的方法只能获得一个脚本,主要是满足了这个同学的需要,局限性比较大。其实方法还有很多,大家可以自己变通。

当然,程序员如果真的能假定节点上只有一个脚本,那允许 node.getComponent() 直接返回第一个组件似乎也没问题。
不过如果太依赖这种写法,似乎又容易自己坑到自己(谁知道场景会不会被其它人玩坏),所以还是像 @toddlxt 推荐的那样显式传入一个 node.getComponent(cc.Component) 吧。多写点难看的代码一般人会比较清楚自己在做什么。

2赞

这样的写法遇到一个bug,1.6.1,macOS Sierra 10.12.5

//NodeExtend.js
window._nodeExtendMath = {
    // 随机一个int [min, max]
    randomInt: function (min, max) {
        if (min === max) return min;
        return min + Math.floor(Math.abs(Math.random() * (max + 1 - min)));
    }
};
cc.js.get(cc.Node.prototype, 'math', function () {
    return _nodeExtendMath;
});
// console error
2017-09-15T03:41:16.886Z - normal: connected!
2017-09-15T03:41:20.986Z - normal: 1.6.1
2017-09-15T03:41:42.737Z - error: TypeError: Cannot redefine property: math
    at Object.js.get (/Applications/CocosCreator.app/Contents/Resources/engine/cocos2d/core/platform/js.js:280:12)
    at disable-commonjs:///Users/webb/Desktop/selfDoc/never-touch/assets/script/NodeExtend.js?002:8:7
    at disable-commonjs:///Users/webb/Desktop/selfDoc/never-touch/assets/script/NodeExtend.js?002:11:3

问题是这样出现的,NodeExtend.js 勾选 导入为插件允许web允许浏览器允许Native
第一次打开creator不会有问题。当我修改_NodeExtend.js_ 再回到creator 必现这个报错。

不要允许 creator 即可,或者 cc.js.get(…, ‘math’, function () {…}, false, true);

我可以这样理解吗?这样动态添加属性到cc.Node上会影响编辑器的属性检查器?还是别的原因?

因为Creator是经常刷新的,比如你改了点代码,它就刷新一下,然后你的插件脚本就又执行了一次。估计cc.js.get内部使用Object.defineProperty定义你的math属性的时候,默认把configurable设置成了false(不能再次定义)。所以一旦Creator刷新,再次执行插件重新定义math,就会报错。@jare老师让你在cc.js.get后面多传一个false,true,应该就是设置configurable为true的意思。跟属性检查器没啥关系。你也可以把cc.js.get自己写成:

Object.defineProperty(cc.Node.prototype, 'math', {
  get: function(){
    return _nodeExtendMath;
  },
  configurable: true
});
1赞

@feijing566 参照@jare老师的写法,可以这样获取node上的第一个非cc脚本(不用改其它代码,直接用node.script即可):

Object.defineProperty(cc.Node.prototype, 'script', {
  get: function(){
    return this.getComponents(cc.Component).find(function(c) {
      return c.__classname__.indexOf("cc.");
    }
  },
  configurable: true
});
1赞

发现node上面有这么一个属性_components,里面的参数是目前节点上自己挂载的脚本

这样可以获取到脚本中的任意一个函数

1赞

谢谢你的思路了

这个问题其实就是组件带来的带副作用 没有统一的流程方法 ,比如init update close等
所谓鱼和熊掌不能兼得

新手,刚学习cocos,觉的这种写法很奇怪。
对编码来说,更应该对外暴露的不应该是“脚本”组件里的各种行为吗?UI组件理应都是通过脚本组件里的行为去进行操作的。所以理想的情况应该是Prefab对外可以直接调用所有的的脚本组件的公共方法。

cc这种以脚本文件名为引用标志的方法,严重降低了cocos的灵活性。。

require(“file path”) , 文件名成了引用的标志。在很多情况下,我不关心文件名是什么,我只关心node上有哪个用户脚本。有哪些方法可用。至于叫什么名字,对开发者没关系。

为此我写了一个工具方法 getScript( node ) , 返回第一个用户绑定的脚本对象

具体实现方法是,遍历Node 上的所有 components , 对于特定名cc_的对象忽略。。返回第一个不是cc内部对象的component 脚本。。

getScript( node , true ) 则返回所有脚本

贴主可以自己去实现。。

let component = this.node.getComponent(cc.Component)
如果只是获取第一个 这样写