You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity实例化预制体碰撞无响应问题求助

碰撞检测失效问题:实例化预制体无法触发碰撞

我编写了一个碰撞测试脚本,普通GameObject与挂载该脚本的对象碰撞时,能正常输出“collision registered”日志,但使用实例化的预制体(挂载有Moveable脚本)碰撞时,完全检测不到碰撞。推测问题出在Moveable脚本,但无法定位具体原因。


相关代码

碰撞测试脚本

private void OnCollisionEnter(Collision collision)
{
        Debug.Log("collision registered");
}

预制体挂载的Moveable脚本

public class Moveable : MonoBehaviour
{
    public float _speedMetersPerSecond = 50f;
    public float resistance = 10f;
    public float current = 0f;
    public List<Moveable> moveables = new List<Moveable>();
    private Vector3? _destination;
    private Vector3 _startPosition;
    private float _totalLerpDuration;
    private float _elapsedLerpDuration;
    private Action _onCompleteCallback;
    public GameObject Electron;
    public Transform Lightbulb;
    public SliderChange SliderScript;
    public VMT2Counter script2;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // 灯泡位置为生成点,请勿删除
            Moveable NextOnPath = Instantiate(Electron, Lightbulb.position, Quaternion.identity).GetComponent<Moveable>();
            moveables.Add(NextOnPath.GetComponent<Moveable>());
            MoverController.instance.targets.Add(NextOnPath.GetComponent<Moveable>());
        }

        if (_destination.HasValue == false)
            return;

        if (_elapsedLerpDuration >= _totalLerpDuration && _totalLerpDuration > 0)
            return;

        _elapsedLerpDuration += Time.deltaTime;
        float percent = (_elapsedLerpDuration / _totalLerpDuration);

        transform.position = Vector3.Lerp(_startPosition, _destination.Value, percent);

        if (_elapsedLerpDuration >= _totalLerpDuration)
            _onCompleteCallback?.Invoke();

        // resistance = SliderScript.slider.value;
        // current = script2.PotentialDifference / resistance;

    }

    public void MoveTo(Vector3 destination, Action onComplete = null)
    {
        var distanceToNextWaypoint = Vector3.Distance(transform.position, destination);
        _totalLerpDuration = distanceToNextWaypoint / _speedMetersPerSecond;

        _startPosition = transform.position;
        _destination = destination;
        _elapsedLerpDuration = 0f;
        _onCompleteCallback = onComplete;
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.name == "Resistor")
        { 
        Debug.Log("lol");
        _speedMetersPerSecond = 25f;
        }
    }
}

MoverController脚本(未挂载到预制体)

public class MoverController : MonoBehaviour
{
    public List<Moveable> targets;
    [SerializeField] private Moveable target;
    private List<Transform> _waypoints;
    private int _nextWaypointIndex;
    public static MoverController instance;

    void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
        else
        {
            Destroy(gameObject);
            return;
        }
        DontDestroyOnLoad(gameObject);
    }

    private void OnEnable()
    {
        MoveToNextWaypoint();
    }

    private void MoveToNextWaypoint()
    {
        for (int i = 0; i < targets.Count; i++)
        {
            if (targets[i] == null) continue;
            _waypoints = GetComponentsInChildren<Transform>().ToList();
            _waypoints.RemoveAt(0);
            var targetWaypointTransform = _waypoints[_nextWaypointIndex];
            targets[i].MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
            targets[i].transform.LookAt(_waypoints[_nextWaypointIndex].position);
            _nextWaypointIndex++;

            if (_nextWaypointIndex >= _waypoints.Count)
                _nextWaypointIndex = 0;
        }
    }
}

预制体截图

预制体截图


问题排查与解决方案

核心原因:直接修改transform.position绕过物理引擎

Moveable脚本的Update中使用transform.position = Vector3.Lerp(...)直接设置物体位置,这种瞬移式的位置修改会跳过Unity物理引擎的运动跟踪,导致碰撞检测系统无法识别物体的运动状态,自然无法触发碰撞事件。

修复步骤

  1. 改用Rigidbody控制位移

    • 给预制体添加Rigidbody组件,若不需要物理受力可勾选Is Kinematic;
    • 在Moveable脚本中添加Rigidbody引用并初始化:
      private Rigidbody _rigidbody;
      
      void Awake()
      {
          _rigidbody = GetComponent<Rigidbody>();
      }
      
    • 替换Update中的位移代码:
      // 替换原来的transform.position = ...
      Vector3 targetPos = Vector3.Lerp(_startPosition, _destination.Value, percent);
      _rigidbody.MovePosition(targetPos);
      
  2. 检查碰撞体配置

    • 确保预制体和碰撞对象都有碰撞体组件(如BoxCollider、SphereCollider);
    • 若使用OnCollisionEnter,请勿勾选碰撞体的Is Trigger(若用OnTriggerEnter则需要勾选);
    • 确认实例化后的预制体碰撞体未被禁用或删除。
  3. 优化MoverController逻辑

    • 将_waypoints的初始化移至Awake,避免每次循环重复获取:
      void Awake()
      {
          // 原有单例代码...
          _waypoints = GetComponentsInChildren<Transform>().ToList();
          _waypoints.RemoveAt(0);
      }
      
  4. 验证碰撞有效性

    • 可先给实例化的预制体添加非运动学Rigidbody,让其自由下落,测试是否能触发碰撞,排除其他配置问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 00:52:40