はじめまして、オルトプラスでクライアントエンジニアをしているepagoraと申します。
これまで4年半ほどUnityでの開発に従事してきましたので、今回はUnity C#で見かけるありがちなアンチパターンを3つお届けしようと思います。
たった3つではありますが、知らない人にとっては結構ハマりどころだと思うので、どうぞ読んでやってください。
1つ目は個人的にUnityあるあるだと思っているこちらです。
public class Bullet : MonoBehaviour
{
public void AliveLog()
{
Debug.Log("Still alive");
}
}
まずは生存しているかどうかのログを出す機能しかありませんが、弾のクラスです。
public class Sample : MonoBehaviour
{
[SerializeField] Bullet prefab;
Bullet bullet = null;
void Start()
{
bullet = Instantiate(prefab);
StartCoroutine(AutoDestroy());
}
IEnumerator AutoDestroy()
{
yield return new WaitForSeconds(3.0f);
Destroy(bullet);
}
void Update()
{
// 実際はログが出過ぎちゃうからやめてね
bullet?.AliveLog();
}
}
そして、その弾をInstantiateし、3秒後にDestroyするという処理です。
Updateではbulletがnullでなければ生存ログを出すようにしています。
しかしこのコード、bulletをDestroyしたあとも生存ログが出続けます。
場合によってはNullReferenceExceptionが出ることもあります。
nullチェックは確かにしているはずなのに、なぜでしょうか?
実は、UnityEngine.Objectを継承しているクラスにとってのnullは、C#本来のnullとは違うものなのです。
UnityEngine.ObjectはDestroyされることでnullになりますが、それはC#本来のnullになるのではなく、Unity的にnull扱いという状態になります。