【Cocos Creator 3.8】SuperArmor の実装:アタッチするだけで「吹き飛びだけを無効化」する汎用スクリプト
このガイドでは、Cocos Creator 3.8.7 と TypeScript で、ダメージは受けるが、ノックバック(吹き飛び)だけを無効化できる「SuperArmor」コンポーネントを実装します。
敵・プレイヤー・ボスなどのキャラクターにアタッチするだけで、既存のノックバック処理(KnockbackReceiver など)を一時的または恒久的に無効化できるようにし、状態異常・スキル・ボス専用ギミックなどで簡単に扱えるようにします。
コンポーネントの設計方針
前提と想定
- プロジェクト内には、何らかの「吹き飛びを発生させるコンポーネント」が存在すると想定します(例:
KnockbackReceiver)。 - ただし、この SuperArmor はそれらの具体的な実装に依存しません。
→ 任意のスクリプトからnode.getComponent(SuperArmor)を見て「今は吹き飛ばしてよいか?」を判定できるような、汎用的なフラグコンポーネントとして設計します。 - 「ダメージは受けるが、ノックバックだけ無効化」という要件から、HP 管理やダメージ計算のロジックは一切持ちません。その代わり、ノックバックを発生させる側が参照するための状態を提供します。
SuperArmor が提供する機能
- 常時スーパーアーマー:有効にしている間は、ノックバックさせようとする側がこのフラグを見て「吹き飛ばさない」判断ができる。
- 時間制限付きスーパーアーマー:一定時間だけスーパーアーマー状態にし、自動で解除する。
- 手動 ON/OFF:スクリプトから
enableSuperArmor()/disableSuperArmor()を呼び出して制御できる。 - 状態変化イベント:ON/OFF のタイミングで任意の処理(エフェクト再生など)を Inspector から登録できるようにする。
このコンポーネント自体はノックバック処理を止めるコードは持たず、「今このノードは吹き飛び無効状態か?」を問い合わせるための、共通インタフェースを提供します。
ノックバックを実装しているコンポーネント側で、例えば以下のように使用します:
// KnockbackReceiver 側(例)
// 実際の KnockbackReceiver 実装には依存しないため、ここは参考例です。
const superArmor = this.node.getComponent(SuperArmor);
if (superArmor && superArmor.isActive) {
// スーパーアーマー中なら、吹き飛びをスキップ
return;
}
// 吹き飛び処理を実行
このように、SuperArmor はどんなノックバック実装とも組み合わせられる「状態フラグ」コンポーネントとして機能します。
Inspector で設定可能なプロパティ設計
SuperArmor コンポーネントに持たせるプロパティは以下の通りです。
- enabledOnStart: boolean
– 初期状態でスーパーアーマーを有効にするかどうか。
–true:start()時点から吹き飛び無効。
–false:開始時は通常状態(吹き飛び有効)。 - useDuration: boolean
– スーパーアーマーを時間制限付きで自動解除するかどうか。
–true:duration秒後に自動で解除。
–false:自動解除は行わない(手動または外部スクリプトで状態を制御)。 - duration: number
– 時間制限付きスーパーアーマーの継続時間(秒)。
– 例:0.5 / 1.0 / 3.0 など。
–useDuration = trueのときのみ有効。 - autoReEnable: boolean
– 時間制限で解除されてから、一定時間後に自動で再度有効にするかどうか。
–true:reEnableDelay秒後に再度スーパーアーマーを ON。
–false:再度有効にするにはスクリプトから明示的に呼び出す。 - reEnableDelay: number
– 自動再有効化までの待機時間(秒)。
–autoReEnable = trueのときのみ有効。 - debugLog: boolean
– スーパーアーマーの ON/OFF 状態が変化したときに、コンソールへログ出力するかどうか。
– デバッグ時に有効にし、リリース時はfalseにする想定。 - onActivated: EventHandler[]
– スーパーアーマーが 有効になった瞬間に呼び出されるイベント。
– エフェクト表示、アニメーション切り替え、サウンド再生などを紐付け可能。 - onDeactivated: EventHandler[]
– スーパーアーマーが 無効になった瞬間に呼び出されるイベント。
– エフェクト非表示、アニメーション戻しなどを紐付け可能。
また、外部スクリプトから利用するための公開読み取り専用プロパティとして、
- isActive: boolean (getter)
– 現在スーパーアーマーが有効かどうかを返す読み取り専用のプロパティ。
–if (superArmor && superArmor.isActive) { ... }のように使用。
TypeScriptコードの実装
以下が、SuperArmor コンポーネントの完成コードです。
import { _decorator, Component, EventHandler, game } from 'cc';
const { ccclass, property } = _decorator;
/**
* SuperArmor
* ダメージは受けるが、ノックバック(吹き飛び)だけを無効化したいときに使用する
* 汎用状態フラグコンポーネント。
*
* - このコンポーネント単体ではノックバック処理を止めません。
* - ノックバックを行う側が this.node.getComponent(SuperArmor) を取得し、
* isActive を参照して「吹き飛ばすかどうか」を判断します。
*/
@ccclass('SuperArmor')
export class SuperArmor extends Component {
@property({
tooltip: 'true の場合、開始時 (start) からスーパーアーマー状態になります。'
})
public enabledOnStart: boolean = true;
@property({
tooltip: 'true の場合、duration 秒経過後にスーパーアーマーを自動で解除します。'
})
public useDuration: boolean = false;
@property({
tooltip: 'スーパーアーマーを維持する時間(秒)。useDuration が true のときのみ有効です。'
})
public duration: number = 1.0;
@property({
tooltip: 'true の場合、解除後に reEnableDelay 秒待って自動で再度有効にします。'
})
public autoReEnable: boolean = false;
@property({
tooltip: 'スーパーアーマーを再度有効にするまでの待機時間(秒)。autoReEnable が true のときのみ有効です。'
})
public reEnableDelay: number = 0.0;
@property({
tooltip: '状態変化時にコンソールへログを出力します。開発・デバッグ時のみ true 推奨。'
})
public debugLog: boolean = false;
@property({
type: [EventHandler],
tooltip: 'スーパーアーマーが有効になった瞬間に呼び出されるイベント群。'
})
public onActivated: EventHandler[] = [];
@property({
type: [EventHandler],
tooltip: 'スーパーアーマーが無効になった瞬間に呼び出されるイベント群。'
})
public onDeactivated: EventHandler[] = [];
// 内部状態:現在スーパーアーマーが有効かどうか
private _isActive: boolean = false;
// 時間制御用
private _timer: number = 0;
private _phase: 'idle' | 'active' | 'cooldown' = 'idle';
/**
* 外部から参照するための読み取り専用プロパティ。
* 現在スーパーアーマーが有効かどうかを返します。
*/
public get isActive(): boolean {
return this._isActive;
}
onLoad() {
// onLoad ではまだゲームの時間進行は始まっていないので、
// 状態だけ初期化しておく。
this._setActive(false, false);
this._timer = 0;
this._phase = 'idle';
}
start() {
// enabledOnStart が true の場合、開始時点からスーパーアーマーを有効化する。
if (this.enabledOnStart) {
this.enableSuperArmor();
}
}
update(deltaTime: number) {
// 時間制御を行う。ゲームが停止しているときは進めない。
if (game.isPaused()) {
return;
}
if (this.useDuration) {
this._updateDuration(deltaTime);
} else if (this.autoReEnable) {
// useDuration が false でも autoReEnable が true の場合、
// 明示的な enableSuperArmor()/disableSuperArmor() と連携して
// クールダウン制御に使えるようにしておく。
this._updateCooldown(deltaTime);
}
}
/**
* スーパーアーマーを有効にする(外部スクリプトからも呼び出し可能)。
* - useDuration が true の場合、duration 秒後に自動で解除されます。
*/
public enableSuperArmor(): void {
this._setActive(true, true);
if (this.useDuration) {
this._timer = 0;
this._phase = 'active';
} else {
this._phase = 'idle';
}
}
/**
* スーパーアーマーを無効にする(外部スクリプトからも呼び出し可能)。
* - autoReEnable が true の場合、reEnableDelay 秒後に自動で再度有効になります。
*/
public disableSuperArmor(): void {
this._setActive(false, true);
if (this.autoReEnable) {
this._timer = 0;
this._phase = 'cooldown';
} else {
this._phase = 'idle';
}
}
/**
* 内部状態を直接書き換えるヘルパー。
* @param active 有効かどうか
* @param invokeEvent イベントを呼び出すかどうか
*/
private _setActive(active: boolean, invokeEvent: boolean): void {
if (this._isActive === active) {
return;
}
this._isActive = active;
if (this.debugLog) {
if (active) {
console.log(`[SuperArmor] Activated on node: ${this.node.name}`);
} else {
console.log(`[SuperArmor] Deactivated on node: ${this.node.name}`);
}
}
if (!invokeEvent) {
return;
}
if (active) {
// 有効化イベントを発火
this._emitEvents(this.onActivated);
} else {
// 無効化イベントを発火
this._emitEvents(this.onDeactivated);
}
}
/**
* duration を使った時間制御。
*/
private _updateDuration(deltaTime: number): void {
if (this._phase !== 'active') {
// active でない場合は何もしない
this._updateCooldown(deltaTime);
return;
}
this._timer += deltaTime;
if (this._timer >= this.duration) {
// 規定時間経過で自動解除
this.disableSuperArmor();
}
}
/**
* autoReEnable を使ったクールダウン制御。
*/
private _updateCooldown(deltaTime: number): void {
if (!this.autoReEnable) {
return;
}
if (this._phase !== 'cooldown') {
return;
}
this._timer += deltaTime;
if (this._timer >= this.reEnableDelay) {
// 規定時間経過で再度有効化
this.enableSuperArmor();
}
}
/**
* EventHandler 配列を一括実行するヘルパー。
*/
private _emitEvents(handlers: EventHandler[]): void {
if (!handlers || handlers.length === 0) {
return;
}
// target が未設定のハンドラがあっても落ちないように防御的に実行
for (const handler of handlers) {
if (!handler || !handler.target) {
if (this.debugLog) {
console.warn('[SuperArmor] EventHandler has no target. Skipped.');
}
continue;
}
try {
EventHandler.emitEvents([handler], this);
} catch (e) {
console.error('[SuperArmor] Error while emitting event:', e);
}
}
}
}
コードの主要部分の解説
enabledOnStartとstart()
–start()内でenabledOnStartをチェックし、trueならenableSuperArmor()を呼び出します。
– これにより、「最初からボスはスーパーアーマー持ち」などを簡単に設定できます。isActivegetter
– 外部からはsuperArmor.isActiveで現在の状態を確認できます。
–private _isActiveは外部から直接変更できないため、状態の一貫性を保てます。enableSuperArmor()/disableSuperArmor()
– 外部スクリプトからも呼べる公開メソッドです。
– これらを呼ぶと、内部状態の更新・イベント発火・タイマーリセットなどをまとめて行います。- 時間制御(
useDuration,autoReEnable,_updateDuration,_updateCooldown)
–useDuration = trueのとき:
enableSuperArmor()で_phase = 'active'にし、duration秒経過でdisableSuperArmor()を自動呼び出し。
–autoReEnable = trueのとき:
disableSuperArmor()で_phase = 'cooldown'にし、reEnableDelay秒経過でenableSuperArmor()を再度呼び出し。 - イベント発火(
onActivated,onDeactivated,_emitEvents())
– 状態が変化したときに、Inspector から登録されたイベントを実行します。
– target が未設定でもエラーでゲームが止まらないように、防御的にチェックしています。 - 防御的な実装
–EventHandler実行時にtry-catchを使い、例外が出てもログに出してゲームが止まらないようにしています。
– 他のカスタムスクリプト(GameManager など)には一切依存していません。
使用手順と動作確認
1. スクリプトファイルの作成
- エディタ上部の Assets パネルで、任意のフォルダ(例:
assets/scripts)を右クリックします。 - Create → TypeScript を選択し、ファイル名を
SuperArmor.tsにします。 - 作成された
SuperArmor.tsをダブルクリックしてエディタで開き、中身をすべて削除してから、前述の TypeScript コードを貼り付けて保存します。
2. テスト用ノードの作成
ここでは分かりやすくするために、単純な Sprite ノードを例にします。
- Hierarchy パネルで右クリック → Create → 2D Object → Sprite を選択し、
TestCharacterなど分かりやすい名前に変更します。 - 必要に応じて Sprite に画像を設定しておきます(任意)。
3. SuperArmor コンポーネントをアタッチ
- Hierarchy で先ほど作成した
TestCharacterノードを選択します。 - Inspector パネルの下部にある Add Component ボタンをクリックします。
- Custom → SuperArmor を選択してアタッチします。
4. Inspector プロパティの設定例
代表的なパターンごとに設定例を紹介します。
パターンA:常時スーパーアーマー(ボスなど)
enabledOnStart:ONuseDuration:OFFduration:1.0(無視される)autoReEnable:OFFreEnableDelay:0.0(無視される)debugLog:必要に応じて ON(動作確認時のみ推奨)
この設定では、ゲーム開始直後からずっと isActive === true の状態が続きます。
ノックバック側が isActive を見ていれば、一切吹き飛ばないボスを簡単に実現できます。
パターンB:短時間だけスーパーアーマー(攻撃中など)
enabledOnStart:OFFuseDuration:ONduration:0.5(例:0.5秒だけスーパーアーマー)autoReEnable:OFFreEnableDelay:0.0(無視される)
この場合、外部スクリプトから攻撃開始時に enableSuperArmor() を呼び出せば、0.5秒後に自動で解除される一時的なスーパーアーマーになります。
パターンC:クールダウン付きスーパーアーマー(一定周期で発動)
enabledOnStart:ONuseDuration:ONduration:2.0(2秒間有効)autoReEnable:ONreEnableDelay:3.0(3秒間は通常状態)
この設定では、
- ゲーム開始時:2秒間スーパーアーマー
- その後3秒間:通常状態(吹き飛び有効)
- 再び2秒間スーパーアーマー
- …というサイクルを自動で繰り返します。
5. ノックバック側からの利用例(参考)
SuperArmor 自体はノックバック処理を持ちません。
ノックバックを担当するコンポーネント(例:KnockbackReceiver)側で、以下のように参照します。
// これは参考例です。実際の KnockbackReceiver 実装はプロジェクトに合わせてください。
import { _decorator, Component, Vec2 } from 'cc';
import { SuperArmor } from './SuperArmor';
const { ccclass, property } = _decorator;
@ccclass('KnockbackReceiverExample')
export class KnockbackReceiverExample extends Component {
@property
knockbackPower: number = 300;
public applyKnockback(direction: Vec2) {
const superArmor = this.node.getComponent(SuperArmor);
if (superArmor && superArmor.isActive) {
// スーパーアーマー中は吹き飛ばさない
return;
}
// ここに実際の吹き飛び処理を書く(RigidBody2D などを使う)
// 例: this.rigidBody.applyLinearImpulseToCenter(...)
}
}
このように、ノックバック側が SuperArmor の存在を「見に行く」だけで、柔軟に吹き飛び無効化を組み込めます。
6. 動作確認のポイント
- debugLog を ON にしてゲームを再生します。
- コンソールに
[SuperArmor] Activated on node: TestCharacter/Deactivated ...のようなログが出るか確認します。 - ノックバック処理側から
isActiveを見ている場合、スーパーアーマー中は吹き飛ばないことを確認します。 - Inspector で
durationやreEnableDelayを変え、挙動が変化するか試してみてください。
まとめ
この SuperArmor コンポーネントは、
- 他のカスタムスクリプトに一切依存せず、単体で完結する「状態フラグ」コンポーネントです。
- ノックバックを行う側が
isActiveを参照するだけで、「ダメージは受けるが吹き飛ばない」挙動を簡単に実現できます。 - Inspector から 常時 / 一時的 / 周期的など、さまざまなスーパーアーマーパターンを設定できるため、ボス、スーパーアーマー付き攻撃、無敵時間の一部表現などに幅広く応用できます。
- イベントフック(
onActivated,onDeactivated)を利用すれば、エフェクトやアニメーションとの連携もノーコードで実現できます。
プロジェクト内のあらゆるキャラクターにこのコンポーネントをアタッチしておけば、吹き飛び挙動を後からでも柔軟に調整・拡張できるようになり、ゲーム全体の設計がシンプルになります。
まずはテスト用のノードに SuperArmor を付けて挙動を確認し、その後プレイヤーや敵キャラクターにも順次導入してみてください。




