UnityのScriptableObjectで起きた意外なバグとその解決法~複数の敵が弾を勝手に撃ち出す奇怪な挙動の原因とは?~

App

UnityでScriptableObject(以下SO)を使って敵キャラのデータを管理していたところ、
複数の敵を配置すると、なぜかバフのはずが弾を撃ち出す」という謎の挙動に直面しました。

今回はその原因と解決法を、実際のログやコードを元にまとめます。


使っていた構成

敵キャラには EnemyData という SO を使って以下のようなパラメータを保持していました。

[CreateAssetMenu(fileName = "EnemyData", menuName = "Game/EnemyData")]
public class EnemyData : ScriptableObject
{
public string enemyName = "Enemy";
public int maxHP = 100;
public float attackInterval = 10f;
public int attackPower = 10;
public int defense = 5;
public EnemyActionType actionType = EnemyActionType.Attack; // ← ここが落とし穴
}

この SO をプレハブの各 EnemyController に設定して利用。


起きた問題:なぜか複数体いると弾が勝手に飛ぶ

敵が1体のときは正しく行動しますが、2体以上置いた途端に「バフのはずが攻撃になる」「回復中にも弾が出る」などのバグが発生。


原因:SOのインスタンスが全員で共有されていた

SOは Unity 上での「共有可能なアセット」です。
そのため、複数の敵が同じ SO を参照していると、actionType を書き換えたタイミングで全員に影響します。

実際、ログを見ると以下のように他の敵が選んだ actionType に上書きされてしまっていました:

[Decision] Drone A decided to Buff
[Decision] Drone B decided to Attack
→ Buff のはずが Drone A も弾を発射

🛠️ 解決策:SOをランタイムで複製する

[SerializeField] private EnemyData statsTemplate; // SO参照用
private EnemyData stats; // 複製インスタンス

void Start()
{
stats = Instantiate(statsTemplate); // ここで複製
}

こうすることで、各敵が個別の stats を持つようになり、バグは完全に解消しました。


お約束:SOに「変化する値」は持たせない!

SOは 共有されるもの
→ 変化しうる値(現在HP、状態、行動ターゲットなど)は入れない。

OK(プリセット値)NG(ランタイム状態)
maxHP, 攻撃力などcurrentHP, actionType など

今回得た教訓

  • ScriptableObject は「設定用」と割り切る
  • 状態を含めるなら Start()Instantiate() して複製するのがお約束
  • プレハブに SO を入れてる場合、**必ずコピーすべきか?**を意識する

まとめ

今回のバグは、**「一見関係ない敵が弾を撃つ」**という直感では分かりにくいものでしたが、
原因を追っていくと、ScriptableObjectの共有性が関係していたことがわかりました。

SOは便利な反面、使い方を誤ると“見えないバグ”を生むこともあるという、非常に良い学びでした。

コメント

タイトルとURLをコピーしました