🎯 Cocos Creator 生命周期详解
2026-07-22
2070 字约 7 分钟
...Cocos Creator 生命周期详解
一、前言
Cocos Creator 作为主流的游戏开发引擎,其组件化架构的核心就是生命周期回调函数。理解这些回调的触发时机、执行顺序和使用场景,是写出高质量游戏逻辑的基础。
本文基于 Cocos Creator 3.x,系统梳理从引擎启动到场景切换、从节点创建到销毁的完整生命周期。
二、组件生命周期回调总览
Cocos Creator 的生命周期回调按触发顺序排列如下:
| 回调函数 | 触发时机 | 说明 |
|---|---|---|
onLoad | 节点加载完成时 | 初始化的起点,只触发一次 |
start | 第一次 update 之前 | 用于业务逻辑初始化 |
update(dt) | 每帧调用 | 游戏主循环,dt 为上一帧耗时 |
lateUpdate(dt) | 每帧在 update 之后调用 | 处理 update 中产生的依赖 |
onDestroy | 节点被销毁时 | 资源释放、事件注销 |
onDisable | 节点或组件被禁用时 | 组件失活的清理 |
onEnable | 节点或组件被启用时 | 组件激活的恢复 |
注意:
constructor(构造函数)不是 Cocos 的生命周期回调,而是 TypeScript/JavaScript 类的构造器。虽然可以在其中写初始化代码,但此时组件尚未被引擎挂载,很多引擎 API 不可用。
三、各回调详解
3.1 onLoad
触发时机:当节点及其所有子节点加载完成后,引擎会按照节点树的顺序依次调用每个组件的 onLoad。
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Player')
export class Player extends Component {
onLoad() {
// 初始化引用、获取其他组件
const health = this.node.getChildByName('Health');
console.log('节点加载完成,可以安全访问子节点');
}
}
关键特性:
- 每个组件只触发一次
- 所有节点的
onLoad在第一帧 update 之前全部执行完毕 - 在
onLoad中可以安全地访问子节点和组件引用 - 节点激活状态下,
onLoad在节点激活时触发;禁用的节点,其onLoad会延迟到节点激活时才触发
典型用途:
- 获取组件引用和子节点引用
- 初始化变量
- 注册事件监听
3.2 start
触发时机:在 onLoad 之后、第一帧 update 之前调用。
@ccclass('Enemy')
export class Enemy extends Component {
start() {
// 在这里开始寻路逻辑
this.scheduleOnce(() => {
this.findPath();
}, 0.5);
}
}
start 与 onLoad 的区别:
| 对比项 | onLoad | start |
|---|---|---|
| 调用顺序 | 先于 start | 后于 onLoad |
| 执行次数 | 每个组件一次 | 每个组件一次 |
| 节点禁用时 | 节点激活后触发 | 节点激活后触发 |
| 多组件依赖 | 不保证同节点其他组件的 onLoad 已执行 | 保证同节点所有组件的 onLoad 已执行 |
最佳实践:如果组件 A 需要访问同节点上组件 B 的初始化结果,把 A 的逻辑放在 start 中。
3.3 update(dt)
触发时机:游戏运行期间,每帧都会被调用。
@ccclass('Bullet')
export class Bullet extends Component {
private speed: number = 500;
update(dt: number) {
const pos = this.node.position;
this.node.setPosition(pos.x, pos.y + this.speed * dt, pos.z);
}
}
注意事项:
dt参数是上一帧到当前帧的时间间隔(秒),不是固定值- 不要在
update中做内存分配(创建对象、拼字符串等),会造成 GC 压力 - 频繁访问属性(如
this.node.position)会触发组件脏标记更新,能缓存就缓存 - 禁用的组件不会调用
update
3.4 lateUpdate(dt)
触发时机:在所有组件的 update 之后调用。
@ccclass('CameraFollow')
export class CameraFollow extends Component {
@property(Node)
target: Node = null;
lateUpdate(dt: number) {
// 跟随玩家移动
if (this.target) {
this.node.setPosition(
this.target.position.x,
this.target.position.y,
this.node.position.z
);
}
}
}
典型用途:
- 摄像机跟随(确保玩家位置更新后再移动摄像机)
- 粒子系统与角色动画的同步
- 在所有逻辑更新完成后做统一的后处理
3.5 onEnable / onDisable
@ccclass('UIPanel')
export class UIPanel extends Component {
onEnable() {
// 面板显示时注册事件
this.node.on(Node.EventType.TOUCH_START, this.onTouch, this);
}
onDisable() {
// 面板隐藏时注销事件,防止内存泄漏
this.node.off(Node.EventType.TOUCH_START, this.onTouch, this);
}
}
触发场景:
node.active = true/false时component.enabled = true/false时- 节点首次被添加到场景树且处于激活状态时(
onEnable) - 节点被移除前(
onDisable)
重要:onDisable 在 onDestroy 之前调用。这意味着节点销毁时,先执行所有组件的 onDisable,再执行 onDestroy。
3.6 onDestroy
触发时机:节点从场景树中移除并被销毁时。
@ccclass('Tower')
export class Tower extends Component {
onDestroy() {
// 清理所有定时器
this.unscheduleAllCallbacks();
// 清理事件监听
this.node.off(Node.EventType.TOUCH_START, this.onTouch, this);
console.log('塔被摧毁');
}
}
注意事项:
onDestroy中不要再访问其他可能已被销毁的节点- 不要在
onDestroy中重新创建节点或启动新的动画 - 如果在
onDestroy中通过director.loadScene切换场景,需要注意时序
四、场景切换的生命周期
当你调用 director.loadScene() 切换场景时,流程如下:
| 步骤 | 执行内容 |
|---|---|
| 1 | 当前场景所有节点的 onDisable |
| 2 | 当前场景所有节点的 onDestroy |
| 3 | 加载新场景资源 |
| 4 | 新场景节点的 onLoad |
| 5 | 新场景节点的 start |
| 6 | 新场景第一帧 update |
实际案例:
// 玩家死亡后切换到结算场景
@ccclass('GameManager')
export class GameManager extends Component {
onPlayerDead() {
// 先执行结算逻辑
this.showResult();
// 1 秒后切换场景
this.scheduleOnce(() => {
director.loadScene('ResultScene');
}, 1);
}
}
五、节点创建与销毁的生命周期
动态创建节点
const node = new Node('NewEnemy');
this.node.addChild(node);
// 此时引擎会:
// 1. 调用 node 上所有组件的 onLoad
// 2. 如果 node 处于激活状态,下一帧调用 start
动态销毁节点
node.destroy();
// 此时引擎会:
// 1. 调用 node 上所有组件的 onDisable
// 2. 调用 node 上所有组件的 onDestroy
// 3. 从场景树中移除
从场景树移除但不销毁
node.removeFromParent();
// 只是从父节点移除,不触发 onDestroy
// 可以之后重新 addChild 到其他节点
六、常用生命周期模式
模式 1:对象池
@ccclass('BulletPool')
export class BulletPool extends Component {
private pool: Node[] = [];
getBullet(): Node {
let bullet: Node;
if (this.pool.length > 0) {
bullet = this.pool.pop();
bullet.active = true; // 触发 onEnable
} else {
bullet = instantiate(this.bulletPrefab);
this.node.addChild(bullet); // 触发 onLoad
}
return bullet;
}
putBullet(bullet: Node) {
bullet.active = false; // 触发 onDisable
// 不调用 destroy(),留着复用
}
}
模式 2:事件驱动的状态机
@ccclass('StateComponent')
export class StateComponent extends Component {
private currentState: string = 'idle';
changeState(newState: string) {
if (this.currentState === newState) return;
this.exitState(this.currentState);
this.currentState = newState;
this.enterState(newState);
}
onEnable() {
this.enterState(this.currentState);
}
onDisable() {
this.exitState(this.currentState);
}
}
模式 3:延迟初始化
@ccclass('LazyInit')
export class LazyInit extends Component {
private initialized = false;
update(dt: number) {
if (!this.initialized) {
// 确保所有依赖都已就绪
this.doInit();
this.initialized = true;
}
// 正常逻辑
}
}
七、常见陷阱
陷阱 1:onLoad 中访问未加载的资源
// 错误示例
onLoad() {
// texture 可能还未加载完成
this.getComponent(Sprite).spriteFrame = this.texture;
}
// 正确示例:使用资源加载 API
start() {
resources.load('textures/hero', SpriteFrame, (err, asset) => {
this.getComponent(Sprite).spriteFrame = asset;
});
}
陷阱 2:update 中频繁创建对象
// 错误示例
update(dt) {
const pos = `${this.node.position.x},${this.node.position.y}`;
this.label.string = pos;
}
// 正确示例
private tempVec = new Vec3();
update(dt) {
Vec3.copy(this.tempVec, this.node.position);
this.label.string = `${this.tempVec.x.toFixed(1)},${this.tempVec.y.toFixed(1)}`;
}
陷阱 3:销毁后访问节点
// 错误示例
onDestroy() {
const parent = this.node.parent;
parent.removeChild(this.node); // 节点可能已经不在场景树中
}
// 正确示例:不需要手动移除,destroy() 已经处理
onDestroy() {
// 只做清理逻辑
this.unscheduleAllCallbacks();
}
陷阱 4:onDisable 中触发新的生命周期
// 危险操作
onDisable() {
this.node.active = true; // 可能导致无限循环
}
八、生命周期执行顺序图
场景加载
│
├─ 所有节点 onLoad(从根到叶)
│
├─ 所有节点 start(从根到叶)
│
├─ [游戏循环开始]
│ │
│ ├─ update(dt) ← 每帧执行
│ ├─ lateUpdate(dt) ← 每帧执行
│ │
│ └─ [下一帧...]
│
└─ 场景卸载
├─ 所有节点 onDisable
└─ 所有节点 onDestroy
九、总结
| 阶段 | 关键回调 | 适合做什么 |
|---|---|---|
| 初始化 | onLoad | 获取引用、注册事件 |
| 启动 | start | 业务逻辑初始化、跨组件依赖 |
| 运行 | update / lateUpdate | 移动、碰撞检测、摄像机跟随 |
| 失活 | onDisable | 注销事件、停止定时器 |
| 销毁 | onDestroy | 释放资源、最终清理 |
掌握生命周期是 Cocos Creator 开发的基础。建议在实际项目中多观察日志输出,加深对执行顺序的理解。
如果您觉得这篇文章有帮助,请点个赞吧~
相关文章
更多文章 →cocos2026-07-22
🚀 Cocos Creator 新手入门指南
一、Cocos Creator 是什么 Cocos Creator 是一款专注于 2D 和 3D 游戏开发的引擎,由 Cocos 团队开发。它的核心理念是 组件化开发 ——把游戏中的每个功能拆成独立的组件,像搭积木一样组合起来。 主要特点 : 完全免费,支持发布到 Web、iOS、Android、桌面等多平台 使用 TypeScript 编写脚本,对前端开发者非常友好 编辑器可视化操作,所见即所得 内置资源管理、动画系统、UI 系统、物...
学习
cocos2025-10-11
🚀 cocos进阶语法
1\. 组件设计与脚本复用 在 Cocos Creator 中, 一切都是组件 。 你可以把通用逻辑封装成独立组件,实现模块化开发。 ✅ 建议 : 把“功能性逻辑”(移动、旋转、漂浮、点击反应等)做成通用组件,避免在业务逻辑脚本里重复写。 2\. 单例与全局管理器 大型项目通常需要全局控制器,比如音效、玩家数据、网络。 使用: ✅ 建议 : 使用“单例模式”集中管理音效、事件、UI、对象池等,减少节点层级耦合。 3\. 动画系统与状态机...
学习
cocos2025-09-23
📘 Cocos Creator 语法入门文档
📘 Cocos Creator 语法入门文档 1. 基础概念 Cocos Creator 使用 TypeScript/JavaScript 作为主要开发语言,游戏逻辑通常通过脚本来驱动。主要模块: 节点 (Node) :场景中的基本元素,可以理解为游戏对象。 组件 (Component) :附加到节点上的脚本/功能,比如渲染、物理、脚本逻辑。 场景 (Scene) :游戏运行的基本单位,包含多个节点。 资源 (Assets) :图片、...
学习
AI2026-09-01
Deep Agents 01:何为 Agent Harness,以及如何开始
1、本篇任务:完成一份多步骤、带证据的技术调研 普通客服 Agent 的问题短、工具少、输出即时。技术调研或编码任务会持续很久,产生计划、搜索结果、文件和中间结论。Deep Agents 在 LangChain/LangGraph 之上预装规划、虚拟文件系统、上下文压缩和子 Agent,适合这类开放任务。 本课让 Agent 比较两种向量数据库,并交付一份可验证报告。 2、什么时候需要 Deep Agent 满足以下两项以上再考虑:任务...
学习
AI2026-09-01
Deep Agents 02:子 Agent、虚拟文件系统与长期记忆
1、本篇任务:让主管只看结论,让子 Agent 处理细节 技术调研会产生几十次搜索和大量文件。如果全部进入主管上下文,真正的目标会被噪音淹没。本课用两个子 Agent: 收集证据, 检查结论;主管负责计划与最终合成。 2、什么时候委派,什么时候直接调用工具 适合委派:子任务有多步;需要专门提示或工具;会产生大量中间结果;只需返回有限结论。不适合:一步查询;主管需要全部中间上下文;协调成本超过任务本身。 3、配置专门子 Agent Pyt...
学习
AI2026-09-01
Deep Agents 03:生产化、Sandbox、权限与上线验收
1、本篇任务:让 Deep Agent 在隔离环境中分析代码 只读研究 Agent 风险有限;编码 Agent 需要读写文件、安装依赖和执行测试。本课不讲如何让模型写更漂亮的代码,只讲执行环境、权限、恢复和上线验收。 2、先做威胁模型 资产包括源代码、用户文件、云凭证、生产网络和发布权限;攻击入口包括用户消息、仓库内容、网页、依赖包、MCP 返回和命令输出。 Prompt injection 不是靠一句 system prompt 解决...
学习
评论
请登录后发表评论
去登录