求大神讲解一下节点和脚本他们之间的关系

学习cocos有几天了,现在特别乱,节点 脚本之类的 大脑缺少一个合理的逻辑 来理顺他们之间的关系

官方论坛有入门demo,也可以去b站查找教学视频

诶神奇, 我的回复为啥突然要审核了, 是因为提友商了吗 :hear_no_evil:

可以简单理解成节点就是一个容器,里面可以放游戏场景里看的到的某个东西:一张图片,一段文字,一个按钮,一个模型等,脚本就是你写的代码和命令,把脚本挂在到某个节点上之后就可以用脚本里的命令控制节点的运动或变化,而且一个脚本只要挂在到某个节点上,不单单可以控制挂载的节点,还可以控制场景里所有节点。

节点就是物体, 一个节点上可以添加各种组件,比如添加Sprite组件,就可以显示图片,添加Label组件可以显示文字,添加MeshRender组件可以展示3D模型 ,脚本是用来控制节点的,我们自定义的脚本,可以控制节点的位置,显示 隐藏 ,创建 和销毁 等等

1赞

一点拙见:

节点是 Node, 没什么好说的, 白纸一张
脚本我先分两类

  1. 自己写的工具类, 模型类, 控制类等 “游戏逻辑相关”, “UI 弱相关” 的普通脚本
  2. 继承了 Component 的组件脚本

前者是自己写的, 全凭你自己, 所以我猜你的困惑主要集中在 Component 上


我觉得比较容易说清楚的方式, 得了解一下它的来源, 这种 组件式编程 的方式借鉴自别处

Unity 那边不叫 Component, 而是叫做 MonoBehaviour, 你把它当 Behaviour 来看待, 可能会更好理解一点

一个 Node, 只有位置, 缩放, 旋转等行为

  • 挂上 UITransform, 就有宽高和锚点
  • 挂上 Sprite, 就有能拥有精灵的一些行为
  • 挂上 Button, 就能集成一些按钮相关的行为

这些都是内建的, 可能不满足要求, 于是你自己写了一个新的行为, 比如

@ccclass('QQBehaviour')
export class QQBehaviour extends Component {
   protected onEnable (): void { // 控件被展示的时候, 要 Q 弹 Q 弹的
       Tween.stopAllByTarget(this.node);

       this.node.setScale(v3(0, 0, 1));

       tween(this.node)
           .to(1.2, { scale: v3(1, 1, 1) }, { easing: easing.elasticOut })
           .start();
   }
}
  • 挂上这个脚本, 这个 Node 现在拥有了 Q 弹行为 !!!

Component 有着足够多的生命周期回调, 足够你在不同的时机驱动你的代码运行
如果你之前在这方面有困惑, 我觉得理解到这里就可以了, 不要再深究更多, 先用

过段时间, 你可能会重新好奇, 到时候再去看看引擎是如何做到这些的

节点是固定不动的,可以是图片,音频,动画,文字,而脚本就是可以使用这些的,比如更改图片,更改大小,音频控制音量,控制是否播放音频,是否播放动画,播放动画什么动作,展示文字,更改文字,当然以上这些都是可以更改位置。简单来说节点可以是你想展示的任意东西,而脚本就是让你展示的更有自己的想法。不过也不单单可以展示,每个组件都有不同的功能可以让你更方便的处理逻辑。再通俗些的说就是,节点是泥,脚本是你的手,你可以把泥塑成你想要的任意样子,只要你会,组件是现成塑好方便使用的零件

父节点有坐标系 大小 子节点也有坐标系 大小 这类 如果我想调整大小 我应该修改父节点 还是子节点

你可以这样理解:整个体系,就是一棵树。然后,每个树枝就是一个组件,每个组件又是一串组件组成的。(在这里一切皆组件。)
因为历史原因,这颗树,还是以Node作为每个节点的,所以destroy这些都在node上面。然后,在node上面,挂了N个组件,代码也是node上面的组件而已。为了方便互相访问,就有组件和组件之间的访问方式 getComponent,别的组件为了方便访问node,所以在基类里实现了 (.node)接口,直接可以访问到node。

首先你需要明白一个游戏引擎做了什么事。引擎虽然接口文档扯了那么多,但其实本质上就是做了2件事情,渲染展示与接收处理输入。
而渲染展示这一块又可以被分为渲染与准备渲染所需要的数据。

有这个理解之后,再来看节点与脚本。
先不考虑输入,也不考虑渲染(引擎默认的渲染已经可以满足你学习引擎需要理解所有的,以及日后投入生产的大部分需求),只关心渲染数据准备这一块。这其实就是脚本大部分所影响的内容。

对于3.x版本的节点,就是一个挂载其他组件逻辑的根。
所有的组件会在所有生存周期事件(也就是那些start,onEnable这些,以及最重要的update)的接口中对渲染数据进行刷新。这些所有的接口,只要被启用,被放到你激活的节点中,引擎就会在对应的时间点,对这个节点里所有的组件的相应生存周期事件接口进行调用。

这个是组件与结点之间的关系。

而你的脚本(继承Component,也即是给引擎调用的东西),其实就是具体覆写以上接口方法的逻辑代码。当然你的这些脚本,还会引用额外的与引擎无关的逻辑脚本来做一些具体的事情(比如一个数据中心)。
比如你自定义了一个往前移动的的组件,表现为让一个节点会不断的向前移动。
其逻辑的实现就可以是在每一帧的update里,修改节点的transform的位置。

现在把你的这个组件放进一个Sprite的节点里
因为每一帧刷新的时候,引擎调用你这个节点里每个组件的update,然后就会调用到你编写的修改位置的逻辑,然后引擎的渲染器,就会根据Sprite的数据,以及transform的位置去渲染这个精灵。实现这个精灵不断前移的效果。

调整父节点会改变子节点(子节点是基于父节点的,但是调整子节点不会改变父节点)

脚本就是你自己编写的组件代码。
预制体给你绑定你自己编写的组件代码的功能。
实例化预制体就得到了节点。
节点持有组件,组件拥有资源。
组件会在适当的时候调用生命周期函数和事件监听函数。
非要说有什么奇怪的就是你的脚本代码怎么和预制体关联起来的。正常人还是习惯自己调用自己写的代码。

没有直接的main函数这种入口函数确实让初学者感觉蛋疼。
在编辑器里帮你启动第一个场景,没法从你自己的业务代码直接看到,这也挺蛋疼的。
实在不行你就自己搞个空场景挂个特别的脚本做初始化,然后在这里跳到你自己的业务场景,这样免得一开始就去理解模块加载顺序这些东西。