You need to enable JavaScript to run this app. Unity中OnCollisionEnter(other)回调的调用顺序由什么决定?
优惠活动
大模型
产品
解决方案
定价
更多

Unity中OnCollisionEnter(other)回调的调用顺序由什么决定?

Unity 碰撞回调顺序问题解决方案

调用顺序的官方规则

Unity 未对两个碰撞物体的OnCollisionEnter同帧执行顺序做任何稳定的公开约定,你测试中观测到的Mover先执行的表现仅为当前运行环境的临时结果,跨版本、跨平台都可能发生变化,绝对不能作为逻辑依赖。
你提到的「层级窗口排序决定执行顺序」的说法是错误的,只有Edit > Project Settings > Script Execution Order中设置的脚本优先级可以控制回调执行顺序,但该方案仅能临时解决表层问题,扩展性极差,不推荐使用。

问题核心

你当前代码的问题是将「状态修改」和「依赖对方状态的读取逻辑」都放在了同一个OnCollisionEnter回调阶段,两个回调执行顺序不确定,自然会产生竞态,导致结果不符合预期。

推荐解决方案

方案1:单脚本集中处理碰撞逻辑(最优)

把同一碰撞事件的所有相关逻辑全部放在其中一个脚本里执行,避免两边重复处理,从根源上消除顺序依赖。按照你的需求,只需要保留Mover的碰撞回调,删除Obstacle的碰撞回调即可:

// Obstacle 脚本简化后
public class Obstacle : MonoBehaviour
{
    private void Start() {
        this.tag = "Unhitted";
    }
}
// Mover 脚本修改后
public class Mover : MonoBehaviour
{
    private int hits = 0;

    private void OnCollisionEnter(Collision other) {
        Debug.Log("Mover has collision.");
        if (other.gameObject.TryGetComponent(out Obstacle obstacle) && obstacle.CompareTag("Unhitted"))
        {
            obstacle.tag = "Hitted";
            Debug.Log($"The mover has had {++hits} times collision");
        }
    }
}

该方案逻辑清晰,执行结果100%确定,没有任何额外副作用。

方案2:拆分状态修改和读取阶段

如果确实有需要两边都处理碰撞的场景,可以把逻辑拆分到不同的Unity执行阶段:

  • 所有OnCollisionEnter中仅做自身状态修改,不要读取对方在本次碰撞阶段修改的状态
  • 把依赖对方最新状态的逻辑,放到当前物理帧所有碰撞回调执行完毕后再执行,比如放在LateUpdate或者下一次FixedUpdate中

Unity的物理执行顺序保证:同一物理帧产生的所有OnCollisionEnter回调,会在本次FixedUpdate结束后、Update开始前全部执行完毕,延迟到后续阶段读取状态就不会读到中间态。


内容的提问来源于stack exchange,提问作者nowisbest

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 23:54:03