调用Destroy()后脚本仍执行?Unity碰撞回调问题咨询
Unity中Destroy与脚本启用/禁用的生效时机问题解答
核心结论
是的,Unity里调用Destroy()或设置脚本enabled=false都不会立即生效。
原因解释
Destroy(gameObject)的执行逻辑:
Unity不会在调用Destroy的瞬间销毁对象,而是将对象标记为待销毁状态加入销毁队列。要等到当前帧的所有Update、FixedUpdate、碰撞回调等所有同步逻辑执行完毕后,才会在帧的末尾阶段真正销毁对象。所以调用Destroy之后,当前方法的剩余代码会正常执行,甚至同一帧内如果有其他碰撞触发回调,脚本依然会响应——因为此时对象还没被真正移除。脚本
enabled=false的生效时机:
脚本启用状态的变更同样不会立即中断当前正在执行的方法,要等当前帧的所有逻辑跑完后,才会在下一帧开始时生效。所以同一帧内的后续回调依然会触发,直到帧结束。
你的代码问题分析
你遇到的health=-1情况,本质是同一帧内有两次碰撞触发了OnCollisionEnter2D:
- 第一次碰撞:
health从1降到0,进入死亡分支,调用Destroy(gameObject),但对象此时还没被销毁。 - 第二次碰撞:同一帧内触发,脚本仍能响应回调,
health再次减1变成-1,进入死亡分支后打印数值,之后再次调用Destroy(但对象已经标记为待销毁,重复调用无影响)。 - 不会出现-2是因为第二次处理完后,对象已标记待销毁,后续帧不会再触发相关逻辑。
解决方法
最可靠的方式是添加一个死亡状态标记,在回调开头就判断是否已死亡,直接终止逻辑:
private bool isDead; // 新增死亡标记 private void CheckDamageCollision(Collision2D other) { if (isDead) return; // 已死亡直接返回,不再处理任何伤害 if (!other.gameObject.CompareTag(Constants.TAG_ENEMY) && !other.gameObject.CompareTag(Constants.TAG_OBSTACLE) && !other.gameObject.CompareTag(Constants.TAG_ENEMY_WEAPON)) return; if (justDamage) return; --health; PlayerDamage?.Invoke(health); if (health < 1) { isDead = true; // 立即标记死亡 print("Health : " + health); PlayerDead?.Invoke(); IgnoreLayerMatrix(false); Destroy(gameObject); return; } StartCoroutine(WaitNextDamageCollide()); }
这个标记是立即生效的,能确保同一帧内的后续碰撞不会再执行伤害逻辑,彻底避免health被多次减少的问题。
内容的提问来源于stack exchange,提问作者Nyein Chan
相关产品推荐
相关产品推荐

