theone-cocos-standards について
Cocos Creator 3.x と TypeScript 4.1+ の開発者向けコーディングスキルです。コード品質、コンポーネント設計、パフォーマンスの指針を示し、プレイアブル広告の開発やレビュー、リファクタリングに対応します。
主な機能
- 01TypeScriptの厳格モードと型安全性
- 02コンポーネントのライフサイクルとイベント管理
- 03リソース読み込みとオブジェクトプールの指針
- 04プレイアブル広告の最適化
- 05コードレビュー用チェックリスト
活用シーン
- 01Cocos Creatorコードの作成やリファクタリング
- 02TypeScriptコード品質の確認
- 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 の strict モード、ESLint の設定、アクセス修飾子(public/private/protected)
- 例外をスローする(エラーを黙って処理しない)
- 開発時は console.log を使用し、本番ビルドでは削除する
- 不変のフィールドには readonly、定数には const を使用する
- インラインコメントは使用しない(説明的な名前を使用する)
- 適切なエラー処理と型安全性
優先度 2:最新の TypeScript パターン
- ループより配列メソッド(map/filter/reduce)を優先する
- アロー関数、分割代入、スプレッド演算子
- オプショナルチェーン、Null 合体演算子
- 型ガード、ユーティリティ型(Partial、Required、Readonly)
- 最新の TypeScript 機能
優先度 3:Cocos Creator のアーキテクチャ
- コンポーネントベースのエンティティ・コンポーネント(EC)システム
- ライフサイクルメソッド:onLoad→start→onEnable→update→onDisable→onDestroy
- カスタムイベント向けの EventDispatcher パターン
- ノードイベントシステム(EventTouch、キーボードイベント)
- プレイアブル広告向けのリソース管理とプーリング
優先度 4:プレイアブル広告のパフォーマンス
- DrawCall のバッチ処理(目標:10 未満)
- スプライトアトラスの設定(自動アトラスを有効化)
- スケルタルアニメーションに GPU スキニングを使用する
- update() ループ内のアロケーションをゼロにする
- バンドルサイズを 5 MB 未満にする(テクスチャ圧縮、コードの縮小)
このスキルが適用される場面
- Cocos Creator の TypeScript コードを作成またはリファクタリングするとき
- プレイアブル広告の機能を実装するとき
- コンポーネントのライフサイクルやイベントを扱うとき
- プレイアブル広告のパフォーマンスを最適化するとき
- コード変更やプルリクエストをレビューするとき
- プレイアブルプロジェクトのアーキテクチャを設定するとき
- バンドルサイズや DrawCall 数を削減するとき
クイックリファレンスガイド
何についてサポートが必要ですか?
| 優先度 | タスク | 参照 |
|---|---|---|
| 🔴 優先度 1:コード品質(最初に確認) | ||
| 1 | TypeScript の strict モード、ESLint、アクセス修飾子 | 品質と衛生 ⭐ |
| 1 | 例外のスロー、適切なエラー処理 | 品質と衛生 ⭐ |
| 1 | console.log(開発時のみ)、本番環境では削除 | 品質と衛生 ⭐ |
| 1 | readonly/const、インラインコメントを使わない、説明的な名前 | 品質と衛生 ⭐ |
| 🟡 優先度 2:最新の TypeScript パターン | ||
| 2 | 配列メソッド、アロー関数、分割代入 | 最新の TypeScript |
| 2 | オプショナルチェーン、Null 合体演算子 | 最新の TypeScript |
| 2 | 型ガード、ユーティリティ型 | 最新の TypeScript |
| 🟢 優先度 3:Cocos アーキテクチャ | ||
| 3 | コンポーネントシステム、@property デコレーター | コンポーネントシステム |
| 3 | ライフサイクルメソッド(onLoad→start→update→onDestroy) | コンポーネントシステム |
| 3 | EventDispatcher、Node イベント、クリーンアップ | イベントパターン |
| 3 | リソースの読み込み、プーリング、メモリ管理 | プレイアブル最適化 |
| 🔵 優先度 4:パフォーマンスとレビュー | ||
| 4 | DrawCall のバッチ処理、スプライトアトラス、GPU スキニング | プレイアブル最適化 |
| 4 | 更新ループの最適化、アロケーションゼロ | パフォーマンス |
| 4 | バンドルサイズの削減(目標:<5MB) | サイズ最適化 |
| 4 | アーキテクチャレビュー(コンポーネント、ライフサイクル、イベント) | アーキテクチャレビュー |
| 4 | TypeScript の品質レビュー | 品質レビュー |
| 4 | パフォーマンスレビュー(DrawCalls、アロケーション) | パフォーマンスレビュー |
🔴 重大:コード品質ルール(最初に確認!)
⚠️ 必須の品質基準
コードを書く前に、必ず以下を徹底してください:
- TypeScript の strict モードを有効にする - tsconfig.json で "strict": true にする
- ESLint の設定を使用する - @typescript-eslint のルールを有効にする
- アクセス修飾子を使用する - すべてのメンバーに public/private/protected を指定する
- エラー時は例外をスローする - サイレントな失敗や undefined を返すことは絶対に避ける
- console.log は開発時のみ使用する - 本番ビルドでは console 文をすべて削除する
- 変更されないフィールドには readonly を使用する - 再代入されないフィールドに指定する
- 定数には const を使用する - 定数には let ではなく const を使用する
- インラインコメントを使用しない - 説明的な名前を使用し、コード自体を自己説明的にする
- null/undefined を適切に処理する - オプショナルチェーンと Null 合体演算子を使用する
- 型安全性を確保する -
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 の strict モードが有効になっている
- ESLint のルールに準拠している(エラーなし)
- すべてのアクセス修飾子が適切である(public/private/protected)
- エラー時に例外をスローする(エラーを黙って無視しない)
- console.log を削除するか、CC_DEBUG で囲んでいる
- 再代入しないフィールドに readonly を使用している
- 定数に const を使用している
- インラインコメントがない(コードだけで意図がわかる)
- null/undefined を適切に処理している
-
any型を使用していない(適切な型を使用する)
🟡 モダンな TypeScript のパターン:
- 手動のループではなく配列メソッドを使用している
- コールバックにアロー関数を使用している
- 安全なプロパティアクセスにオプショナルチェーン(?.)を使用している
- デフォルト値に Null 合体演算子(??)を使用している
- コードをすっきりさせるために分割代入を使用している
- 型の絞り込みに型ガードを使用している
🟢 Cocos Creator のアーキテクチャ:
- コンポーネントのライフサイクルメソッドが正しい順序になっている
- 初期化には onLoad()、参照には start() を使用している
- イベントリスナーを onEnable() で登録している
- イベントリスナーを onDisable() で登録解除している
- onDestroy() でリソースを解放している
- @property デコレーターを正しく使用している
- 必須の参照を検証している(null の場合は例外をスローする)
🔵 プレイアブルのパフォーマンス:
- update() ループ内でメモリ割り当てを行わない
- DrawCall のバッチ処理にスプライトアトラスを使用している
- スケルタルアニメーションで GPU スキニングを有効にしている
- 負荷の高い処理を毎フレームではなく間引いて実行している
- 頻繁に生成されるオブジェクトにオブジェクトプーリングを使用している
- テクスチャ圧縮を有効にしている
- バンドルサイズの目標が 5MB 未満である
- DrawCall 数の目標が 10 未満である
よくある間違いとその回避方法
❌ やってはいけないこと:
- TypeScript の strict モードを無視する → "strict": true を有効にします
- エラーを黙って処理する → エラー時に例外をスローします
- 本番環境に console.log を残す → 削除するか、CC_DEBUG で囲みます
- アクセス修飾子を省略する → public/private/protected を使用します
any型を使用する → 適切な型とインターフェースを定義します- インラインコメントを追加する → 代わりに説明的な名前を使用します
- イベントのクリーンアップを省略する → 必ず onDisable/onDestroy で登録解除します
- update() 内でメモリを割り当てる → あらかじめ割り当てたオブジェクトを再利用します
- スプライトアトラスを忘れる → DrawCall のバッチ処理にアトラスを使用します
- update() で負荷の高いロジックを実行する → 負荷の高い処理を間引いて実行します
- null チェックを省略する → onLoad で必須の参照を検証します
- 変更可能な @property フィールドを使用する → 適切な場合は readonly を使用します
- 配列を手動でループ処理する → map/filter/reduce を使用します
- バンドルサイズを無視する → サイズを監視して最適化します(目標:5MB 未満)
✅ DO:
- TypeScript の strict モードを有効にする("strict": true)
- エラー時には例外をスローする(エラーを黙って処理しない)
- 開発時のみ console.log を使用する(本番環境では削除する)
- アクセス修飾子を使用する(public/private/protected)
- 適切な型を定義する(
anyを避ける) - 説明的な名前を使用する(インラインコメントは使用しない)
- イベントを必ずクリーンアップする(onDisable/onDestroy)
- オブジェクトを事前に確保する(update() で再利用する)
- スプライトアトラスを使用する(DrawCall のバッチ処理)
- 負荷の高い処理をスロットリングする(毎フレーム実行しない)
- 必須の参照を検証する(null の場合は onLoad で例外をスローする)
- @property に readonly を使用する(適切な場合)
- 配列メソッドを使用する(map/filter/reduce)
- バンドルサイズを監視する(プレイアブルでは <5MB を目標にする)
レビューの重大度レベル
🔴 重大(必ず修正)
- TypeScript の strict モードが無効 - "strict": true を有効にする必要があります
- エラーを黙って処理している - エラー時には例外をスローする必要があります
- 本番コードに console.log がある - 削除するか、CC_DEBUG で囲む必要があります
- アクセス修飾子がない - すべてのメンバーに修飾子を指定する必要があります
- 正当な理由なく
any型を使用している - 適切な型を定義する必要があります - 説明的な名前の代わりにインラインコメントを使用している - 名前を変更し、コメントを削除する必要があります
- イベントリスナーがクリーンアップされていない - メモリーリークが発生するため、登録解除する必要があります
- 必須の参照が検証されていない - null の場合は onLoad で例外をスローする必要があります
- update() ループ内で割り当てを行っている - パフォーマンス上重大なため、事前に確保する必要があります
- 複数のスプライトにスプライトアトラスを使用していない - DrawCall が急増するため、アトラスを使用する必要があります
- バンドルサイズが >5MB - プレイアブルの上限を超えているため、最適化する必要があります
🟡 重要(修正を推奨)
- @property フィールドに readonly がない - 再代入しない場合は readonly にする必要があります
- 定数に const がない - let ではなく const を使用する必要があります
- 配列メソッドの代わりに手動ループを使用している - map/filter/reduce を使用する必要があります
- オプショナルチェーンがない - 安全にアクセスするために ?. を使用する必要があります
- Null 合体演算子がない - デフォルト値には ?? を使用する必要があります
- update() に負荷の高いロジックがある - 負荷の高い処理をスロットリングする必要があります
- 頻繁な割り当てにオブジェクトプーリングを使用していない - プーリングを実装する必要があります
- テクスチャ圧縮が有効になっていない - バンドルサイズを小さくするため、有効にする必要があります
- DrawCall 数が >10 - バッチ処理を最適化する必要があります
🟢 あると望ましい(提案)
- コールバックにアロー関数を使用できます
- コードをすっきりさせるために分割代入を使用できます
- 型安全性のために型ガードを使用できます
- 明確さのために命名を改善できます
- より適切な型付けのためにインターフェースを追加できます
- パフォーマンス向上のためにアルゴリズムを最適化できます
詳細なリファレンス
TypeScript 言語標準
- 品質と衛生管理 - strict モード、ESLint、アクセス修飾子、エラー処理
- モダンな TypeScript - 配列メソッド、オプショナルチェーン、型ガード、ユーティリティ型
- パフォーマンス - 更新ループの最適化、割り当てゼロ、キャッシュ
Cocos Creator フレームワーク
- コンポーネントシステム - EC システム、ライフサイクルメソッド、@property デコレーター
- イベントパターン - EventDispatcher、Node イベント、サブスクリプションのクリーンアップ
- プレイアブルの最適化 - DrawCall のバッチ処理、スプライトアトラス、GPU スキニング、リソースプーリング
- サイズの最適化 - バンドルサイズの削減、テクスチャ圧縮、ビルドの最適化
コードレビュー
- アーキテクチャレビュー - コンポーネント違反、ライフサイクルエラー、イベントリーク
- 品質レビュー - TypeScript の品質問題、アクセス修飾子、エラー処理
- パフォーマンスレビュー - プレイアブル特有のパフォーマンス問題、DrawCall、割り当て
概要
このスキルは、TheOne Studio のプレイアブル広告チーム向けに、Cocos Creator 開発の包括的な標準を提供します。
- TypeScript の卓越性:厳格モード、最新のパターン、型安全性
- Cocos のアーキテクチャ:コンポーネントのライフサイクル、イベントパターン、リソース管理
- プレイアブル広告のパフォーマンス:DrawCall のバッチ処理、GPU スキニング、5MB 未満のバンドル
- コード品質:品質、衛生、パフォーマンスに関するルールを徹底
上記のクイックリファレンスガイドを使用して、必要なパターンに移動してください。
コメント
Markdown 対応