Unityを触り始めた頃、つい何でもかんでも Update() に書いてしまいがちですよね。
「ダメージ処理」「ノックバック処理」「アニメーション切り替え」「入力処理」…全部1つのスクリプトに詰め込むと、だんだん手が付けられない「Godクラス」が出来上がってしまいます。
とくにアクションゲームでは、
- ダメージは受けるけど、吹き飛びは無効化したい(ボスのスーパーアーマーなど)
- 特定の攻撃中だけノックバックを止めたい
といった「状態によってノックバック挙動を切り替える」要件がよく出てきます。
これを1つの巨大なプレイヤー/敵スクリプトに全部書いてしまうと、条件分岐だらけになってメンテナンスが大変です。
そこで今回は、「ダメージは通すけど、ノックバックだけ無効化する」という責務だけを切り出した、
小さなコンポーネント 「SuperArmor」 を作ってみましょう。
【Unity】吹き飛ばないタフなキャラを作る!「SuperArmor」コンポーネント
この記事では、以下のような構成を前提にします。
KnockbackReceiver… ノックバック(吹き飛び)を処理するコンポーネントDamageable… ダメージを受けてHPを減らすコンポーネントSuperArmor… 「ダメージは通すが、ノックバックを無効化する」状態を管理するコンポーネント
SuperArmor自体は「ノックバックのON/OFFを制御するだけ」に責務を限定し、
ノックバックの実装やダメージ処理は別コンポーネントに任せる設計にします。
前提:シンプルな KnockbackReceiver と Damageable
まずは、SuperArmor と連携するためのシンプルな KnockbackReceiver と Damageable を用意します。
既に自作のものがある場合は、それに合わせて IsKnockbackDisabled のようなフラグを読みに行く形に改造してもOKです。
using UnityEngine;
/// <summary>ノックバック(吹き飛び)を処理するコンポーネント</summary>
[RequireComponent(typeof(Rigidbody))]
public class KnockbackReceiver : MonoBehaviour
{
[Header("ノックバック設定")]
[SerializeField] private float knockbackForce = 10f; // ノックバックの基本力
[SerializeField] private float verticalForce = 2f; // 少し浮かせる場合の上方向成分
private Rigidbody _rb;
// 外部からノックバック無効化を制御するためのフラグ
public bool IsKnockbackDisabled { get; set; }
private void Awake()
{
_rb = GetComponent<Rigidbody>();
}
/// <summary>
/// 攻撃側から呼び出してノックバックさせる
/// direction は「攻撃された方向とは逆向き」のベクトルを想定
/// </summary>
public void ApplyKnockback(Vector3 direction)
{
// スーパーアーマーなどでノックバックが無効な場合は何もしない
if (IsKnockbackDisabled)
{
// デバッグ用ログ(必要に応じてコメントアウト)
// Debug.Log($"{name}: Knockback is disabled.");
return;
}
// direction がゼロベクトルの場合は安全のためリターン
if (direction.sqrMagnitude < 0.0001f)
{
return;
}
// 正規化して力を計算
Vector3 knockDir = direction.normalized;
Vector3 force = (knockDir * knockbackForce) + (Vector3.up * verticalForce);
// 既存の速度を少し抑えてからノックバックを加える
_rb.velocity = new Vector3(_rb.velocity.x * 0.3f, _rb.velocity.y, _rb.velocity.z * 0.3f);
_rb.AddForce(force, ForceMode.VelocityChange);
}
}
/// <summary>ダメージを受けるコンポーネント(HP管理)</summary>
public class Damageable : MonoBehaviour
{
[Header("HP設定")]
[SerializeField] private int maxHp = 100;
private int _currentHp;
// HPの現在値を外部から読み取りたい場合用
public int CurrentHp => _currentHp;
private void Awake()
{
_currentHp = maxHp;
}
/// <summary>ダメージを受ける</summary>
public void TakeDamage(int amount)
{
if (amount <= 0) return;
_currentHp -= amount;
_currentHp = Mathf.Max(_currentHp, 0);
// ここでアニメーションやSE再生などを呼び出してもOK
// Debug.Log($"{name} took {amount} damage. HP: {_currentHp}/{maxHp}");
if (_currentHp == 0)
{
OnDead();
}
}
private void OnDead()
{
// 死亡時の処理(とりあえず非アクティブ化)
// 実際のゲームではアニメーション再生やリスポーン処理などをここに
gameObject.SetActive(false);
}
}
本題:SuperArmor コンポーネントの実装
いよいよ SuperArmor 本体です。
SuperArmor は以下の責務だけを持ちます。
- ノックバック無効フラグを管理する
- KnockbackReceiver にその状態を反映する
- 任意のタイミングで ON/OFF できる API を提供する
ダメージ処理は Damageable が担当し、SuperArmor は一切触りません。
「ダメージは受けるが、吹き飛びだけ無効化する」という分離がポイントです。
using System.Collections;
using UnityEngine;
/// <summary>
/// スーパーアーマー状態を管理するコンポーネント。
/// ダメージは受けるが、KnockbackReceiver による吹き飛びを無効化する。
/// </summary>
[RequireComponent(typeof(KnockbackReceiver))]
public class SuperArmor : MonoBehaviour
{
[Header("初期状態")]
[SerializeField]
private bool startWithSuperArmor = false;
// 最初からスーパーアーマー状態にするかどうか(ボスなど)
[Header("デバッグ用表示")]
[SerializeField]
private bool showDebugLog = false;
private KnockbackReceiver _knockbackReceiver;
// 現在スーパーアーマーが有効かどうか(外部から読み取り専用)
public bool IsActive { get; private set; }
private Coroutine _autoDisableCoroutine;
private void Awake()
{
_knockbackReceiver = GetComponent<KnockbackReceiver>();
}
private void Start()
{
// 初期状態を反映
if (startWithSuperArmor)
{
EnableSuperArmor();
}
else
{
DisableSuperArmor();
}
}
/// <summary>
/// スーパーアーマーを有効化する(ノックバック無効)。
/// </summary>
public void EnableSuperArmor()
{
IsActive = true;
_knockbackReceiver.IsKnockbackDisabled = true;
if (showDebugLog)
{
Debug.Log($"[{name}] SuperArmor ENABLED");
}
}
/// <summary>
/// スーパーアーマーを無効化する(ノックバック有効)。
/// </summary>
public void DisableSuperArmor()
{
IsActive = false;
_knockbackReceiver.IsKnockbackDisabled = false;
if (showDebugLog)
{
Debug.Log($"[{name}] SuperArmor DISABLED");
}
}
/// <summary>
/// 一定時間だけスーパーアーマーを付与する。
/// 例:攻撃モーション中だけ、被弾しても吹き飛ばないようにする。
/// </summary>
/// <param name="duration">付与時間(秒)</param>
public void EnableForSeconds(float duration)
{
if (duration <= 0f)
{
// 0以下の場合は即時OFF扱い
DisableSuperArmor();
return;
}
// すでにタイマーが動いていたらキャンセル
if (_autoDisableCoroutine != null)
{
StopCoroutine(_autoDisableCoroutine);
}
_autoDisableCoroutine = StartCoroutine(AutoDisableRoutine(duration));
}
/// <summary>
/// 外部から「今スーパーアーマー中ならノックバックを飛ばしてほしい」などの判定に使えるヘルパー。
/// </summary>
public bool IsSuperArmorActive()
{
return IsActive;
}
/// <summary>
/// 一定時間後に自動でスーパーアーマーをOFFにするコルーチン。
/// </summary>
private IEnumerator AutoDisableRoutine(float duration)
{
EnableSuperArmor();
float timer = 0f;
while (timer < duration)
{
timer += Time.deltaTime;
yield return null;
}
DisableSuperArmor();
_autoDisableCoroutine = null;
}
}
攻撃側からの利用例:ダメージ+ノックバックを与えるコンポーネント
SuperArmor の動作を確認するために、攻撃判定用の簡単なコンポーネントも用意しておきます。
プレイヤーや敵の「武器」オブジェクトに付けて、OnTriggerEnter でダメージとノックバックを与えるイメージです。
using UnityEngine;
/// <summary>
/// 当たった相手にダメージ+ノックバックを与えるシンプルな攻撃コンポーネント。
/// </summary>
public class SimpleAttacker : MonoBehaviour
{
[Header("攻撃設定")]
[SerializeField] private int damage = 10;
[SerializeField] private float knockbackPower = 1f; // KnockbackReceiver の knockbackForce に乗算する係数
[Header("攻撃者のTransform(ノックバック方向計算用)")]
[SerializeField] private Transform attackerRoot;
private void Reset()
{
// デフォルトで自分自身を攻撃者の基準にする
attackerRoot = transform;
}
private void OnTriggerEnter(Collider other)
{
// ダメージ処理
Damageable damageable = other.GetComponentInParent<Damageable>();
if (damageable != null)
{
damageable.TakeDamage(damage);
}
// ノックバック処理
KnockbackReceiver knockbackReceiver = other.GetComponentInParent<KnockbackReceiver>();
if (knockbackReceiver != null)
{
// 「攻撃された側から見てどの方向に吹き飛ぶか」を計算
Vector3 dir = (other.transform.position - attackerRoot.position).normalized;
// Power を掛けて方向ベクトルをスケール
knockbackReceiver.ApplyKnockback(dir * knockbackPower);
}
}
}
SuperArmor が有効なキャラにこの攻撃を当てると、HPは減るがノックバックは発生しない挙動になります。
使い方の手順
-
プレイヤー or 敵キャラのプレハブに基礎コンポーネントを追加
例として、3Dアクションゲームの敵キャラを想定します。- 敵キャラの GameObject(例:
Enemy)を選択 Rigidbodyを追加(Use Gravity ON / Constraints は必要に応じて設定)KnockbackReceiverを追加Damageableを追加
- 敵キャラの GameObject(例:
-
SuperArmor コンポーネントを追加
同じEnemyGameObject にSuperArmorを追加します。Start With Super Armorを ON にすると、常時吹き飛ばないボスのような挙動になります。- OFF にしておけば、スクリプトから任意のタイミングで有効化できます。
- 動作確認用に
Show Debug Logを ON にしておくと、状態変化がコンソールに出ます。
-
攻撃側(プレイヤー武器や敵の当たり判定)に SimpleAttacker を追加
例:プレイヤーの剣オブジェクトに以下を設定します。- 剣オブジェクトに
Collider(Is Trigger = ON)を追加 SimpleAttackerを追加Attacker Rootにプレイヤーのルート Transform をドラッグ&ドロップ- ダメージ量や
Knockback Powerを調整
これで剣の当たり判定が敵に触れると、
DamageableとKnockbackReceiverに処理が飛びます。 - 剣オブジェクトに
-
一時的なスーパーアーマーをスクリプトから付与する
例として、敵の攻撃モーション中だけスーパーアーマーを付与したい場合、
敵AIやアニメーション制御スクリプトから次のように呼び出します。public class EnemyAttackSample : MonoBehaviour { [SerializeField] private SuperArmor superArmor; [SerializeField] private float attackSuperArmorDuration = 0.8f; public void StartAttack() { // 攻撃開始時にスーパーアーマーを一定時間付与 superArmor.EnableForSeconds(attackSuperArmorDuration); // ここで攻撃アニメーション再生などを行う // animator.SetTrigger("Attack"); } }これで「攻撃モーション中だけ、被弾しても吹き飛ばない敵」が簡単に作れます。
プレイヤー側でも同じように適用できるので、「強攻撃中だけスーパーアーマー」などの実装にも便利ですね。
メリットと応用
SuperArmor を独立したコンポーネントとして切り出すことで、次のようなメリットがあります。
-
プレハブのバリエーション管理が楽になる
- 通常敵:
Damageable + KnockbackReceiver - 常時スーパーアーマー敵:
Damageable + KnockbackReceiver + SuperArmor(startWithSuperArmor = true) - 一部攻撃中のみスーパーアーマー敵:
SuperArmorを追加し、AIからEnableForSeconds()を呼ぶ
というように、コンポーネントの組み合わせだけで挙動を切り替えられます。
- 通常敵:
-
巨大なプレイヤー/敵スクリプトを避けられる
ノックバック無効化のロジックをプレイヤーや敵のメインスクリプトに書き始めると、
条件分岐(if文)だらけになって保守しづらくなります。
SuperArmor に「ノックバック無効化」という責務を閉じ込めることで、
他のコンポーネントは「スーパーアーマーがあるかどうか」を気にせずシンプルに書けます。 -
アニメーションやステートマシンとの連携がしやすい
Animator の StateMachineBehaviour やタイムラインからEnableSuperArmor()/DisableSuperArmor()を呼ぶだけで、
特定のアニメーション中だけスーパーアーマーを付与する、といった制御も簡単に行えます。
さらに応用として、「特定の攻撃タイプだけノックバック無効」 といった拡張も考えられます。
例えば「小攻撃のノックバックは無効だが、必殺技のノックバックは通す」といったボス挙動です。
その一歩として、「スーパーアーマー中でも、強制的にノックバックを通す」ための
簡単な API を SuperArmor に追加する改造案を示しておきます。
/// <summary>
/// スーパーアーマー中であっても、強制的にノックバックを適用したいときに呼ぶ。
/// 例:必殺技による強制吹き飛ばしなど。
/// </summary>
public void ForceKnockback(Vector3 direction)
{
// 一時的にフラグを無効化してノックバックを通す
bool prevState = _knockbackReceiver.IsKnockbackDisabled;
_knockbackReceiver.IsKnockbackDisabled = false;
_knockbackReceiver.ApplyKnockback(direction);
_knockbackReceiver.IsKnockbackDisabled = prevState;
}
このように小さなコンポーネント単位で責務を分けておくと、
「このボスだけ特殊な挙動をさせたい」といった要件にも柔軟に対応しやすくなります。
ぜひ自分のプロジェクトに合わせて SuperArmor を育ててみてください。
