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は便利な反面、使い方を誤ると“見えないバグ”を生むこともあるという、非常に良い学びでした。
コメント