【Cocos Creator】アタッチするだけ!Invincibility (無敵時間)の実装方法【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】Invincibility(無敵時間)の実装:アタッチするだけで「ダメージ後の一定時間、当たり判定を無効化しつつノードを点滅させる」汎用スクリプト

このコンポーネントは、被ダメージ後の「無敵時間」を簡単に実装するためのものです。
任意のノードにアタッチしておけば、スクリプトから triggerInvincibility() を呼ぶだけで、

  • 一定時間 Hurtbox(当たり判定)をオフにする
  • 同時にノード(または任意ノード)を点滅させる

といった処理を一括で行えます。
外部の GameManager などには一切依存せず、インスペクタ上の設定だけで完結する汎用コンポーネントとして設計します。


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

1. 機能要件の整理

  • 「ダメージを受けた瞬間」に無敵時間を開始できるようにする
    • 外部から triggerInvincibility() を呼び出すだけで開始できる
    • 同じ無敵時間中に再度呼ばれた場合の挙動は「再スタート(時間をリセット)」とする
  • 無敵時間中の挙動
    • Hurtbox(当たり判定)用コンポーネントを一時的に無効化する
    • 同時に、見た目としてノードを点滅させる(点滅ON/OFFの間隔は調整可能)
    • 無敵時間終了後は、Hurtboxを元の状態に戻し、点滅も停止させる
  • 完全な独立性
    • 他のカスタムスクリプトに依存しない
    • Hurtbox は「任意のコンポーネントを無効化する」という汎用仕様にする
      • 例: Collider2D, BoxCollider3D, RigidBody2D など何でも可

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

このコンポーネントでは、以下のようなプロパティを用意します。

  • duration(無敵時間の長さ)
    • 型: number
    • 単位: 秒
    • 説明: 無敵時間がどれくらい続くか。例: 1.0 なら 1 秒間
  • flashInterval(点滅の間隔)
    • 型: number
    • 単位: 秒
    • 説明: 何秒ごとに表示/非表示を切り替えるか。例: 0.1 なら 0.1 秒ごとに点滅
  • targetNode(点滅させるノード)
    • 型: Node
    • 説明: 点滅させたいノード。未指定(null)の場合は、このコンポーネントがアタッチされたノード自身を点滅させる
  • hurtboxNode(無効化する Hurtbox ノード)
    • 型: Node
    • 説明: 無敵時間中に当たり判定を無効化したいノード。未指定の場合は、このコンポーネントがアタッチされたノードを対象とする
  • hurtboxComponentType(無効化対象コンポーネントのクラス名文字列)
    • 型: string
    • 説明: 無効化したいコンポーネントのクラス名(例: Collider2D, BoxCollider 等)を文字列で指定
    • 備考: ここは文字列で受け取り、実行時に getComponent で探し、enabled を操作する
  • logWarnings(警告ログを出すか)
    • 型: boolean
    • 説明: Hurtbox ノードやコンポーネントが見つからない場合に警告ログを出すかどうか
  • startInactive(開始時は無敵状態か)
    • 型: boolean
    • 説明: true の場合、ゲーム開始時から無敵状態で開始する(例: スポーン直後の無敵)

Hurtbox の実体を「特定のクラスに固定」してしまうと、2D/3D やゲームごとの構成の違いに対応しにくくなります。
そこで本記事では「任意ノードに付いている任意コンポーネントを、文字列で指定して無効化する」という汎用アプローチを採用します。


TypeScriptコードの実装


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

/**
 * Invincibility
 * 
 * 任意のタイミングで triggerInvincibility() を呼ぶことで、
 * - 指定ノードの任意コンポーネント(Hurtbox)を一時的に無効化
 * - 指定ノードを点滅表示
 * を行う汎用コンポーネント。
 */
@ccclass('Invincibility')
export class Invincibility extends Component {

    @property({
        tooltip: '無敵時間の長さ(秒)。例: 1.0 なら 1秒間無敵になります。',
        min: 0.01,
    })
    public duration: number = 1.0;

    @property({
        tooltip: '点滅の間隔(秒)。小さいほど高速に点滅します。',
        min: 0.01,
    })
    public flashInterval: number = 0.1;

    @property({
        tooltip: '点滅させる対象ノード。未指定の場合、このコンポーネントが付いているノードを点滅させます。',
    })
    public targetNode: Node | null = null;

    @property({
        tooltip: '無敵時間中に当たり判定を無効化したいノード(Hurtboxノード)。未指定の場合、このコンポーネントが付いているノードが対象になります。',
    })
    public hurtboxNode: Node | null = null;

    @property({
        tooltip: '無効化したいコンポーネントのクラス名(例: Collider2D, BoxCollider, BoxCollider2D, BoxCollider3D など)。\n空文字の場合は、Hurtboxノード全体を active=false にします。',
    })
    public hurtboxComponentType: string = '';

    @property({
        tooltip: 'Hurtboxノードやコンポーネントが見つからない場合に警告ログを出すかどうか。',
    })
    public logWarnings: boolean = true;

    @property({
        tooltip: 'true の場合、開始時から無敵状態でスタートします(スポーン直後の無敵などに使用)。',
    })
    public startInactive: boolean = false;

    /** 現在無敵状態かどうか */
    private _isInvincible: boolean = false;

    /** 無敵状態の終了時刻(game.totalTime を使用) */
    private _invincibleEndTime: number = 0;

    /** 次に点滅トグルを行う時刻 */
    private _nextFlashTime: number = 0;

    /** 無敵開始前の Hurtbox コンポーネント enabled 状態 */
    private _originalHurtboxEnabled: boolean | null = null;

    /** 無敵開始前の Hurtbox ノード active 状態(コンポーネントタイプ未指定時に使用) */
    private _originalHurtboxActive: boolean | null = null;

    /** Hurtbox として扱うコンポーネント参照(任意型) */
    private _hurtboxComponent: Component | null = null;

    /** 点滅対象ノードの初期 active 状態 */
    private _originalTargetActive: boolean = true;

    onLoad() {
        // targetNode / hurtboxNode が未設定の場合は自分自身を使用
        if (!this.targetNode) {
            this.targetNode = this.node;
        }
        if (!this.hurtboxNode) {
            this.hurtboxNode = this.node;
        }

        // 点滅対象ノードの初期 active 状態を記録
        if (this.targetNode) {
            this._originalTargetActive = this.targetNode.active;
        }

        // Hurtbox コンポーネントを取得(あれば)
        this._resolveHurtboxComponent();

        // startInactive が true の場合は開始時から無敵にする
        if (this.startInactive) {
            this.triggerInvincibility();
        }
    }

    /**
     * 毎フレーム、無敵時間と点滅処理を管理。
     */
    update(deltaTime: number) {
        if (!this._isInvincible) {
            return;
        }

        const now = game.totalTime / 1000; // totalTime はミリ秒なので秒に変換

        // 無敵時間終了チェック
        if (now >= this._invincibleEndTime) {
            this._endInvincibility();
            return;
        }

        // 点滅処理
        if (this.targetNode && now >= this._nextFlashTime) {
            this.targetNode.active = !this.targetNode.active;
            this._nextFlashTime = now + this.flashInterval;
        }
    }

    /**
     * 外部から呼び出して無敵時間を開始する。
     * すでに無敵中であっても、時間をリセットして再スタートします。
     */
    public triggerInvincibility(durationOverride?: number) {
        const now = game.totalTime / 1000;

        const duration = durationOverride > 0 ? durationOverride : this.duration;
        if (duration <= 0) {
            if (this.logWarnings) {
                warn('[Invincibility] duration が 0 以下のため、無敵を開始しません。');
            }
            return;
        }

        // Hurtbox コンポーネント参照を再解決(動的に付け外しされている可能性に備える)
        this._resolveHurtboxComponent();

        // 初回開始時のみ元状態を記録
        if (!this._isInvincible) {
            this._storeOriginalStates();
        }

        this._isInvincible = true;
        this._invincibleEndTime = now + duration;
        this._nextFlashTime = now + this.flashInterval;

        // Hurtbox を無効化
        this._setHurtboxEnabled(false);

        // 点滅対象ノードを強制的に表示状態から開始
        if (this.targetNode) {
            this.targetNode.active = true;
        }

        if (this.logWarnings) {
            log(`[Invincibility] 無敵開始: duration=${duration.toFixed(2)} 秒`);
        }
    }

    /**
     * 現在無敵中かどうかを返す。
     */
    public get isInvincible(): boolean {
        return this._isInvincible;
    }

    /**
     * Hurtbox コンポーネントの参照を取得する。
     * hurtboxComponentType が空文字の場合は、コンポーネントは使わずノード全体を active で制御する。
     */
    private _resolveHurtboxComponent() {
        this._hurtboxComponent = null;

        if (!this.hurtboxNode) {
            if (this.logWarnings) {
                warn('[Invincibility] hurtboxNode が設定されていません。');
            }
            return;
        }

        if (!this.hurtboxComponentType || this.hurtboxComponentType.trim().length === 0) {
            // コンポーネントタイプ未指定: ノード全体を active で制御するため、ここでは何もしない
            return;
        }

        // クラス名文字列でコンポーネントを探す
        // Cocos Creator では、クラス名文字列で getComponent を呼ぶことができます。
        const comp = this.hurtboxNode.getComponent(this.hurtboxComponentType) as unknown as Component | null;
        if (!comp) {
            if (this.logWarnings) {
                warn(`[Invincibility] 指定されたコンポーネント "${this.hurtboxComponentType}" が hurtboxNode に見つかりませんでした。ノード名=${this.hurtboxNode.name}`);
            }
            return;
        }

        this._hurtboxComponent = comp;
    }

    /**
     * 無敵開始前の Hurtbox / Target の状態を記録する。
     */
    private _storeOriginalStates() {
        // Hurtbox の元状態
        if (this._hurtboxComponent) {
            this._originalHurtboxEnabled = this._hurtboxComponent.enabled;
        } else if (this.hurtboxNode) {
            this._originalHurtboxActive = this.hurtboxNode.active;
        }

        // 点滅対象ノードの元状態
        if (this.targetNode) {
            this._originalTargetActive = this.targetNode.active;
        }
    }

    /**
     * Hurtbox の有効 / 無効を切り替える。
     * コンポーネントタイプが指定されている場合は enabled を操作し、
     * 指定されていない場合はノードの active を操作します。
     */
    private _setHurtboxEnabled(enabled: boolean) {
        if (this._hurtboxComponent) {
            this._hurtboxComponent.enabled = enabled;
        } else if (this.hurtboxNode) {
            this.hurtboxNode.active = enabled;
        }
    }

    /**
     * 無敵状態の終了処理。
     * Hurtbox や点滅状態を元に戻します。
     */
    private _endInvincibility() {
        this._isInvincible = false;

        // Hurtbox 状態を元に戻す
        if (this._hurtboxComponent && this._originalHurtboxEnabled !== null) {
            this._hurtboxComponent.enabled = this._originalHurtboxEnabled;
        } else if (this.hurtboxNode && this._originalHurtboxActive !== null) {
            this.hurtboxNode.active = this._originalHurtboxActive;
        }

        // 点滅対象ノードの表示状態を元に戻す
        if (this.targetNode) {
            this.targetNode.active = this._originalTargetActive;
        }

        if (this.logWarnings) {
            log('[Invincibility] 無敵終了');
        }
    }

    /**
     * このコンポーネントが無効化されたり破棄されたときに、
     * もし無敵中であれば状態を安全に元に戻します。
     */
    onDisable() {
        if (this._isInvincible) {
            this._endInvincibility();
        }
    }

    onDestroy() {
        if (this._isInvincible) {
            this._endInvincibility();
        }
    }
}

コードの要点解説

  • onLoad()
    • targetNodehurtboxNode が未設定なら自ノードを自動設定
    • Hurtbox コンポーネントを文字列クラス名から getComponent で解決
    • startInactive が true の場合は開始直後に triggerInvincibility() を呼ぶ
  • update()
    • game.totalTime を用いて「現在時刻」を秒単位で取得し、無敵終了タイミングを監視
    • 無敵中は flashInterval ごとに targetNode.active をトグルして点滅を実現
  • triggerInvincibility()
    • 外部から呼び出す公開メソッド
    • 引数 durationOverride を渡すと、その呼び出しだけ別の長さの無敵にできる
    • すでに無敵中でも、終了時刻を再設定して「延長 or リセット」できる
  • Hurtbox 制御
    • hurtboxComponentType が指定されていれば、そのコンポーネントの enabled を操作
    • 指定されていなければ、hurtboxNode.active を操作してノードごと無効化
  • 防御的実装
    • Hurtbox ノードやコンポーネントが見つからない場合には warn で通知(logWarnings でオンオフ可能)
    • onDisable / onDestroy で、無敵中にコンポーネントが無効化・破棄されても状態を元に戻す

使用手順と動作確認

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

  1. Assets パネルで右クリック → Create → TypeScript を選択します。
  2. ファイル名を Invincibility.ts にします。
  3. 自動生成されたコードをすべて削除し、本記事の Invincibility クラスのコードを貼り付けて保存します。

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

ここでは例として 2D のキャラクターを想定し、Collider2D を Hurtbox として扱うケースで説明します。

  1. Hierarchy パネルで右クリック → Create → 2D Object → Sprite などを選んで、テスト用のノード(例: Player)を作成します。
  2. Player ノードを選択し、Inspector の Add Component ボタンから
    • Physics 2D → Collider2D(任意の形状) を追加します。

    これが Hurtbox 相当になります。

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

  1. Hierarchy で Player ノードを選択します。
  2. Inspector の Add ComponentCustomInvincibility を選択して追加します。

4. プロパティの設定

Inspector 上で、Invincibility コンポーネントの各プロパティを次のように設定してみます。

  • Duration: 1.0
    • 1 秒間無敵にします。
  • Flash Interval: 0.1
    • 0.1 秒ごとに点滅します。
  • Target Node: (空欄のまま)
    • 空欄にしておくと、Player ノード自身が点滅します。
  • Hurtbox Node: (空欄のまま)
    • 空欄にしておくと、Player ノードが Hurtbox 対象になります。
  • Hurtbox Component Type: Collider2D
    • Player に付いている Collider2D コンポーネントを無効化します。
  • Log Warnings: ON
    • ログを見ながら挙動を確認したいので ON のままにします。
  • Start Inactive: OFF
    • ゲーム開始時は無敵ではない状態から始めます。

5. 無敵時間を実際に発動させる

このコンポーネントは「アタッチしただけで自動で無敵が始まる」わけではなく、
ダメージを受けたタイミングなどで明示的に triggerInvincibility() を呼び出す設計です。

簡単なテスト方法として、同じ Player ノードにテスト用スクリプトを追加し、キー入力で無敵を開始してみます。

  1. Assets パネルで右クリック → Create → TypeScript を選択し、InvincibilityTester.ts を作成します。
  2. 以下のような簡易テストコードを貼り付けます。

import { _decorator, Component, input, Input, EventKeyboard, KeyCode } from 'cc';
import { Invincibility } from './Invincibility';
const { ccclass } = _decorator;

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

    private _inv: Invincibility | null = null;

    onLoad() {
        this._inv = this.getComponent(Invincibility);
        if (!this._inv) {
            console.warn('[InvincibilityTester] 同じノードに Invincibility コンポーネントが見つかりません。');
        }
        input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
    }

    onDestroy() {
        input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
    }

    private onKeyDown(event: EventKeyboard) {
        if (!this._inv) {
            return;
        }
        if (event.keyCode === KeyCode.SPACE) {
            // スペースキーで無敵時間を開始
            this._inv.triggerInvincibility();
        }
    }
}
  1. Player ノードに InvincibilityTester コンポーネントを Add Component → Custom から追加します。
  2. エディタ右上の Play ボタンでプレビューを開始します。
  3. ゲーム画面が表示されたら、キーボードの Space キーを押してみてください。

期待される挙動:

  • Space キーを押した瞬間から約 1 秒間、Player が高速に点滅します。
  • この間、Player に付いている Collider2D コンポーネントの enabledfalse になっているため、物理的な当たり判定が無効になります。
  • 1 秒経過すると点滅が止まり、Collider2Denabled が元の状態(通常は true)に戻ります。
  • Console に [Invincibility] 無敵開始 ... / [Invincibility] 無敵終了 のログが表示されます。

6. Hurtbox をノードごと無効化するパターン

もし Hurtbox を「特定のコンポーネント」ではなく「ノード丸ごと」無効化したい場合は、

  • Hurtbox Node: Hurtbox 用の子ノード(例: Player/Hurtbox)をドラッグ&ドロップで指定
  • Hurtbox Component Type: (空文字) のまま

と設定します。この場合、無敵中は hurtboxNode.active = false となり、無敵終了後に元の active 状態に戻ります。


まとめ

この Invincibility コンポーネントは、

  • 任意のノードにアタッチしておくだけで
  • 外部から triggerInvincibility() を呼ぶだけで
  • Hurtbox の無効化と点滅表示を一括管理できる

という「ダメージ後無敵」の定番処理を汎用化したものです。

応用例としては、

  • 敵キャラにも同じコンポーネントを付けて、被弾時に一瞬だけ無敵にする
  • ステージギミック(トゲ床など)に当たった後、一定時間再ダメージを受けないようにする
  • スポーン直後の無敵(startInactive = true)を簡単に実装する

といった形で、ゲーム中のさまざまな「再ダメージ防止」処理を統一的に扱うことができます。
外部の GameManager やシングルトンに依存しないため、プロジェクト間でのコピペ再利用もしやすく、
コンポーネント単体で完結した設計になっている点も大きなメリットです。

自分のゲームの構成に合わせて、hurtboxNodehurtboxComponentType を適切に設定するだけで、
2D / 3D / 各種当たり判定に対応した無敵時間処理を簡単に導入できます。ぜひプロジェクトに組み込んでみてください。

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