Unity脚本功能正常却出现NullReferenceException的排查与解决
NullReferenceException在GameObject.FindGameObjectWithTag上的问题 报错信息:
NullReferenceException: Object reference not set to an instance of an object DestroyCollision.OnCollisionEnter2D (UnityEngine.Collision2D collision) (at Assets/Scripts/DestroyCollision.cs:66)
报错原因
GameObject.FindGameObjectWithTag("Shield")返回null导致报错,核心原因是:该API仅能查找场景中处于激活状态(activeSelf = true)的对象。
你的代码逻辑中,第一次碰撞到Shield时,对象处于激活状态,Find方法能正常找到并执行SetActive(false);但如果后续有重复碰撞触发(比如多个碰撞体连续触发该逻辑),此时Shield已经被禁用,FindGameObjectWithTag会返回null,调用SetActive自然抛出空引用异常。你觉得功能正常是因为首次逻辑执行成功,不影响核心功能,但后续触发会持续输出错误。
修复方案
最优方案:直接操作碰撞到的对象
既然已经通过collision.gameObject.tag == "Shield"确认碰撞对象就是目标Shield,无需额外查找,直接操作该对象即可彻底避免空引用风险:
if (collision.gameObject.tag == "Shield") { collision.gameObject.SetActive(false); Destroy(this.gameObject); }
备选方案:提前缓存全局引用
如果需要在多个逻辑中访问Shield对象,建议在脚本初始化阶段缓存引用,避免重复查找的性能开销和空引用问题:
private GameObject shield; void Awake() { // 初始化时一次性查找并缓存 shield = GameObject.FindGameObjectWithTag("Shield"); } void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.tag == "Shield" && shield != null) { shield.SetActive(false); Destroy(this.gameObject); } }
注意:若Shield对象可能在运行时被销毁,需额外判断引用有效性。
特殊场景:查找非激活对象
如果确实需要定位非激活状态的Shield对象,可使用FindObjectsOfType并开启includeInactive参数(性能开销较高,不推荐频繁调用):
if (collision.gameObject.tag == "Shield") { GameObject[] allObjects = GameObject.FindObjectsOfType<GameObject>(true); foreach (GameObject obj in allObjects) { if (obj.CompareTag("Shield")) { obj.SetActive(false); break; } } Destroy(this.gameObject); }
内容的提问来源于stack exchange,提问作者Rick

