mcpField
T
theone-cocos-standards

theone-cocos-standards

github.com/the1studio開発195 回インストール
リポジトリ

theone-cocos-standards について

Cocos Creator 3.x と TypeScript 4.1+ の開発者向けコーディングスキルです。コード品質、コンポーネント設計、パフォーマンスの指針を示し、プレイアブル広告の開発やレビュー、リファクタリングに対応します。

主な機能

  1. 01TypeScriptの厳格モードと型安全性
  2. 02コンポーネントのライフサイクルとイベント管理
  3. 03リソース読み込みとオブジェクトプールの指針
  4. 04プレイアブル広告の最適化
  5. 05コードレビュー用チェックリスト

活用シーン

  1. 01Cocos Creatorコードの作成やリファクタリング
  2. 02TypeScriptコード品質の確認
  3. 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.md
event-patterns.md
playable-optimization.md
size-optimization.md
references/language/
modern-typescript.md
performance.md
quality-hygiene.md
references/review/
architecture-review.md
performance-review.md
quality-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:コード品質(最初に確認)
1TypeScript の strict モード、ESLint、アクセス修飾子品質と衛生 ⭐
1例外のスロー、適切なエラー処理品質と衛生 ⭐
1console.log(開発時のみ)、本番環境では削除品質と衛生 ⭐
1readonly/const、インラインコメントを使わない、説明的な名前品質と衛生 ⭐
🟡 優先度 2:最新の TypeScript パターン
2配列メソッド、アロー関数、分割代入最新の TypeScript
2オプショナルチェーン、Null 合体演算子最新の TypeScript
2型ガード、ユーティリティ型最新の TypeScript
🟢 優先度 3:Cocos アーキテクチャ
3コンポーネントシステム、@property デコレーターコンポーネントシステム
3ライフサイクルメソッド(onLoad→start→update→onDestroy)コンポーネントシステム
3EventDispatcher、Node イベント、クリーンアップイベントパターン
3リソースの読み込み、プーリング、メモリ管理プレイアブル最適化
🔵 優先度 4:パフォーマンスとレビュー
4DrawCall のバッチ処理、スプライトアトラス、GPU スキニングプレイアブル最適化
4更新ループの最適化、アロケーションゼロパフォーマンス
4バンドルサイズの削減(目標:<5MB)サイズ最適化
4アーキテクチャレビュー(コンポーネント、ライフサイクル、イベント)アーキテクチャレビュー
4TypeScript の品質レビュー品質レビュー
4パフォーマンスレビュー(DrawCalls、アロケーション)パフォーマンスレビュー

🔴 重大:コード品質ルール(最初に確認!)

⚠️ 必須の品質基準

コードを書く前に、必ず以下を徹底してください:

  1. TypeScript の strict モードを有効にする - tsconfig.json で "strict": true にする
  2. ESLint の設定を使用する - @typescript-eslint のルールを有効にする
  3. アクセス修飾子を使用する - すべてのメンバーに public/private/protected を指定する
  4. エラー時は例外をスローする - サイレントな失敗や undefined を返すことは絶対に避ける
  5. console.log は開発時のみ使用する - 本番ビルドでは console 文をすべて削除する
  6. 変更されないフィールドには readonly を使用する - 再代入されないフィールドに指定する
  7. 定数には const を使用する - 定数には let ではなく const を使用する
  8. インラインコメントを使用しない - 説明的な名前を使用し、コード自体を自己説明的にする
  9. null/undefined を適切に処理する - オプショナルチェーンと Null 合体演算子を使用する
  10. 型安全性を確保する - 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

実行順序:

  1. onLoad() - コンポーネントの初期化、一度限りのセットアップ
  2. start() - すべてのコンポーネントの読み込み後に実行され、ほかのコンポーネントを参照できます
  3. onEnable() - コンポーネント/ノードが有効になったときに実行されます(複数回呼び出される場合があります)
  4. update(dt) - 毎フレーム実行されます(プレイアブルでは必要最小限にしてください)
  5. lateUpdate(dt) - すべての update() 呼び出し後に実行されます
  6. onDisable() - コンポーネント/ノードが無効になったときに実行されます
  7. 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 未満である

よくある間違いとその回避方法

❌ やってはいけないこと:
  1. TypeScript の strict モードを無視する → "strict": true を有効にします
  2. エラーを黙って処理する → エラー時に例外をスローします
  3. 本番環境に console.log を残す → 削除するか、CC_DEBUG で囲みます
  4. アクセス修飾子を省略する → public/private/protected を使用します
  5. any 型を使用する → 適切な型とインターフェースを定義します
  6. インラインコメントを追加する → 代わりに説明的な名前を使用します
  7. イベントのクリーンアップを省略する → 必ず onDisable/onDestroy で登録解除します
  8. update() 内でメモリを割り当てる → あらかじめ割り当てたオブジェクトを再利用します
  9. スプライトアトラスを忘れる → DrawCall のバッチ処理にアトラスを使用します
  10. update() で負荷の高いロジックを実行する → 負荷の高い処理を間引いて実行します
  11. null チェックを省略する → onLoad で必須の参照を検証します
  12. 変更可能な @property フィールドを使用する → 適切な場合は readonly を使用します
  13. 配列を手動でループ処理する → map/filter/reduce を使用します
  14. バンドルサイズを無視する → サイズを監視して最適化します(目標:5MB 未満)
✅ DO:
  1. TypeScript の strict モードを有効にする("strict": true)
  2. エラー時には例外をスローする(エラーを黙って処理しない)
  3. 開発時のみ console.log を使用する(本番環境では削除する)
  4. アクセス修飾子を使用する(public/private/protected)
  5. 適切な型を定義する(any を避ける)
  6. 説明的な名前を使用する(インラインコメントは使用しない)
  7. イベントを必ずクリーンアップする(onDisable/onDestroy)
  8. オブジェクトを事前に確保する(update() で再利用する)
  9. スプライトアトラスを使用する(DrawCall のバッチ処理)
  10. 負荷の高い処理をスロットリングする(毎フレーム実行しない)
  11. 必須の参照を検証する(null の場合は onLoad で例外をスローする)
  12. @property に readonly を使用する(適切な場合)
  13. 配列メソッドを使用する(map/filter/reduce)
  14. バンドルサイズを監視する(プレイアブルでは <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 言語標準
Cocos Creator フレームワーク
コードレビュー

概要

このスキルは、TheOne Studio のプレイアブル広告チーム向けに、Cocos Creator 開発の包括的な標準を提供します。

  • TypeScript の卓越性:厳格モード、最新のパターン、型安全性
  • Cocos のアーキテクチャ:コンポーネントのライフサイクル、イベントパターン、リソース管理
  • プレイアブル広告のパフォーマンス:DrawCall のバッチ処理、GPU スキニング、5MB 未満のバンドル
  • コード品質:品質、衛生、パフォーマンスに関するルールを徹底

上記のクイックリファレンスガイドを使用して、必要なパターンに移動してください。

コメント