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

Unity中如何让自定义移动的Kinematic Rigidbody2D遵守碰撞?

解决方案与问题解析

先纠正你对Kinematic Rigidbody2D的理解

Kinematic刚体的核心逻辑是完全由代码控制运动,Unity物理引擎不会自动对它施加碰撞响应——哪怕开启了use full kinematic contact,也只是让它能触发OnCollisionEnter2D这类碰撞事件,不会自动调整速度或位置来避免穿模。你看到的"忽略Static碰撞、速度不受影响"是正常行为,碰撞检测仅用于告知你"发生了碰撞",修正逻辑需要自己写。

推荐方案:用Dynamic Rigidbody2D实现自定义移动+物理碰撞防穿模

这是最省心的方案,既保留物理引擎的碰撞防穿模能力,又能完全自定义移动逻辑:

  1. Rigidbody2D设置:
    • 类型改为Dynamic
    • 开启Constraints里的Freeze Rotation(避免角色旋转)
    • 把Gravity Scale设为0(关闭内置重力,用自己的逻辑控制)
  2. 代码逻辑调整:
    不要直接硬改velocity(容易和物理引擎的碰撞响应冲突),改用AddForce或者在修正速度时结合物理状态,示例代码如下(优化你的测试逻辑):
    using UnityEngine;
    
    public class MovementTest : MonoBehaviour
    {
        Rigidbody2D rb;
        public float maxFallSpeed = 10f;
        public float gravityAccel = 1f;
    
        void Start()
        {
            rb = GetComponent<Rigidbody2D>();
            rb.gravityScale = 0;
            rb.constraints = RigidbodyConstraints2D.FreezeRotation;
        }
    
        void FixedUpdate()
        {
            // 自定义重力逻辑,限制最大下落速度
            if (rb.velocity.y > -maxFallSpeed)
            {
                rb.AddForce(Vector2.down * gravityAccel, ForceMode2D.Force);
            }
    
            // 这里可以添加左右移动逻辑,比如:
            // float horizontalInput = Input.GetAxis("Horizontal");
            // rb.AddForce(Vector2.right * horizontalInput * moveForce, ForceMode2D.Force);
            // 或直接控制velocity.x(需注意碰撞后的速度修正)
        }
    }
    
    这种方式下,当角色碰撞到Static刚体时,物理引擎会自动修正速度和位置,不会出现穿模或卡住的问题,同时你完全掌控移动逻辑。

备选方案:继续用Kinematic,但手动实现碰撞修正

如果坚持用Kinematic,必须提前检测移动路径并修正位置,避免穿模:

  • 用Physics2D.BoxCast或Rigidbody2D.Cast替代单点Raycast,检测角色移动方向上的碰撞
  • 在FixedUpdate中,先计算本次想要移动的位移,再用Cast检测该路径上是否有碰撞体
  • 如果检测到碰撞,将位移截断到碰撞点的位置,再执行移动

示例逻辑伪代码:

void FixedUpdate()
{
    // 计算想要移动的位移
    Vector2 moveDelta = Vector2.down * accel * Time.fixedDeltaTime;
    // 用BoxCast检测位移路径
    RaycastHit2D hit = Physics2D.BoxCast(transform.position, GetComponent<Collider2D>().bounds.size, 0f, moveDelta.normalized, moveDelta.magnitude, LayerMask.GetMask("Ground"));
    
    if (hit)
    {
        // 碰撞到地面,只移动到碰撞点前的位置
        transform.Translate(moveDelta.normalized * hit.distance);
        // 重置下落速度
        rb.velocity = new Vector2(rb.velocity.x, 0);
    }
    else
    {
        // 无碰撞,正常移动
        transform.Translate(moveDelta);
        // 或更新velocity(Kinematic下velocity仅用于状态记录)
        rb.velocity = moveDelta / Time.fixedDeltaTime;
    }
}

同时优化地面检测:用多个Raycast(角色底部左右+中间)或者BoxCast,避免单点检测的不稳定导致的穿模/卡住。

不推荐:完全自定义碰撞系统

除非有特殊需求(比如极端性能优化或自定义物理规则),否则不要自己写完整的碰撞系统——需要处理碰撞形状、分层、重叠检测、响应逻辑等大量细节,容易出现各种bug,效率远不如Unity内置物理引擎。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 01:20:59