关于 theone-cocos-standards
面向 Cocos Creator 3.x 和 TypeScript 4.1+ 开发者的编码技能,提供代码质量、组件架构及性能优化规范。适用于可玩广告开发、代码审查与重构。
主要功能
- 01TypeScript 严格模式与类型安全
- 02组件生命周期与事件管理
- 03资源加载与对象池建议
- 04可玩广告性能优化
- 05代码审查检查清单
适用场景
- 01编写或重构 Cocos Creator 代码
- 02检查 TypeScript 代码质量
- 03优化可玩广告性能
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ the1studio/ theone-training-skills --skill theone-cocos-standards安装后在 Claude Code 中输入/theone-cocos-standards确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/framework/component-system.mdevent-patterns.mdplayable-optimization.mdsize-optimization.mdreferences/language/modern-typescript.mdperformance.mdquality-hygiene.mdreferences/review/architecture-review.mdperformance-review.mdquality-review.md详细介绍
TheOne Studio Cocos Creator 开发规范
⚠️ Cocos Creator 3.x(TypeScript 4.1+): 所有模式和示例均适用于 Cocos Creator 3.x 可玩广告开发。
技能用途
此技能通过以下标准落实 TheOne Studio 全面的 Cocos Creator 开发规范,并将代码质量放在首位:
优先级 1:代码质量与规范(最重要)
- TypeScript 严格模式、ESLint 配置、访问修饰符(public/private/protected)
- 抛出异常(绝不静默处理错误)
- 开发期间使用 console.log,在生产构建中移除
- 不可变字段使用 readonly,常量使用 const
- 不写行内注释(使用描述性名称)
- 妥善处理错误并确保类型安全
优先级 2:现代 TypeScript 模式
- 优先使用数组方法(map/filter/reduce),而非循环
- 箭头函数、解构、展开运算符
- 可选链、空值合并
- 类型守卫、工具类型(Partial、Required、Readonly)
- 现代 TypeScript 特性
优先级 3:Cocos Creator 架构
- 基于组件的实体-组件(EC)系统
- 生命周期方法:onLoad→start→onEnable→update→onDisable→onDestroy
- 自定义事件采用 EventDispatcher 模式
- 节点事件系统(EventTouch、键盘事件)
- 可玩广告的资源管理与对象池
优先级 4:可玩广告性能
- DrawCall 批处理(目标:<10 个 DrawCall)
- Sprite 图集配置(启用自动图集)
- 骨骼动画使用 GPU 蒙皮
- update() 循环中零内存分配
- Bundle 大小 <5MB(纹理压缩、代码压缩)
此技能的触发场景
- 编写或重构 Cocos Creator TypeScript 代码
- 实现可玩广告功能
- 使用组件生命周期和事件
- 优化可玩广告性能
- 审查代码变更或拉取请求
- 搭建可玩项目架构
- 减小 Bundle 大小或 DrawCall 数量
快速参考指南
需要什么帮助?
| 优先级 | 任务 | 参考资料 |
|---|---|---|
| 🔴 优先级 1:代码质量(首先检查) | ||
| 1 | TypeScript 严格模式、ESLint、访问修饰符 | 质量与规范 ⭐ |
| 1 | 抛出异常,正确处理错误 | 质量与规范 ⭐ |
| 1 | console.log(仅用于开发),生产环境中移除 | 质量与规范 ⭐ |
| 1 | readonly/const、无内联注释、使用描述性名称 | 质量与规范 ⭐ |
| 🟡 优先级 2:现代 TypeScript 模式 | ||
| 2 | 数组方法、箭头函数、解构 | 现代 TypeScript |
| 2 | 可选链、空值合并 | 现代 TypeScript |
| 2 | 类型守卫、工具类型 | 现代 TypeScript |
| 🟢 优先级 3:Cocos 架构 | ||
| 3 | 组件系统、@property 装饰器 | 组件系统 |
| 3 | 生命周期方法(onLoad→start→update→onDestroy) | 组件系统 |
| 3 | EventDispatcher、节点事件、清理 | 事件模式 |
| 3 | 资源加载、对象池、内存管理 | 可玩广告优化 |
| 🔵 优先级 4:性能与审查 | ||
| 4 | DrawCall 批处理、精灵图集、GPU 蒙皮 | 可玩广告优化 |
| 4 | 更新循环优化、零分配 | 性能 |
| 4 | 缩减包体积(目标 <5MB) | 体积优化 |
| 4 | 架构审查(组件、生命周期、事件) | 架构审查 |
| 4 | TypeScript 质量审查 | 质量审查 |
| 4 | 性能审查(DrawCalls、分配) | 性能审查 |
🔴 关键:代码质量规则(首先检查!)
⚠️ 强制质量标准
编写任何代码之前,务必先落实以下要求:
- 启用 TypeScript 严格模式——在 tsconfig.json 中设置 "strict": true
- 使用 ESLint 配置——启用 @typescript-eslint 规则
- 使用访问修饰符——为所有成员指定 public/private/protected
- 遇到错误时抛出异常——绝不静默失败或返回 undefined
- console.log 仅用于开发——在生产构建中移除所有 console 语句
- 不可变字段使用 readonly——为不会重新赋值的字段标记 readonly
- 常量使用 const——常量应使用 const,而不是 let
- 不写内联注释——使用描述性名称,让代码能够自解释
- 妥善处理 null/undefined——使用可选链和空值合并
- 确保类型安全——避免使用
any类型,使用恰当的类型和接口
示例:优先落实代码质量要求
// ✅ EXCELLENT: All quality rules enforced
import { _decorator, Component, Node, EventTouch } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('PlayerController')
export class PlayerController extends Component {
// 3. Access modifier, 6. readonly for immutable
@property(Node)
private readonly targetNode: Node | null = null;
// 7. const for constants
private static readonly MAX_HEALTH: number = 100;
private currentHealth: number = 100;
// Lifecycle: onLoad → start → onEnable
protected onLoad(): void {
// 4. Throw exception for errors
if (!this.targetNode) {
throw new Error('PlayerController: targetNode is not assigned');
}
// 9. Proper event listener setup
this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
}
protected onDestroy(): void {
// 9. Always cleanup event listeners
this.node.off(Node.EventType.TOUCH_START, this.onTouchStart, this);
}
private onTouchStart(event: EventTouch): void {
// 5. console.log only for development (remove in production)
if (CC_DEBUG) {
console.log('Touch detected');
}
this.takeDamage(10);
}
// 8. Descriptive method names (no inline comments needed)
private takeDamage(amount: number): void {
this.currentHealth -= amount;
if (this.currentHealth <= 0) {
this.handlePlayerDeath();
}
}
private handlePlayerDeath(): void {
// Death logic
}
}
⚠️ Cocos Creator 架构规则(质量要求之后)
组件系统基础
实体-组件(EC)系统:
- 组件继承
Component类 - 使用
@ccclass和@property装饰器 - 生命周期:onLoad → start → onEnable → update → lateUpdate → onDisable → onDestroy
执行顺序:
- onLoad() - 组件初始化,一次性设置
- start() - 所有组件加载完毕后调用,此时可以引用其他组件
- onEnable() - 组件/节点启用时调用(可以多次调用)
- update(dt) - 每帧调用(在可玩广告中谨慎使用)
- lateUpdate(dt) - 所有 update() 调用完成后调用
- onDisable() - 组件/节点禁用时调用
- onDestroy() - 清理、移除监听器、释放资源
通用规则:
- ✅ 在 onLoad() 中初始化,在 start() 中引用其他组件
- ✅ 在 onEnable() 中注册事件,在 onDisable() 中取消注册
- ✅ 始终在 onDestroy() 中清理监听器
- ✅ 避免在 update() 中执行繁重逻辑(这对可玩广告的性能至关重要)
- ✅ 对不应重新赋值的 @property 字段使用 readonly
- ✅ 缺少必需引用时抛出异常
简要示例
🔴 优先保证代码质量
// ✅ EXCELLENT: Quality rules enforced
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('GameManager')
export class GameManager extends Component {
@property(Node)
private readonly playerNode: Node | null = null;
private static readonly MAX_SCORE: number = 1000;
private currentScore: number = 0;
protected onLoad(): void {
// Throw exception for missing required references
if (!this.playerNode) {
throw new Error('GameManager: playerNode is required');
}
if (CC_DEBUG) {
console.log('GameManager initialized'); // Development only
}
}
public addScore(points: number): void {
if (points <= 0) {
throw new Error('GameManager.addScore: points must be positive');
}
this.currentScore = Math.min(
this.currentScore + points,
GameManager.MAX_SCORE
);
}
}
🟡 现代 TypeScript 模式
// ✅ GOOD: Array methods instead of loops
const activeEnemies = allEnemies.filter(e => e.isActive);
const enemyPositions = activeEnemies.map(e => e.node.position);
// ✅ GOOD: Optional chaining and nullish coalescing
const playerName = player?.name ?? 'Unknown';
// ✅ GOOD: Destructuring
const { x, y } = this.node.position;
// ✅ GOOD: Arrow functions
this.enemies.forEach(enemy => enemy.takeDamage(10));
// ✅ GOOD: Type guards
function isPlayer(node: Node): node is PlayerNode {
return node.getComponent(PlayerController) !== null;
}
🟢 Cocos Creator 组件模式
import { _decorator, Component, Node, EventTouch, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('TouchHandler')
export class TouchHandler extends Component {
@property(Node)
private readonly targetNode: Node | null = null;
private readonly tempVec3: Vec3 = new Vec3(); // Reusable vector
// 1. onLoad: Initialize component
protected onLoad(): void {
if (!this.targetNode) {
throw new Error('TouchHandler: targetNode is required');
}
}
// 2. start: Reference other components (if needed)
protected start(): void {
// Can safely access other components here
}
// 3. onEnable: Register event listeners
protected onEnable(): void {
this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
}
// 4. onDisable: Unregister event listeners
protected onDisable(): void {
this.node.off(Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.off(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
}
// 5. onDestroy: Final cleanup
protected onDestroy(): void {
// Release any additional resources
}
private onTouchStart(event: EventTouch): void {
// Handle touch
}
private onTouchMove(event: EventTouch): void {
// Reuse vector to avoid allocations
this.targetNode!.getPosition(this.tempVec3);
this.tempVec3.y += 10;
this.targetNode!.setPosition(this.tempVec3);
}
}
🟢 事件分发器模式
import { _decorator, Component, EventTarget } from 'cc';
const { ccclass } = _decorator;
// Custom event types
export enum GameEvent {
SCORE_CHANGED = 'score_changed',
LEVEL_COMPLETE = 'level_complete',
PLAYER_DIED = 'player_died',
}
export interface ScoreChangedEvent {
oldScore: number;
newScore: number;
}
@ccclass('EventManager')
export class EventManager extends Component {
private static instance: EventManager | null = null;
private readonly eventTarget: EventTarget = new EventTarget();
protected onLoad(): void {
if (EventManager.instance) {
throw new Error('EventManager: instance already exists');
}
EventManager.instance = this;
}
public static emit(event: GameEvent, data?: any): void {
if (!EventManager.instance) {
throw new Error('EventManager: instance not initialized');
}
EventManager.instance.eventTarget.emit(event, data);
}
public static on(event: GameEvent, callback: Function, target?: any): void {
if (!EventManager.instance) {
throw new Error('EventManager: instance not initialized');
}
EventManager.instance.eventTarget.on(event, callback, target);
}
public static off(event: GameEvent, callback: Function, target?: any): void {
if (!EventManager.instance) {
throw new Error('EventManager: instance not initialized');
}
EventManager.instance.eventTarget.off(event, callback, target);
}
}
// Usage in component
@ccclass('ScoreDisplay')
export class ScoreDisplay extends Component {
protected onEnable(): void {
EventManager.on(GameEvent.SCORE_CHANGED, this.onScoreChanged, this);
}
protected onDisable(): void {
EventManager.off(GameEvent.SCORE_CHANGED, this.onScoreChanged, this);
}
private onScoreChanged(data: ScoreChangedEvent): void {
console.log(`Score: ${data.oldScore} → ${data.newScore}`);
}
}
🔵 可玩广告性能优化
import { _decorator, Component, Node, Sprite, SpriteAtlas } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('OptimizedSpriteManager')
export class OptimizedSpriteManager extends Component {
// Use sprite atlas for DrawCall batching
@property(SpriteAtlas)
private readonly characterAtlas: SpriteAtlas | null = null;
// Preallocate arrays to avoid allocations in update()
private readonly tempNodes: Node[] = [];
private frameCount: number = 0;
protected onLoad(): void {
if (!this.characterAtlas) {
throw new Error('OptimizedSpriteManager: characterAtlas is required');
}
// Prewarm sprite frames from atlas
this.prewarmSpriteFrames();
}
private prewarmSpriteFrames(): void {
// Load all sprites from atlas (batched in single DrawCall)
const spriteFrame = this.characterAtlas!.getSpriteFrame('character_idle');
if (!spriteFrame) {
throw new Error('Sprite frame not found in atlas');
}
}
// Optimize update: avoid allocations, use object pooling
protected update(dt: number): void {
// Run expensive operations every N frames instead of every frame
this.frameCount++;
if (this.frameCount % 10 === 0) {
this.updateExpensiveOperation();
}
}
private updateExpensiveOperation(): void {
// Reuse array instead of creating new one
this.tempNodes.length = 0;
// Batch operations to reduce DrawCalls
}
}
代码审查清单
快速验证(提交前)
🔴 代码质量(优先检查):
- 在 tsconfig.json 中启用 TypeScript 严格模式
- ESLint 规则检查通过(无错误)
- 所有访问修饰符正确(public/private/protected)
- 出错时抛出异常(不静默失败)
- 移除 console.log 或将其封装在 CC_DEBUG 中
- 对不会重新赋值的字段使用 readonly
- 对常量使用 const
- 不写行内注释(代码应自明)
- 正确处理 null/undefined
- 不使用
any类型(使用适当的类型)
🟡 现代 TypeScript 模式:
- 使用数组方法代替手动循环
- 回调使用箭头函数
- 使用可选链 (?.) 安全访问属性
- 使用空值合并运算符 (??) 设置默认值
- 使用解构让代码更简洁
- 使用类型守卫缩小类型范围
🟢 Cocos Creator 架构:
- 组件生命周期方法的顺序正确
- 使用 onLoad() 初始化,使用 start() 引用其他组件
- 在 onEnable() 中注册事件监听器
- 在 onDisable() 中取消注册事件监听器
- 在 onDestroy() 中释放资源
- 正确使用 @property 装饰器
- 验证必需引用(如果为 null 则抛出异常)
🔵 可玩广告性能:
- update() 循环中不进行内存分配
- 使用精灵图集进行 DrawCall 批处理
- 为骨骼动画启用 GPU 蒙皮
- 限制高开销操作的频率(不要每帧执行)
- 对频繁创建的对象使用对象池
- 启用纹理压缩
- Bundle 大小目标 <5MB
- DrawCall 数量目标 <10
应避免的常见错误
❌ 不要:
- 忽略 TypeScript 严格模式 → 启用 "strict": true
- 静默处理错误 → 出错时抛出异常
- 在生产环境中保留 console.log → 移除或将其封装在 CC_DEBUG 中
- 省略访问修饰符 → 使用 public/private/protected
- 使用
any类型 → 定义适当的类型和接口 - 添加行内注释 → 改用描述性名称
- 跳过事件清理 → 始终在 onDisable/onDestroy 中取消注册
- 在 update() 中分配内存 → 预先分配并复用对象
- 忘记精灵图集 → 使用图集进行 DrawCall 批处理
- 在 update() 中执行繁重逻辑 → 限制高开销操作的频率
- 跳过 null 检查 → 在 onLoad 中验证必需引用
- 使用可变的 @property 字段 → 适当时使用 readonly
- 手动遍历数组 → 使用 map/filter/reduce
- 忽略 Bundle 大小 → 监控并优化(目标 <5MB)
✅ 应做:
- 启用 TypeScript 严格模式("strict": true)
- 遇到错误时抛出异常(绝不静默失败)
- 仅在开发期间使用 console.log(生产环境中移除)
- 使用访问修饰符(public/private/protected)
- 定义恰当的类型(避免使用
any) - 使用描述性名称(不要添加行内注释)
- 始终清理事件(onDisable/onDestroy)
- 预分配对象(在 update() 中重复使用)
- 使用精灵图集(批处理 DrawCall)
- 对开销大的操作进行限流(不要每帧执行)
- 验证必需的引用(如果为 null,则在 onLoad 中抛出异常)
- 在适当情况下为 @property 使用 readonly
- 使用数组方法(map/filter/reduce)
- 监控包体积(可玩广告的目标为 <5MB)
审查严重级别
🔴 严重(必须修复)
- 禁用了 TypeScript 严格模式 - 必须启用 "strict": true
- 静默处理错误 - 遇到错误时必须抛出异常
- 生产代码中存在 console.log - 移除或用 CC_DEBUG 包裹
- 缺少访问修饰符 - 所有成员都必须有修饰符
- 无正当理由使用
any类型 - 定义恰当的类型 - 使用行内注释而非描述性名称 - 重命名并移除注释
- 未清理事件监听器 - 会造成内存泄漏,必须取消注册
- 缺少必需引用验证 - 如果为 null,必须在 onLoad 中抛出异常
- 在 update() 循环中分配对象 - 这是关键性能问题,必须预分配
- 多个精灵未使用精灵图集 - 会导致 DrawCall 激增,必须使用图集
- 包体积 >5MB - 超出可玩广告限制,必须优化
🟡 重要(应该修复)
- @property 字段缺少 readonly - 如果不会重新赋值,应该使用 readonly
- 常量缺少 const - 应使用 const 而不是 let
- 使用手动循环而非数组方法 - 应使用 map/filter/reduce
- 缺少可选链 - 应使用 ?. 进行安全访问
- 缺少空值合并运算符 - 应使用 ?? 设置默认值
- update() 中包含繁重逻辑 - 应对开销大的操作进行限流
- 频繁分配对象但未使用对象池 - 应实现对象池
- 未启用纹理压缩 - 应启用以缩小包体积
- DrawCall 数量 >10 - 应优化批处理
🟢 可选(建议)
- 可以在回调中使用箭头函数
- 可以使用解构让代码更简洁
- 可以使用类型守卫提高类型安全性
- 可以改进命名以提高清晰度
- 可以添加接口以获得更好的类型定义
- 可以优化算法以提升性能
详细参考
TypeScript 语言标准
- 质量与规范 - 严格模式、ESLint、访问修饰符、错误处理
- 现代 TypeScript - 数组方法、可选链、类型守卫、工具类型
- 性能 - 更新循环优化、零分配、缓存
Cocos Creator 框架
- 组件系统 - EC 系统、生命周期方法、@property 装饰器
- 事件模式 - EventDispatcher、节点事件、订阅清理
- 可玩广告优化 - DrawCall 批处理、精灵图集、GPU 蒙皮、资源池化
- 体积优化 - 包体积缩减、纹理压缩、构建优化
代码审查
摘要
此技能为 TheOne Studio 的可玩广告团队提供全面的 Cocos Creator 开发规范:
- TypeScript 卓越实践:严格模式、现代模式和类型安全
- Cocos 架构:组件生命周期、事件模式和资源管理
- 可玩广告性能:DrawCall 批处理、GPU 蒙皮、<5MB 的包体
- 代码质量:落实质量、规范和性能规则
使用上方的快速参考指南,查找所需的具体模式。
评论
支持 Markdown