【Cocos Creator】アタッチするだけ!RegenEffect (自動回復)の実装方法【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】RegenEffect(自動回復)の実装:アタッチするだけで一定間隔でHPが自動回復する汎用スクリプト

この記事では、ノードにアタッチするだけで「一定時間ごとにHPが自動回復」する汎用コンポーネント RegenEffect を実装します。

外部の GameManager やカスタム HealthManager に一切依存せず、このコンポーネント単体で「HPの管理」+「自動回復」まで完結するように設計します。HP の最大値・現在値・回復量・回復間隔などは、すべてインスペクタから調整可能にします。


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

機能要件の整理

  • このコンポーネントをアタッチしたノードは、「HP」を持つ。
  • HP は「最大値」「現在値」を持ち、0〜最大値の範囲にクランプされる。
  • 一定時間ごとに、現在の HP を少しずつ回復させる。
  • 自動回復は「オン/オフ」できる。
  • ゲーム開始時の HP 初期値を「最大HP」「任意の値」から選べる。
  • 外部のスクリプトに依存せず、このコンポーネント単体で完結する。
  • 将来的に「外部からダメージを与える」「現在HPを参照する」用途も想定し、
    シンプルなパブリックメソッドを用意する(任意で利用可能)。

本来の仕様では「親の HealthManager を呼び出す」とありますが、「他のカスタムスクリプトへの依存禁止」という条件があるため、
このコンポーネント自体が「簡易 HealthManager 兼 自動回復機能」として完結するようにします。

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

RegenEffect コンポーネントに定義するプロパティとその役割は以下の通りです。

  • maxHp: number
    • HP の最大値。
    • 0 より大きい値を推奨(例: 100)。
    • 現在 HP はこの値を超えないようにクランプされる。
  • initialHpRatio: number
    • ゲーム開始時の HP 初期値を「最大HP に対する割合(0〜1)」で指定。
    • 例: 1.0 なら最大HPから開始、0.5 なら半分のHPから開始。
    • 0〜1 の範囲外を指定した場合は、自動的にクランプされる。
  • enableRegen: boolean
    • 自動回復を有効にするかどうか。
    • チェックを外すと、自動回復は行われない(HP管理のみ)。
  • regenAmount: number
    • 1 回の回復で増加する HP の量。
    • 正の値を推奨(例: 1, 5, 10 など)。
    • 0 以下の場合は、警告ログを出しつつ実質的に回復しない。
  • regenInterval: number
    • 自動回復の発生間隔(秒)。
    • 例: 1.0 なら 1 秒ごとに regenAmount だけ回復。
    • 0 以下の場合は、自動回復が動作しないようにし、警告ログを出す。
  • stopRegenAtFullHp: boolean
    • HP が最大になったら自動回復タイマーを止めるかどうか。
    • true の場合、HP が最大値に達した時点で以後の回復処理を行わない。
    • false の場合、HP が最大でもチェックは続くが、実質的には何も起こらない。
  • logDebug: boolean
    • HPの変化やエラー・警告をログに出すかどうか。
    • 開発・デバッグ時のみ ON、本番では OFF を推奨。

内部的な挙動

  • onLoad
    • プロパティの値を安全な範囲にクランプ。
    • 現在HP(_currentHp)を maxHp * initialHpRatio から計算。
  • start
    • enableRegen が true かつ regenInterval > 0 かつ regenAmount > 0 の場合のみ、タイマーを開始。
    • Cocos Creator の this.schedule() を使って、一定間隔で doRegen() を呼び出す。
  • onDisable / onDestroy
    • スケジュールしている回復処理を停止して、メモリリークや予期しない挙動を防止。
  • doRegen
    • 現在HPが 0 以下の場合は、回復を行うかどうかをゲームデザインに応じて選べるが、ここでは「0 でも回復する(蘇生可能な仕様)」にしている。
    • 現在HPが最大HP以上なら、stopRegenAtFullHp に応じてスケジュールを停止する。
    • それ以外の場合、regenAmount を加算し、最大値を超えないようにクランプ。
  • パブリックメソッド
    • getCurrentHp(): number … 現在HPを取得。
    • getMaxHp(): number … 最大HPを取得。
    • applyDamage(amount: number): void … ダメージを与えて HP を減少させる。
    • heal(amount: number): void … 即時回復。
    • setRegenEnabled(enabled: boolean): void … 自動回復のオン/オフをコードから切り替え。

TypeScriptコードの実装


import { _decorator, Component, log, warn, error } from 'cc';
const { ccclass, property } = _decorator;

/**
 * RegenEffect
 * ノードにアタッチするだけで「HP管理 + 一定間隔の自動回復」を行う汎用コンポーネント。
 */
@ccclass('RegenEffect')
export class RegenEffect extends Component {

    @property({
        tooltip: '最大HP。現在HPはこの値を超えません。',
    })
    public maxHp: number = 100;

    @property({
        tooltip: '開始時のHP割合(0〜1)。1なら最大HPから開始、0.5なら半分から開始します。',
        slide: true,
        range: [0, 1, 0.01],
    })
    public initialHpRatio: number = 1.0;

    @property({
        tooltip: '自動回復を有効にするかどうか。',
    })
    public enableRegen: boolean = true;

    @property({
        tooltip: '1回の回復で増加するHP量。正の値を指定してください。',
    })
    public regenAmount: number = 1;

    @property({
        tooltip: '回復間隔(秒)。例: 1.0 で1秒ごとに回復します。',
    })
    public regenInterval: number = 1.0;

    @property({
        tooltip: 'HPが最大になったら自動回復を停止するかどうか。',
    })
    public stopRegenAtFullHp: boolean = true;

    @property({
        tooltip: 'HP変化や警告をログに出力するかどうか(デバッグ用)。',
    })
    public logDebug: boolean = false;

    // 現在のHP(インスペクタには表示しない)
    private _currentHp: number = 0;

    // スケジュール中かどうかのフラグ
    private _isRegenScheduled: boolean = false;

    onLoad() {
        // 防御的な値のクランプ
        if (this.maxHp <= 0) {
            warn('[RegenEffect] maxHp が 0 以下です。1 に補正します。');
            this.maxHp = 1;
        }

        if (this.initialHpRatio < 0) {
            this.initialHpRatio = 0;
        } else if (this.initialHpRatio > 1) {
            this.initialHpRatio = 1;
        }

        if (this.regenInterval < 0) {
            warn('[RegenEffect] regenInterval が負の値です。絶対値を使用します。');
            this.regenInterval = Math.abs(this.regenInterval);
        }

        if (this.regenAmount < 0) {
            warn('[RegenEffect] regenAmount が負の値です。正の値に反転します。');
            this.regenAmount = Math.abs(this.regenAmount);
        }

        // 初期HPを設定
        this._currentHp = this.maxHp * this.initialHpRatio;
        this._currentHp = this._clamp(this._currentHp, 0, this.maxHp);

        if (this.logDebug) {
            log(`[RegenEffect] onLoad: maxHp=${this.maxHp}, initialHp=${this._currentHp}`);
        }
    }

    start() {
        // 自動回復の開始条件をチェック
        this._updateRegenSchedule();
    }

    onDisable() {
        // ノードが無効化されたらスケジュールを停止
        this._unscheduleRegen();
    }

    onDestroy() {
        // ノード破棄時もスケジュールを停止
        this._unscheduleRegen();
    }

    /**
     * 内部用: 値を指定範囲にクランプする。
     */
    private _clamp(value: number, min: number, max: number): number {
        if (value < min) return min;
        if (value > max) return max;
        return value;
    }

    /**
     * 自動回復のスケジュール状態を、現在の設定に合わせて更新する。
     */
    private _updateRegenSchedule() {
        // いったん全て解除
        this._unscheduleRegen();

        if (!this.enableRegen) {
            if (this.logDebug) {
                log('[RegenEffect] 自動回復は無効化されています。');
            }
            return;
        }

        if (this.regenInterval <= 0) {
            warn('[RegenEffect] regenInterval が 0 以下のため、自動回復は実行されません。');
            return;
        }

        if (this.regenAmount <= 0) {
            warn('[RegenEffect] regenAmount が 0 以下のため、自動回復は実行されません。');
            return;
        }

        // すでに最大HPなら、必要に応じて停止
        if (this._currentHp >= this.maxHp && this.stopRegenAtFullHp) {
            if (this.logDebug) {
                log('[RegenEffect] すでに最大HPのため、自動回復は開始しません。');
            }
            return;
        }

        // 一定間隔で doRegen を呼び出す
        this.schedule(this.doRegen, this.regenInterval);
        this._isRegenScheduled = true;

        if (this.logDebug) {
            log(`[RegenEffect] 自動回復を開始: interval=${this.regenInterval}, amount=${this.regenAmount}`);
        }
    }

    /**
     * 自動回復のスケジュールを解除する。
     */
    private _unscheduleRegen() {
        if (this._isRegenScheduled) {
            this.unschedule(this.doRegen);
            this._isRegenScheduled = false;
            if (this.logDebug) {
                log('[RegenEffect] 自動回復を停止しました。');
            }
        }
    }

    /**
     * スケジュールから呼ばれる実際の回復処理。
     */
    private doRegen() {
        // すでに最大HPの場合
        if (this._currentHp >= this.maxHp) {
            this._currentHp = this.maxHp;

            if (this.stopRegenAtFullHp) {
                if (this.logDebug) {
                    log('[RegenEffect] HPが最大に達したため、自動回復を停止します。');
                }
                this._unscheduleRegen();
            }
            return;
        }

        const before = this._currentHp;
        this._currentHp += this.regenAmount;
        this._currentHp = this._clamp(this._currentHp, 0, this.maxHp);

        if (this.logDebug) {
            log(`[RegenEffect] 自動回復: ${before} → ${this._currentHp}`);
        }
    }

    // ====== パブリックAPI(任意で利用可能) ======

    /**
     * 現在のHPを取得します。
     */
    public getCurrentHp(): number {
        return this._currentHp;
    }

    /**
     * 最大HPを取得します。
     */
    public getMaxHp(): number {
        return this.maxHp;
    }

    /**
     * ダメージを与えてHPを減少させます。
     * @param amount 減少させるHP量(正の値を想定)
     */
    public applyDamage(amount: number): void {
        if (amount < 0) {
            warn('[RegenEffect] applyDamage に負の値が渡されました。絶対値を使用します。');
            amount = Math.abs(amount);
        }

        const before = this._currentHp;
        this._currentHp -= amount;
        this._currentHp = this._clamp(this._currentHp, 0, this.maxHp);

        if (this.logDebug) {
            log(`[RegenEffect] ダメージ: ${before} → ${this._currentHp} (amount=${amount})`);
        }

        // HPが減った結果、自動回復が必要であれば再スケジュール
        if (this.enableRegen && !this._isRegenScheduled) {
            this._updateRegenSchedule();
        }
    }

    /**
     * 即時回復を行います。
     * @param amount 増加させるHP量(正の値を想定)
     */
    public heal(amount: number): void {
        if (amount < 0) {
            warn('[RegenEffect] heal に負の値が渡されました。絶対値を使用します。');
            amount = Math.abs(amount);
        }

        const before = this._currentHp;
        this._currentHp += amount;
        this._currentHp = this._clamp(this._currentHp, 0, this.maxHp);

        if (this.logDebug) {
            log(`[RegenEffect] 即時回復: ${before} → ${this._currentHp} (amount=${amount})`);
        }

        // HPが最大に達した場合、必要に応じて自動回復を停止
        if (this._currentHp >= this.maxHp && this.stopRegenAtFullHp) {
            this._unscheduleRegen();
        }
    }

    /**
     * 自動回復の有効/無効を切り替えます。
     */
    public setRegenEnabled(enabled: boolean): void {
        this.enableRegen = enabled;
        this._updateRegenSchedule();
    }
}

コードの要点解説

  • onLoad
    • maxHp, initialHpRatio, regenInterval, regenAmount を安全な値に補正。
    • _currentHpmaxHp * initialHpRatio から計算し、0〜maxHp にクランプ。
  • start
    • _updateRegenSchedule() を呼び出し、プロパティの状態に応じて自動回復のスケジュールを開始。
    • 条件を満たさない場合(interval や amount が 0 など)は、警告を出して何もしない。
  • _updateRegenSchedule / _unscheduleRegen
    • 現在の設定に合わせて schedule(this.doRegen, this.regenInterval) を開始・停止。
    • 二重スケジュールを防ぐため、フラグ _isRegenScheduled で管理。
  • doRegen
    • HP が最大なら、stopRegenAtFullHp に応じてスケジュールを停止。
    • それ以外なら regenAmount 分だけ HP を増加させ、最大値を超えないようにクランプ。
    • logDebug が true のときのみログ出力。
  • applyDamage / heal
    • 外部から任意にダメージや即時回復を行いたいときに利用可能。
    • ダメージ後に自動回復が有効で、まだスケジュールされていなければ再スケジュール。

使用手順と動作確認

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

  1. エディタ左下の Assets パネルで、スクリプトを置きたいフォルダ(例: assets/scripts)を選択します。
  2. フォルダ内で右クリック → CreateTypeScript を選択します。
  3. 作成されたファイル名を RegenEffect.ts に変更します。
  4. RegenEffect.ts をダブルクリックして開き、内容をすべて削除して、前述の TypeScript コードを貼り付けて保存します。

2. テスト用ノードの作成

  1. 上部メニューから Scene を開き、テスト用のシーン(例: TestScene.scene)を開きます。
  2. 中央上部の Hierarchy パネルで、空の場所を右クリック → Create3D ObjectNode など、何でもよいのでノードを 1 つ作成します。
    • 2D ゲームの場合は CreateUISprite などでも構いません。
  3. 作成したノードの名前を分かりやすく PlayerTestRegen などに変更します。

3. RegenEffect コンポーネントをアタッチ

  1. Hierarchy で先ほど作成したノードを選択します。
  2. 右側の Inspector パネルの下部にある Add Component ボタンをクリックします。
  3. メニューから CustomRegenEffect を選択します。
    • もし一覧に見つからない場合は、スクリプトのコンパイル中か、クラス名とファイル名の綴りを確認してください。

4. プロパティの設定例

Inspector の RegenEffect セクションに以下のようなプロパティが表示されます。

  • Max Hp(maxHp)
  • Initial Hp Ratio(initialHpRatio)
  • Enable Regen(enableRegen)
  • Regen Amount(regenAmount)
  • Regen Interval(regenInterval)
  • Stop Regen At Full Hp(stopRegenAtFullHp)
  • Log Debug(logDebug)

まずは以下のように設定してみてください。

  • Max Hp: 100
  • Initial Hp Ratio: 0.5(HP50から開始)
  • Enable Regen: チェック ON
  • Regen Amount: 5
  • Regen Interval: 1.0(1秒ごとに5回復)
  • Stop Regen At Full Hp: チェック ON
  • Log Debug: チェック ON(動作確認用にログを出す)

5. 動作確認(プレビュー)

  1. エディタ右上の Play ボタン(▶)をクリックして、ゲームをプレビューします。
  2. プレビューウィンドウが開いたら、Cocos Creator の Console タブを確認します。
  3. 毎秒ごとに、以下のようなログが流れていれば成功です。
    • [RegenEffect] 自動回復: 50 → 55
    • [RegenEffect] 自動回復: 55 → 60
    • [RegenEffect] HPが最大に達したため、自動回復を停止します。

6. ダメージとの連携を試す(任意)

よりゲームらしい挙動を確認するために、一定時間ごとにダメージを与える簡単なテストスクリプトを追加してもよいでしょう。

例として、同じノードに以下のような簡易テストスクリプトを追加することで、
「ダメージ → 自動回復 → ダメージ → …」の挙動を確認できます。


import { _decorator, Component } from 'cc';
import { RegenEffect } from './RegenEffect';
const { ccclass } = _decorator;

@ccclass('RegenTester')
export class RegenTester extends Component {
    private _timer = 0;

    update(deltaTime: number) {
        this._timer += deltaTime;
        if (this._timer >= 3.0) { // 3秒ごとにダメージ
            this._timer = 0;
            const regen = this.getComponent(RegenEffect);
            if (regen) {
                regen.applyDamage(20);
            }
        }
    }
}

この RegenTester はあくまで「テスト用」の例なので、本番用のゲームロジックには依存させずRegenEffect 単体で機能する構成を維持できます。


まとめ

  • RegenEffect は、ノードにアタッチするだけで「HP管理+一定間隔の自動回復」を実現する汎用コンポーネントです。
  • 外部の GameManager や HealthManager に依存せず、このスクリプト単体で完結するため、小さなプロトタイプから本格的なゲームまで簡単に組み込めます。
  • インスペクタから
    • 最大HP(maxHp)
    • 開始時のHP割合(initialHpRatio)
    • 回復量(regenAmount)
    • 回復間隔(regenInterval)
    • 最大HP到達時の挙動(stopRegenAtFullHp)

    を調整できるため、キャラクターごとに異なる自動回復特性を簡単に設定できます。

  • パブリックメソッド(applyDamage, heal, setRegenEnabled など)を使えば、
    既存の攻撃判定やUI更新ロジックとも柔軟に連携できます。

このように、「状態管理+時間経過処理」を1つの独立コンポーネントにまとめることで、
シーン構成やゲームロジックをシンプルに保ちつつ、再利用性の高い設計が可能になります。
別のステータス(MP、スタミナ、シールド耐久など)にも、同じパターンで簡単に応用できます。

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をコピーしました!