cocos=》带你全面、系统的了解周期函数(含源码分析)

news2024/11/20 20:38:30

目录

简介

第一 初步了解周期函数

第二 进一步认识周期函数

一、结合节点树来了解一下周期函数

二、节点激活、脚本组件启用

三、node.parent、setParent、addChild 与 周期函数

四、addComponent 与周期函数

五、exectionOrder 与 周期函数

第三 从源码中 学习周期函数

node-activator

activateNode

调用

激活状态

结合节点树(cocos面板-层级管理器一栏)分析

结合组件是否启用分析

反激活状态

activateComp 函数

onEnable 调用的特殊性

destroyComp 函数

cc.Director 的mainLoop函数

Component-scheduler

第四 单个周期函数总结

onLoad

__preload

start

onEnable

onDisable

update

lateUpdate

onDestroy

扩展:

isValid 与 cc.isValid 区别

          isValid 与 cc.isValid 总结:


简介

本篇文章将会由浅入深的、系统的分析一下cocos的生命周期函数。主要分为以下几个阶段:

第一阶段:简单的知道cocos周期函数有哪些,以及常规的执行顺序。

第二阶段:与其他情形结合,去理解cocos周期函数。

第三阶段:cocos 周期函数的实现,触发时机,分析代码实现。

第四阶段:对每个周期函数的一个总结。

还包含node.parent、setParent、addChild、exectionOrder、isValid、cc.isValid等API

什么是周期函数?如果你在脚本组件中定义了周期函数,那么cocos引擎会在其特定的时期自动执行相关的周期函数。

第一 初步了解周期函数

初学cocos的同学,大部分应该都是从cocos文档中了解、学习周期函数的。这里先贴上官方文档地址:Cocos Creator 2.4 手册 - 生命周期回调。这里我给简单总结归纳一下,也可省了大家时间。默认节点已激活,脚本组件已启用。

  • cocos周期函数有:(__preload)、onLoad、onEnabel、start、update、lateUpdate、onDisable、onDestroy。
  • 执行顺序:onLoad、onEnable、start、update、lateUpdate

1. 节点显示隐藏:onEnable、onDisable

2. 节点删除:onDisable、onDestroy

第二 进一步认识周期函数

​在第一阶段 我们对周期函数有了一个初步认识了,但那是基于单个节点的认知。现在我们再进一步的了解周期函数。

一、结合节点树来了解一下周期函数

cocos 的节点结构本质上是一个特殊的树形结构,一个节点可以多个子节点,但是一个子节点只有一个唯一的父节点。

那在节点树中,周期函数的执行顺序是什么样的呢? 默认节点已激活,脚本组件已启用。

测试的节点结构如下:

周期函数执行顺序:

根据打印信息我们可以看到,执行顺序是从上往下依次执行,每个周期函数都是全部执行完后再执行下一个周期函数。

执行顺序是:所有的onLoad => 所有的onEnable => 所有的start => 所有的update => 所有的lateUpdate

二、节点激活、脚本组件启用

细心的同学应该会发现,前面都有一个强调节点已激活,脚本组件已启用。所以这里我们说一下节点激活、脚本组件启用与周期函数的关系。

  1. 节点的激活分两种情况:首次激活 和 再次激活。
  1. 脚本组件的启用也分两种情况:首次启用 和 再次启用 (默认节点是激活状态)
    1. 首次启用,触发:onEnable、start、update、lateUpdate
    2. 再次启用,触发:onEnable、update、lateUpdate

注意📢:

1,当节点在节点树中,且处于激活状态,节点上的脚本组件不管有没有启用,都会执行__preoload、onLoad 函数。

2,脚本组件的启用还控制 start的调用,即首次启用 会执行start

三、node.parent、setParent、addChild 与 周期函数

在我们给节点设置父节点后,会立即执行节点上脚本组件的周期函数:__prelaod、onLoad、onEnable

四、addComponent 与周期函数

addComponent 同 三 的触发顺序。

在 addComponent 时,即会立刻触发 __preload、onLoad、onEnable。start、update、lateUpdate 按照正常顺序触发。因为会立刻调用 activateComp 函数。然后再按深度遍历顺序触发周期函数。

  1. 在onload、start 中addComponent 会立即触发__preload、onLoad、onEnable。
  2. 在update、lateUpdate中 addComponent 会在lateUpdate之后再,触发__preload、onLoad、onEnable。

五、exectionOrder 与 周期函数

@exectionOrder(number)

在脚本组件中,我们可以通过 exectionOrder 来控制脚本组件的执行顺序。该设置是 相对于整个节点树的所有脚本组件而言的,不是单个节点的所有脚本组件。

例如:脚本组件的 exectionOrder 默认值均是 0,如果任意的一个节点上的一个组件的 exectionOrder 设置为-1,则该组件的生命周期函数会在所有的的脚本组件函数之前执行(是相对于单独的某个周期函数的执行而言),即onLoad 会在所有脚本组件的 onLoad 之前执行,start 会在所有脚本组件的 start 之前执行等。

第三 从源码中 学习周期函数

这里我们来从代码层面分析周期函数,主要是 component-scheduler、node-activator 两个核心类。

  • node-activator 类主要负责节点的__preload、onLoad、onEnable三个周期函数的触发功能。其次还有onDestroy函数的直接调用功能。该类主要函数有 activateNode、activateComp、destroyComp,以及私有递归激活函数_activateNodeRecurisively、私有递归反激活函数_deactivateNodeRecursively。
  • component-scheduler 类主要负责 start、update、lateUpdate 周期函数的触发。

node-activator

  • activateNode

调用
  • 场景运行前会调用 runSceneImmediate函数中的scene._activate会调用_nodeActivator的activateNode函数。
  • 节点首次active=ture 时,且祖父节点已激活时,会调用
  • addChild、setParent、node.parent,且自身是激活、祖父节点已激活时,会调用
激活状态

激活状态是节点的active=true的状态。对节点树进行深度遍历,把每个节点上的所有组件存入task的__preload 中、onLoad 中、以及onEnable 中。然后依次触发队列。

  1. 存放 __preload 的队列是一个特殊的队列,该队列不会进行排序。即不受exectionOrder的影响。触发顺序与深度遍历顺序一致。
  2. onLoad、onEnable 是存放在一个会根据组件的 exectionOrder 进行排序的队列,排序方式是升序排序。加入队列时根据exectionOrder 小于0、等于0、大于0 被分别放入队列的 _neg、_zero、_pos 数组中。如在触发onLoad时,会先对这三个数组进行升序排序再调用onLoad函数,调用后立即清空数组。onEnable同理。

// components which priority === 0 (default) this._zero = new Iterator([]); // components which priority < 0this._neg = new Iterator([]); // components which priority > 0this._pos = new Iterator([]);

注意

__preload、onLoad、onEnable 都受节点(祖父节点)是否是激活状态决定调用(祖父节点优先级高于节点自身,因为是深度遍历)。同时onEnable 还受组件本身是否被启用(enabled)控制,即节点是激活状态(active=true),还会再判断 enabled=true。

结合节点树(cocos面板-层级管理器一栏)分析

__preload、onLoad 、onEnable 的执行顺序是从上往下(所有节点打开看)依次执行。优先把节点树中所有组件的__preload执行完,再执行节点树中所有组件的 onLoad 函数,最后执行节点树中所有组件的onEnable 函数。

结合组件是否启用分析

  • 组件是否启用不会影响_preload、onLoad 调用,即enabled值是true或false 不会影响_preload、onLoad调用。
  • onEnable、start、update、lateUpdate 函数受组件是否启用影响。

反激活状态

反激活状态就是节点的active=false。深度遍历该节点树,调用component-scheduler的disableComp方法,同时会清除start队列、update队列、lateUpdate队列中存放的该节点树下的所有的组件。会清除组件启用标志位。

activateComp 函数

该函数是负责把组件的__preload、onLoad、onEnable函数放入对应的队列中,或直接调用__preload、onLoad、onEnable 函数。

调用

  • 节点激活时,会调用,此时会先放入队列中,然后再触发队列。
  • 添加组件(addComponent)时,会调用。此时的调用是直接调用,即在那个函数中addComponent 即会立马触发__preload、onLoad、onEnable三个函数。

注意📢

例如 在onLoad中给节点添加组件(addComponent),会立即出发__preload、onLoad、onEnable三个函数,而后继续遍历后续节点的组件

onEnable 调用的特殊性

在activateComp函数中,onEnable的调用是通过调用component-scheduler的enableComp 函数,

该函数enableComp主要实现的功能:

对onEnable函数的操作

  • 把组件放入 onEnable队列中,等待_activateNodeRecurisively收集完成后,再触发onEnable队列。
  • 组件启用(enabled=true)时,会调用enableComp函数,此时是直接调用组件的onEnable函数。

把组件的start、update、lateUpdate函数放入component-scheduler的对应队列中。加入时,会进行升序排序。

注意📢

如果已经进入start、update、lateUpdate其中一个函数执行节点,会暂时缓存到deferredComps数组中,等待后续执行。例如:在start函数中激活组件enabled=true,此时会先暂时放入deferredComps数组中,等待start函数(紧紧是当前函数不是所有组件的start函数)执行完,再执行启用组件的start函数。启用组件的update、lateUpdate按照常规的顺序执行。

destroyComp 函数

节点被真正销毁会调用该函数。具体请查看 (Component node)isValid 与 cc.isValid 区别

该函数实现的功能:先直接调用组件的onDisable,再直接调用onDestroy。

因为正在的销毁是在 lateUpdate 与渲染函数_render 之间,所有onDisable、onDestroy发生在lateUpdate之后。

cc.Director 的mainLoop函数

start、update、lateUpdate是在activateComp函数中放入Component-scheduler的队应队列中的,触发是在mainLoop函数中触发的。

Component-scheduler

用于管理所有脚本组件的 start、update、lateUpdate三个周期函数。创建了三个对应的调用器用于触发start、update、lateUpdate周期方法。

this.startInvoker = new OneOffInvoker(invokeStart);// 创建启动调用器,用于触发组件的 start 生命周期方法 this.updateInvoker = new ReusableInvoker(invokeUpdate);// 创建更新调用器,用于触发组件的 update 生命周期方法 this.lateUpdateInvoker = new ReusableInvoker(invokeLateUpdate);// 创建后期更新调用器,用于触发组件的 lateUpdate 生命周期方法

每个调用器的数据结构类似node-activator 的onLoad、onEnable都会排序。所有的已激活启用的脚步组件的都会放入这三个调用器中,并进行升序排序。这三个调用器是在CCDirector类中的mainLoop 函数中触发的。

脚本组件加入时机:在每个脚本组件的onEnable调用时,会把脚本组件加入到这三个调用器中。

删除:当节点从激活状态=>不激活 active=false或当脚本组件从启用=>不启用时,enabled=false,会从这三个调用器中删除对应的脚步组件

第四 单个周期函数总结

onLoad

onLoad 的调用是在节点被激活时,被调用,却仅此一次。再次由未激活状态 到激活状态便不会被调用。

  1. onLoad 的调用不受组件是否被启用控制,仅由节点是否激活控制,同时也由其祖父节点是否激活控制(优先级高于节点本身,因为是深度遍历节点树的),addChild、node.parent 会立即触发,不会放入 onLoad 队列中后才触发。
  2. 结合节点树,onLoad 的调用顺序是从根节点开始的一种深度遍历的顺序进行调用。
  3. 结合节点本身,如果节点有多个组件,则是由上到下的调用顺序(编辑面板)。但是我们可以通过 exectionOrder 控制执行顺序。

注意 📢

我们不可以在该方法中去调用在自己节点树以下的其他组件在 onLoad、start 中做了业务逻辑处理后得出来的结果值。因为它们的 onLoad、start 方法尚未执行,无法拿到正确的结果值。但可以使用节点树中所有序列化的数据。

在onLoad的操作

可以使用节点树中所有已序列化的数据。不建议使用需要依赖其他组件的onLoad处理后的数据,根节点(如场景)除外。

__preload

同 onLoad,区别:onLoad 受 exectionOrder 影响,__preload 不受影响

start

start 的调用是脚本组件首次启用时调用(onEnable->start),仅此一次。再次由未启用状态=》启用状态便不会被调用。

  1. 永远在__preload、onLoad 函数后调用,相对于场景中的所有脚本组件而言。
  2. 首次启用时,start 会在 onEnable 后才调用,但是在 update 调用之前。
  3. 结合节点树,start 的调用顺序同 onLoad
  4. 结合节点本身,start 的调用顺序同 onLoad

注意 📢:

我们不可以在该方法中去调用在自己节点树以下的其他组件在 start 中做了业务逻辑处理后得出来的结果值。因为它们的 start 方法尚未执行,无法拿到正确的结果值。但是我们可以去拿任意脚本组件在 onLoad 中逻辑处理后的结果值。(如果是默认就已启用,我们也可以拿到 onEnable 中的逻辑处理后的结果值,因次数 onEnable 执行在 start 前,但是一般我们不这样做,因为 onEnable 会被多次调用)。

在start中的操作

可以使用加载好的配置数据、序列化的数据、节点树中所有组件的onLoad中处理后的数据。不建议使用需要依赖其他组件的start处理后的数据,根节点除外。

onEnable

onEnable 的调用是由脚本组件是否启用,可多次触发,即由未启用状态->启用状态 会调用一次。

  1. 永远在__preload、onLoad 函数后调用,相对于所有脚本组件而言。
  2. 首次启用时,在 start 前调用
  3. 结合节点树,onEnable 的调用顺序同 onLoad
  4. 结合节点本身,onEnable 的调用顺序同 onLoad

一般我们在此函数中的操作:

  • 注册事件函数,该组件启用时,注册脚本会使用到事件函数(观察者事件)
  • 启动定时器,该组件启用时,启动定时器做一些业务逻辑
  • 启用碰撞检测,该组件启用时,可开启碰撞,如果禁用,则可以关闭碰撞。

onDisable

onDisable 的调用是在 脚本组件被禁用时。 可多次触发,即由启用状态->禁用状态 会调用一次。

  1. 脚本组件默认状态 enabled=false 时(面板不勾选),并不会触发 onDisable
  2. 默认状态是启用时,在 onLoad、start 中,设置不启用,也不会触发 onDisable
  3. 删除时,会触发,未启用不会触发。已启用的脚本组件执行顺序,按深度遍历顺序

一般我们在此函数中的操作:

  • 取消注册事件,该组件禁用时,删除注册事件函数
  • 关闭启动定时器,该组件禁用时,关闭定时器
  • 关闭碰撞检测,该组件禁用时,关闭不必要的碰撞检测

update

update 是每帧都会调用的帧函数。节点未激活、在场景中未激活、脚本组件未启用时都不出触发。

  1. 在__preload、onLoad、onEnable、start 之后调用,在 lateUpdate 之前调用

lateUpdate

lateUpdate 是每帧都会调用的帧函数。节点未激活、在场景中未激活、脚本组件未启用时都不出触发。

  1. 在__preload、onLoad、onEnable、start、update 之后调用
  2. 在 lateUpdate 函数之后,会进行彻底删除调用了 destroy 的节点或组件,紧接着调用渲染函数进行渲染。

注意 📢

该函数是渲染前,最后一次可以修改渲染数据的周期函数。

onDestroy

onDestroy 是节点被删除时调用。未激活节点上的脚本组件不会触发。节点已激活,脚本组件未启用依然会触发

  1. 触发顺序,以节点树最深、最左的脚本组件开始触发,然后从左到右,节点上的脚本组件顺序是从上往下触发

扩展:

isValid 与 cc.isValid 区别

  1. 当节点调用了 Destroy 方法后,会给该对象的_objFlags 添加一个 ToDestroy 标志位,并暂时存储在对象的全局变量 objectsToDestroy 数组中。对象真正被删除时,会给_objFlags 添加一个 Destroyed 标志位。

在 lateUpdate 函数触发之后,会调用 obj._deferredDestroy() 函数,删除所有存储在 objectsToDestroy 数组中的对象。对象的真正删除工作由对象自己的 _onPreDestroy 函数删除(事件、动作、定时器等,调用周期函数 onDestroy等),再添加一个 Destroyed 标志。

注意 📢

实际删除是在当前帧进行的,并不是下一帧,即在 lateUpdate 之后,渲染函数render之前,即两者之间删除的。

(常说节点删除是在下一帧删除,因为cocos是以lateUpdate函数调用之后为一帧的结束)

isValid 与 cc.isValid 总结:

  • isValid 是检查该对象的_objFlag 是否有 Destroyed,所以该对象只有在真正删除时,才存在 Destroyed。

  • cc.isValid 有两种检查模式,第二个参数为空或false时,与 isValid 一致。 第二个参数等于 true 时,则同时检测了 Destroyed、toDestroy 两个标志位,来判断对象是否被删除,推荐使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1657001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科学碳目标(SBTI)认证是什么?

科学碳目标&#xff08;SBTI&#xff09;认证是一种基于科学的减排目标认证和监测体系&#xff0c;旨在确保企业和国家制定的减排目标符合科学标准&#xff0c;并且能够实现全球气候目标的减缓效应。这个认证体系由全球碳项目和世界资源研究所&#xff08;WRI&#xff09;共同开…

如何判断自己是不是强迫型人格障碍?

什么是强迫型人格障碍&#xff1f; 强迫型人格&#xff0c;也叫强迫固执型人格&#xff0c;当某些强迫型行为严重影响到正常的生活&#xff0c;工作和人际关系&#xff0c;且具有长期稳定的持续性特征&#xff0c;即是强迫型人格障碍。 这类思维和行为特征可以概括为&#x…

leetcode 1235

leetcode 1235 代码 class Solution { public:int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {int n startTime.size();vector<vector<int>> jobs(n);for(int i0; i<n; i){jobs[i] …

【Anaconda】升级Anaconda Navigator提示JSONDecoderError,删除.condarc文件后搞定

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、报错&#xff1a;JSONDecoderError二、错误原因三、解决问题总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 时间长未升级Ana…

本地搭建springboot服务并实现公网远程调试本地接口

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

2024/5/9 QTday4

完成定时器制作 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);connect(&timer2, &QTimer::timeout, this, &Widget::label_begin);connect(&…

数据库(MySQL)—— 索引

数据库&#xff08;MySQL&#xff09;—— 索引 什么是索引创建索引使用 CREATE INDEX 语句使用 ALTER TABLE 语句在创建表时定义索引特殊类型索引注意事项 举个例子无索引的情况有索引的情况为什么索引快索引的结构 今天我们来看看MySQL中的索引&#xff1a; 什么是索引 MyS…

0509_IO4

练习1&#xff1a; 创建一对父子进程&#xff1a; 父进程负责向文件中写入 长方形的长和宽 子进程负责读取文件中的长宽信息后&#xff0c;计算长方形的面积 1 #include <stdio.h>2 #include <string.h>3 #include <stdlib.h>4 #include <sys/types.h>…

PyCharm安装详细教程

PyCharm安装详细教程 PyCharm简介及其下载网站 PyCharm是由JetBrains打造的一款Python IDE(Integrated Development Environment&#xff0c;集成开发环境)&#xff0c;带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具。PyCharm提供了代码编辑、调试、语法高亮…

【BUUCTF】[RoarCTF 2019]Easy Java1

工具&#xff1a;hackbar发包&#xff0c;bp抓包。 解题步骤&#xff1a;【该网站有时候send不了数据&#xff0c;只能销毁靶机重试】 这里的登录界面是个天坑【迷魂弹】 直接点击help&#xff0c;然后进行打开hackbar——通过post请求&#xff0c;再通过bp抓包&#xff0c;…

DetCLIPv3:面向多功能生成开放词汇的目标检测

DetCLIPv3:面向多功能生成开放词汇的目标检测 摘要IntroductionRelated worksMethod DetCLIPv3: Towards Versatile Generative Open-vocabulary Object Detection 摘要 现有的开词汇目标检测器通常需要用户预设一组类别&#xff0c;这大大限制了它们的应用场景。在本文中&…

长难句打卡5.9

For example, the Long Now Foundation has as its flagship project a mechanical clock that is designed to still be marking time thousands of years hence. 例如,今日永存资金会将机械钟表视为旗舰项目,因此该钟表旨在为未来几千年保持计时。 Foundation n.基金会flag…

如何快速注册企业邮箱?只需要三步

快速注册一个企业邮箱&#xff0c;只需要以下三个步骤&#xff1a;一是挑选适合的邮箱版本和邮箱价格&#xff0c;二是填写必要的企业信息&#xff0c;三是完成企业邮箱的基础配置。完成上述三个步骤&#xff0c;企业就能够拥有一个专属的企业邮箱。 一、挑选适合的邮箱版本 …

jpg照片怎么压缩到10k?压缩照片并不难

jpg照片怎么压缩到10k&#xff1f;随着数字摄影的普及&#xff0c;我们手机或电脑中存储的照片数量越来越多&#xff0c;而这些高分辨率的照片往往会占用大量的存储空间。为了节省空间&#xff0c;将JPG照片压缩到较小的文件大小成为了许多人的需求。本文将为您介绍几款可以将J…

《基于GNU-Radio和USRP的雷达通信系统的实现》文献阅读

文章目录 前言一、摘要二、引言三、联合系统实施1、基本原理2、实验方案 四、软件设置1、发射机2、接收机 五、实验结果1、实验设置2、波形3、室内外对比4、不同参数的结果 六、结论七、参考文献八、论文自取九、阅读收获 前言 本文记录《基于GNU-Radio和USRP的雷达通信系统的实…

新手做抖音小店,应该怎么做才挣钱?按照这个思路来!

大家好&#xff0c;我是电商糖果 别人在抖音创业开店&#xff0c;都可以赚到钱&#xff0c;他们是怎么做到的。 作为一个新手&#xff0c;应该怎么做呢&#xff1f; 我相信这是很多人想开店的朋友&#xff0c;前期都会思考的问题。 下面糖果就来说一下&#xff0c;新手做抖…

vue 代码样式问题

部分电脑存在样式错乱问题&#xff0c;部分电脑样式正常。最后发现是样式写在 el-col 里面导致的。 注意&#xff1a;写样式不要放在 el-row 或者 el-row &#xff0c;导致部分电脑会出现莫名其妙的样式问题 <el-row class"detail"><el-col class"it…

达梦数据库限制用户登录IP测试

达梦数据库创建用户时可以限制登录ip和时间段。 创建测试测试用户 create user test1 identified by Test_1234 ALLOW_IP "192.168.100.101"; 限定该用户只能通过192.168.100.101地址登录数据库 连接测试 上图可见&#xff0c;192.168.100.101客户端可以连接上19…

研发妹子说如果问题不解决她就给我一口大黑锅:记Oracle库SQL执行超时导致业务无法测试优化处理记录

备注&#xff1a;本次记录根据后期回顾整理&#xff0c;实际测试过程遇到问题较多&#xff0c;也花费了不少时间&#xff0c;后期整理也有些处理过程被遗漏。 一、问题现象及处理过程 研发中心开发部一同事反馈某Oracle 测试库 10.xxx.xxx.xxx 无法访问&#xff0c;业务无法测…

振动分析的一些概念

一.时域分析 振动测试领域中&#xff0c;通常使用标准是ISO 10816系列标准&#xff0c;其要去使用有效值&#xff08;RMS&#xff09;来表示震动信号的能量大小&#xff0c;并提供一组限制值&#xff0c;以帮助用户评估机器的振动水平是否正常。 1.位移&#xff1a; 峰峰&…