【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()targetNodeとhurtboxNodeが未設定なら自ノードを自動設定- Hurtbox コンポーネントを文字列クラス名から
getComponentで解決 startInactiveが true の場合は開始直後にtriggerInvincibility()を呼ぶ
update()game.totalTimeを用いて「現在時刻」を秒単位で取得し、無敵終了タイミングを監視- 無敵中は
flashIntervalごとにtargetNode.activeをトグルして点滅を実現
triggerInvincibility()- 外部から呼び出す公開メソッド
- 引数
durationOverrideを渡すと、その呼び出しだけ別の長さの無敵にできる - すでに無敵中でも、終了時刻を再設定して「延長 or リセット」できる
- Hurtbox 制御
hurtboxComponentTypeが指定されていれば、そのコンポーネントのenabledを操作- 指定されていなければ、
hurtboxNode.activeを操作してノードごと無効化
- 防御的実装
- Hurtbox ノードやコンポーネントが見つからない場合には
warnで通知(logWarningsでオンオフ可能) onDisable/onDestroyで、無敵中にコンポーネントが無効化・破棄されても状態を元に戻す
- Hurtbox ノードやコンポーネントが見つからない場合には
使用手順と動作確認
1. スクリプトファイルの作成
- Assets パネルで右クリック → Create → TypeScript を選択します。
- ファイル名を
Invincibility.tsにします。 - 自動生成されたコードをすべて削除し、本記事の
Invincibilityクラスのコードを貼り付けて保存します。
2. テスト用ノードの作成
ここでは例として 2D のキャラクターを想定し、Collider2D を Hurtbox として扱うケースで説明します。
- Hierarchy パネルで右クリック → Create → 2D Object → Sprite などを選んで、テスト用のノード(例:
Player)を作成します。 Playerノードを選択し、Inspector の Add Component ボタンから- Physics 2D → Collider2D(任意の形状) を追加します。
これが Hurtbox 相当になります。
3. Invincibility コンポーネントをアタッチ
- Hierarchy で
Playerノードを選択します。 - Inspector の Add Component → Custom → Invincibility を選択して追加します。
4. プロパティの設定
Inspector 上で、Invincibility コンポーネントの各プロパティを次のように設定してみます。
- Duration:
1.0- 1 秒間無敵にします。
- Flash Interval:
0.1- 0.1 秒ごとに点滅します。
- Target Node:
(空欄のまま)- 空欄にしておくと、
Playerノード自身が点滅します。
- 空欄にしておくと、
- Hurtbox Node:
(空欄のまま)- 空欄にしておくと、
Playerノードが Hurtbox 対象になります。
- 空欄にしておくと、
- Hurtbox Component Type:
Collider2DPlayerに付いているCollider2Dコンポーネントを無効化します。
- Log Warnings:
ON- ログを見ながら挙動を確認したいので ON のままにします。
- Start Inactive:
OFF- ゲーム開始時は無敵ではない状態から始めます。
5. 無敵時間を実際に発動させる
このコンポーネントは「アタッチしただけで自動で無敵が始まる」わけではなく、
ダメージを受けたタイミングなどで明示的に triggerInvincibility() を呼び出す設計です。
簡単なテスト方法として、同じ Player ノードにテスト用スクリプトを追加し、キー入力で無敵を開始してみます。
- Assets パネルで右クリック → Create → TypeScript を選択し、
InvincibilityTester.tsを作成します。 - 以下のような簡易テストコードを貼り付けます。
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();
}
}
}
Playerノードに InvincibilityTester コンポーネントを Add Component → Custom から追加します。- エディタ右上の Play ボタンでプレビューを開始します。
- ゲーム画面が表示されたら、キーボードの Space キーを押してみてください。
期待される挙動:
- Space キーを押した瞬間から約 1 秒間、
Playerが高速に点滅します。 - この間、
Playerに付いているCollider2Dコンポーネントのenabledがfalseになっているため、物理的な当たり判定が無効になります。 - 1 秒経過すると点滅が止まり、
Collider2Dのenabledが元の状態(通常は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 やシングルトンに依存しないため、プロジェクト間でのコピペ再利用もしやすく、
コンポーネント単体で完結した設計になっている点も大きなメリットです。
自分のゲームの構成に合わせて、hurtboxNode と hurtboxComponentType を適切に設定するだけで、
2D / 3D / 各種当たり判定に対応した無敵時間処理を簡単に導入できます。ぜひプロジェクトに組み込んでみてください。




