【Cocos Creator】アタッチするだけ!DebugLabel (デバッグ表示)の実装方法【TypeScript】

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

【Cocos Creator 3.8】DebugLabel の実装:アタッチするだけで親ノードの状態を頭上にテキスト表示する汎用スクリプト

このコンポーネントは、親ノードの「速度(Velocity)」「状態(State)」など、任意の情報を頭上に表示するためのデバッグ用ラベルを簡単に追加できるようにするものです。
DebugLabel をアタッチするだけで、シーン内の任意のノードの上にテキストが表示され、ゲーム中の挙動確認やチューニングがしやすくなります。

コンポーネントの設計方針

今回の要件:

  • このコンポーネントをアタッチしたノードの「親ノード」の情報を頭上に表示する。
  • 表示内容は「Velocity」「State」など、インスペクタから自由に設定できる文字列をベースに構成できる。
  • 他のカスタムスクリプト(GameManager など)には一切依存しない。
  • ラベル表示に必要な Label コンポーネントが無い場合は、自動で追加を試み、失敗時はログを出す。
  • ワールド座標 or ローカル座標での追従・オフセット位置・更新頻度などをインスペクタから調整可能にする。
  • 「Velocity」「State」などの値は、基本的にはユーザーがインスペクタで入力した文字列を表示する形にし、将来的にコードから更新しやすい構造にしておく。

「完全な独立性」を守るため、他スクリプトから直接参照して値を取るのではなく、DebugLabel 自体が持つテキストプロパティを使って表示します。
(必要に応じて、外部スクリプトから getComponent(DebugLabel) で参照し、プロパティを書き換えれば、動的に Velocity や State を表示できます。)

インスペクタで設定可能なプロパティ設計

DebugLabel コンポーネントで用意するプロパティと役割は次の通りです。

  • enableDebugLabel (boolean)
    • このコンポーネントによる表示を有効/無効にするトグル。
    • デバッグ時のみ ON、本番ビルドでは OFF にするなどの用途。
  • baseText (string)
    • ラベルのベースとなるテキスト。
    • 例: "Player" / "Enemy" / "State:" など。
  • showVelocity (boolean)
    • Velocity 情報を表示するかどうか。
    • ON の場合、velocityText の内容がラベルに付加される。
  • velocityText (string)
    • 現在の Velocity を表す文字列。
    • 外部スクリプトから動的に書き換えることを想定。
    • 例: "(1.2, -3.4)" など。
  • showState (boolean)
    • State 情報を表示するかどうか。
    • ON の場合、stateText の内容がラベルに付加される。
  • stateText (string)
    • 現在の State を表す文字列。
    • 例: "Idle" / "Run" / "Jump" など。
  • customSuffix (string)
    • 任意の追加情報を最後に付け足すための文字列。
    • 例: "HP: 10" / "DebugMode" など。
  • useWorldSpace (boolean)
    • ラベルの位置を「ワールド座標」で制御するか、「ローカル座標」で制御するかを切り替える。
    • ON の場合: 親ノードのワールド座標に対するオフセットとして動く。
    • OFF の場合: 親ノードのローカル座標に対するオフセットとして動く。
  • offsetY (number)
    • 親ノードの「頭上」に表示するための Y 方向オフセット。
    • 単位はローカル or ワールド座標系の単位(useWorldSpace に依存)。
    • 例: 50 〜 100 程度にすると分かりやすい。
  • offsetX (number)
    • 左右方向の微調整用オフセット。
  • updateInterval (number)
    • テキストを更新する間隔(秒)。
    • 0 以下なら毎フレーム更新、0.1 なら 0.1 秒ごとに更新。
    • 性能と見やすさのバランスを取るためのパラメータ。
  • autoHideIfNoParent (boolean)
    • 親ノードが存在しない場合にラベルを非表示にするかどうか。
    • ON の場合、親が無いときはノードを active = false にする。
  • fontSize (number)
    • ラベルのフォントサイズ。
  • textColor (Color)
    • ラベルの文字色。
  • outlineColor (Color)
    • アウトラインの色。
    • アウトラインを使わない場合は無視してもよいが、視認性向上のために自動で追加を試みる。
  • useOutline (boolean)
    • ラベルに LabelOutline を付与するかどうか。

これらのプロパティにより、DebugLabel 単体で柔軟に表示内容と見た目を調整できるようにします。

TypeScriptコードの実装


import { _decorator, Component, Node, Label, Color, LabelOutline, Vec3 } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('DebugLabel')
export class DebugLabel extends Component {

    @property({
        tooltip: 'デバッグラベルの表示を有効にするかどうか。\nOFFにするとテキスト更新と位置追従を停止します。'
    })
    public enableDebugLabel: boolean = true;

    @property({
        tooltip: 'ラベルのベースとなるテキスト。\n例: "Player", "Enemy", "State" など。'
    })
    public baseText: string = '';

    @property({
        tooltip: 'Velocity 情報を表示するかどうか。'
    })
    public showVelocity: boolean = false;

    @property({
        tooltip: 'Velocity を表す文字列。\n外部スクリプトから動的に書き換えることを想定しています。'
    })
    public velocityText: string = '';

    @property({
        tooltip: 'State 情報を表示するかどうか。'
    })
    public showState: boolean = false;

    @property({
        tooltip: 'State を表す文字列。\n例: "Idle", "Run", "Jump" など。'
    })
    public stateText: string = '';

    @property({
        tooltip: '任意の追加情報を末尾に付けるための文字列。\n例: "HP: 10", "DebugMode" など。'
    })
    public customSuffix: string = '';

    @property({
        tooltip: 'ラベル位置をワールド座標で制御するかどうか。\nON: 親ノードのワールド座標 + オフセット\nOFF: 親ノードのローカル座標 + オフセット'
    })
    public useWorldSpace: boolean = true;

    @property({
        tooltip: '親ノードからのX方向オフセット。'
    })
    public offsetX: number = 0;

    @property({
        tooltip: '親ノードからのY方向オフセット(頭上に表示するための高さ)。'
    })
    public offsetY: number = 80;

    @property({
        tooltip: 'テキストを更新する間隔(秒)。\n0以下なら毎フレーム更新します。'
    })
    public updateInterval: number = 0.1;

    @property({
        tooltip: '親ノードが存在しない場合に自動で非表示にするかどうか。'
    })
    public autoHideIfNoParent: boolean = true;

    @property({
        tooltip: 'ラベルのフォントサイズ。'
    })
    public fontSize: number = 20;

    @property({
        tooltip: 'ラベルの文字色。'
    })
    public textColor: Color = new Color(255, 255, 255, 255);

    @property({
        tooltip: 'アウトラインを使用するかどうか。\nONの場合、LabelOutlineを自動で追加します。'
    })
    public useOutline: boolean = true;

    @property({
        tooltip: 'アウトラインの色。'
    })
    public outlineColor: Color = new Color(0, 0, 0, 255);

    private _label: Label | null = null;
    private _outline: LabelOutline | null = null;
    private _timeAccumulator: number = 0;

    onLoad() {
        // Label コンポーネントを取得 or 追加
        this._label = this.getComponent(Label);
        if (!this._label) {
            this._label = this.node.addComponent(Label);
            console.warn('[DebugLabel] Label コンポーネントが見つからなかったため、自動で追加しました。');
        }

        if (!this._label) {
            console.error('[DebugLabel] Label コンポーネントを取得・追加できませんでした。このノードに Label を追加してください。');
            return;
        }

        // Label の初期設定
        this._label.string = '';
        this._label.fontSize = this.fontSize;
        this._label.color = this.textColor;
        this._label.enableWrapText = false;

        // アウトラインの設定
        if (this.useOutline) {
            this._outline = this.getComponent(LabelOutline);
            if (!this._outline) {
                this._outline = this.node.addComponent(LabelOutline);
            }
            if (this._outline) {
                this._outline.color = this.outlineColor;
            } else {
                console.warn('[DebugLabel] LabelOutline コンポーネントを追加できませんでした。アウトラインは無効になります。');
            }
        }

        // 親がいない場合の扱い
        if (!this.node.parent) {
            console.warn('[DebugLabel] 親ノードが存在しません。頭上表示の対象がないため、位置追従は行われません。');
            if (this.autoHideIfNoParent) {
                this.node.active = false;
            }
        }
    }

    start() {
        // 初期表示を一度更新
        this._updateLabelText();
        this._updateLabelPosition(0);
    }

    update(deltaTime: number) {
        if (!this.enableDebugLabel) {
            // 無効化されている場合は何もしない
            return;
        }

        // 親ノードの存在確認
        const parent = this.node.parent;
        if (!parent) {
            if (this.autoHideIfNoParent) {
                this.node.active = false;
            }
            return;
        } else {
            this.node.active = true;
        }

        // テキスト更新タイミングの制御
        if (this.updateInterval > 0) {
            this._timeAccumulator += deltaTime;
            if (this._timeAccumulator >= this.updateInterval) {
                this._timeAccumulator = 0;
                this._updateLabelText();
            }
        } else {
            // updateInterval が 0 以下なら毎フレーム更新
            this._updateLabelText();
        }

        // 位置追従
        this._updateLabelPosition(deltaTime);
    }

    /**
     * ラベルに表示するテキストを組み立てて反映する。
     */
    private _updateLabelText() {
        if (!this._label) {
            return;
        }

        const parts: string[] = [];

        if (this.baseText) {
            parts.push(this.baseText);
        }

        if (this.showVelocity && this.velocityText) {
            parts.push(`Vel: ${this.velocityText}`);
        }

        if (this.showState && this.stateText) {
            parts.push(`State: ${this.stateText}`);
        }

        if (this.customSuffix) {
            parts.push(this.customSuffix);
        }

        // パーツを " | " で連結して見やすくする
        this._label.string = parts.join(' | ');
    }

    /**
     * ラベルノードの位置を親ノードの頭上に追従させる。
     */
    private _updateLabelPosition(deltaTime: number) {
        const parent = this.node.parent;
        if (!parent) {
            return;
        }

        const offset = new Vec3(this.offsetX, this.offsetY, 0);

        if (this.useWorldSpace) {
            // 親のワールド座標 + オフセット
            const parentWorldPos = parent.worldPosition;
            const targetWorldPos = new Vec3(
                parentWorldPos.x + offset.x,
                parentWorldPos.y + offset.y,
                parentWorldPos.z
            );
            this.node.setWorldPosition(targetWorldPos);
        } else {
            // 親のローカル座標 + オフセット(= 親のローカル空間内での相対位置)
            const parentPos = parent.position;
            const targetLocalPos = new Vec3(
                parentPos.x + offset.x,
                parentPos.y + offset.y,
                parentPos.z
            );
            this.node.setPosition(targetLocalPos);
        }
    }

    /**
     * インスペクタから値を変更したときに即座に反映したい項目をここで処理。
     * (エディタ上でのプレビュー用)
     */
    protected onValidate() {
        if (this._label) {
            this._label.fontSize = this.fontSize;
            this._label.color = this.textColor;
        }
        if (this.useOutline) {
            if (!this._outline && this._label) {
                this._outline = this.node.addComponent(LabelOutline);
            }
            if (this._outline) {
                this._outline.color = this.outlineColor;
            }
        } else {
            if (this._outline) {
                this._outline.destroy();
                this._outline = null;
            }
        }
        this._updateLabelText();
    }
}

コードのポイント解説

  • onLoad
    • Label コンポーネントの取得を試み、無ければ自動追加します。
    • アウトライン使用設定に応じて LabelOutline を取得 or 追加します。
    • 親ノードが無い場合の挙動(警告ログ、必要なら非表示)をここで処理します。
  • start
    • 最初の 1 回だけテキストと位置を更新して初期状態を整えます。
  • update
    • enableDebugLabel が OFF のときは何もしません。
    • 親ノードが存在するかを毎フレーム確認し、存在しなければ必要に応じて非表示にします。
    • updateInterval に基づき、テキスト更新の頻度を制御します。
    • 毎フレーム、親ノードの「頭上」にラベルを移動させます(ワールド or ローカル座標)。
  • _updateLabelText
    • baseText / velocityText / stateText / customSuffix を組み立てて 1 行の文字列にします。
    • 表示する情報ごとに showVelocity, showState で ON/OFF 切り替えが可能です。
  • _updateLabelPosition
    • 親ノードの位置 + オフセットでラベルの位置を決めます。
    • useWorldSpace により、ワールド座標かローカル座標かを切り替えます。
  • onValidate
    • エディタ上でプロパティ変更時にフォントサイズや色を即時反映し、テキストも再組み立てします。
    • エディタ内プレビューをしやすくするための補助メソッドです。

使用手順と動作確認

Cocos Creator 3.8.7 で DebugLabel コンポーネントを使う手順を、エディタ操作ベースで説明します。

1. スクリプトファイルの作成

  1. Assets パネルで任意のフォルダ(例: assets/scripts)を右クリックします。
  2. Create > TypeScript を選択します。
  3. ファイル名を DebugLabel.ts に変更します。
  4. 作成された DebugLabel.ts をダブルクリックし、エディタ(VS Code など)で開き、上記の TypeScript コードを全て貼り付けて保存します。

2. テスト用ノード(親)とラベルノードの作成

  1. Hierarchy パネルで右クリックし、Create > 2D Object > Sprite を選択して、テスト用の親ノードを作成します。
    • 名前は例として Player とします。
  2. 同様に、ラベル表示用のノードを作成します。
    • Hierarchy で Player ノードを右クリックし、Create > 2D Object > Node を選択します。
    • このノードの名前を PlayerDebugLabel など分かりやすい名前に変更します。
    • このノードは Player の子ノードになっていることを確認してください。

※ ラベルノードは親の子にしておくことで、シーン構造が分かりやすくなり、ローカル座標使用時も扱いやすくなります。

3. DebugLabel コンポーネントのアタッチ

  1. Hierarchy で PlayerDebugLabel ノードを選択します。
  2. Inspector パネル下部の Add Component ボタンをクリックします。
  3. Custom カテゴリから DebugLabel を選択してアタッチします。
  4. この時点で、スクリプトは自動的に Label コンポーネントを追加しようとします。Inspector の PlayerDebugLabelLabel が存在するか確認してください。

4. プロパティの設定例

Inspector 上で DebugLabel のプロパティを次のように設定してみます。

  • Enable Debug Label: チェック ON
  • Base Text: Player
  • Show Velocity: チェック ON
  • Velocity Text: (0, 0)
  • Show State: チェック ON
  • State Text: Idle
  • Custom Suffix: HP:10
  • Use World Space: チェック ON(2Dカメラ固定なら ON が扱いやすいです)
  • Offset X: 0
  • Offset Y: 80(Sprite の大きさに応じて 50〜100 で調整)
  • Update Interval: 0.1(0.1秒ごとにテキスト更新)
  • Auto Hide If No Parent: チェック ON
  • Font Size: 20
  • Use Outline: チェック ON
  • Outline Color: 黒(デフォルトのままでOK)

この状態でゲームを再生すると、Player ノードの頭上に

Player | Vel: (0, 0) | State: Idle | HP:10

というテキストが表示され、Player を移動させるとラベルも追従して動きます。

5. 値を動的に変化させてみる(任意)

DebugLabel 自体は他スクリプトに依存しませんが、外部スクリプトから velocityTextstateText を更新することで、実際の「Velocity」や「State」を表示できます。
ここでは「使い方の例」として、親ノードに別スクリプトを付けて連携する方法を簡単に示します。

例: Player の状態を更新するスクリプト(あくまで任意・参考)


// 参考用:DebugLabel を更新するだけの簡易スクリプト例(外部依存ではなく「利用例」です)
import { _decorator, Component, Vec3 } from 'cc';
import { DebugLabel } from './DebugLabel';
const { ccclass } = _decorator;

@ccclass('PlayerDebugExample')
export class PlayerDebugExample extends Component {
    private _velocity: Vec3 = new Vec3(0, 0, 0);
    private _state: string = 'Idle';
    private _debugLabel: DebugLabel | null = null;

    start() {
        // 子ノードから DebugLabel を探す
        const child = this.node.getChildByName('PlayerDebugLabel');
        if (child) {
            this._debugLabel = child.getComponent(DebugLabel);
        }
    }

    update(deltaTime: number) {
        // ここでは例としてダミーの値を更新
        this._velocity.x += deltaTime;
        this._state = this._velocity.x > 1 ? 'Run' : 'Idle';

        if (this._debugLabel) {
            this._debugLabel.velocityText = `(${this._velocity.x.toFixed(2)}, ${this._velocity.y.toFixed(2)})`;
            this._debugLabel.stateText = this._state;
        }
    }
}

※ 上記は DebugLabel コンポーネントの外部利用例であり、DebugLabel 自体はこのスクリプトに依存しません。

まとめ

今回作成した DebugLabel コンポーネントは、

  • 任意のノードの「頭上」にテキストを表示できる。
  • 表示内容(Velocity / State / 任意の文字列)をインスペクタから簡単に設定できる。
  • ラベルの位置・更新頻度・見た目(フォントサイズ・色・アウトライン)を柔軟に調整できる。
  • 他のカスタムスクリプトに依存せず、このスクリプト単体で完結している。

という特徴を持った、汎用性の高いデバッグ用コンポーネントです。

ゲーム開発中は「このキャラの現在速度はいくつか?」「今どのステートにいるか?」を素早く確認したい場面が頻繁にあります。DebugLabel を用意しておけば、

  • 任意のノードに子ノード + DebugLabel を追加するだけで可視化できる。
  • 必要な時だけ enableDebugLabel を ON にするだけでデバッグ表示を有効化できる。
  • コード側から文字列プロパティを更新するだけで、リアルタイムな状態監視が可能になる。

といった形で、開発中の検証効率が大幅に向上します。
まずはシンプルな Velocity / State 表示から始め、必要に応じて customSuffix に HP やフラグ情報などを追加して、自分のプロジェクトに合わせたデバッグ HUD として活用してみてください。

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

URLをコピーしました!