2d物理引起学习 - 圆与圆碰撞信息计算

发布时间 2023-12-24 23:24:55作者: yanghui01

碰撞情况分析

球1向上运动,球2向下运动

1) 如果它们速度不快,碰撞的瞬间,碰撞点可能就在球的边缘上(红色点为碰撞点,绿色箭头为碰撞法向量)

2) 如果它们速度很快,碰撞的瞬间,两球可能就发生重叠(穿插),碰撞点就到球的里面去了

在物理引擎中,需要用到的碰撞信息包括:

1) 碰撞点。主要用于旋转运动相关的计算。

2) 分离向量(即:碰撞法向量+穿透深度),沿着分离向量可以将两物体分开

 

计算

1) 碰撞法向量为两圆圆心连线方向

2) 穿透深度为两圆的半径和减去两圆圆心距离

3) 碰撞点在穿透深度的中点处

 

using UnityEditor;
using UnityEngine;

public class MyCricleContact : MonoBehaviour
{
    public MyCircle m_C1;
    public MyCircle m_C2;

    public Vector2 m_C1MoveDir = Vector2.up; //展示圆1移动方向
    public Vector2 m_C2MoveDir = Vector2.down; //展示圆2移动方向

    public Vector2 m_Normal; //碰撞法向量
    public float m_Penetration; //穿透深度
    public Vector2 m_Point; //碰撞点

#if UNITY_EDITOR

    private void OnDrawGizmos()
    {
        if (m_C1 && m_C2)
        {
            var c1Trans = m_C1.transform;
            var c2Trans = m_C2.transform;

            Gizmos.color = Color.gray;
            Gizmos.DrawLine(c1Trans.position, c2Trans.position);

            Gizmos.color = Color.blue;
            DrawArrowLine(c1Trans.position, m_C1MoveDir, m_C1.m_Radius * 0.6f, 0.03f); //c1运动方向
            DrawArrowLine(c2Trans.position, m_C2MoveDir, m_C2.m_Radius * 0.6f, 0.03f); //c2运动方向

            float totalR = (m_C1.m_Radius + m_C2.m_Radius);
            var circleDistVec = c2Trans.position - c1Trans.position; //圆心距离向量
            m_Penetration = totalR - circleDistVec.magnitude; //穿透深度
            if (m_Penetration > 0) //发生碰撞了
            {
                m_Normal = circleDistVec.normalized; //碰撞法线为圆心连线方向
                m_Point = (Vector2)c1Trans.position + m_Normal * (m_C1.m_Radius - m_Penetration * 0.5f); //碰撞点在穿透向量中点处

                Gizmos.color = Color.green;
                DrawArrowLine(m_Point, m_Normal, 0.15f, 0.03f); //碰撞法线方向

                Handles.color = Color.red;
                Handles.DrawSolidDisc(m_Point, Vector3.back, 0.018f); //碰撞点
            }
            else
            {
                m_Penetration = 0;
                m_Normal = Vector2.zero;
                m_Point = Vector2.zero;
            }

            Handles.color = Color.white;
            Gizmos.color = Color.white;
        }
    }

    public static void DrawArrowLine(Vector2 start, Vector2 dir, float len, float size = 0.2f)
    {
        var end = start + dir * len;
        Gizmos.DrawLine(start, end);

        Vector2 arrowLineDirCCW = Quaternion.Euler(0, 0, 150) * dir * size;
        Gizmos.DrawLine(end, end + arrowLineDirCCW);
        Vector2 arrowLineDirCW = Quaternion.Euler(0, 0, -150) * dir * size;
        Gizmos.DrawLine(end, end + arrowLineDirCW);
    }

#endif


}

 

using UnityEngine;

public class MyCircle : MonoBehaviour
{
    protected MyRigidbody m_Rigidbody; //关联的刚体

    public float m_Radius;

    public MyCircle() {}

    public MyCircle(float r)
    {
        m_Radius = r;
    }

#if UNITY_EDITOR
    public bool m_ShowRadius;

    private void OnDrawGizmos()
    {
        var trans = this.transform;
        DrawCircle(trans.position, m_Radius);
        if (m_ShowRadius)
            Gizmos.DrawLine(trans.position, trans.TransformPoint(Vector3.right * m_Radius));
    }

    public static void DrawCircle(Vector2 c, float r)
    {
        int segmentCount = 30;
        float radDelta = Mathf.PI * 2 / segmentCount;
        float rad = 0;
        var lastPoint = new Vector2(r * Mathf.Cos(rad), r * Mathf.Sin(rad));
        for (int i = 1; i <= segmentCount; ++i)
        {
            rad += radDelta;
            var curPoint = new Vector2(r * Mathf.Cos(rad), r * Mathf.Sin(rad));
            Gizmos.DrawLine(c + lastPoint, c + curPoint);
            lastPoint = curPoint;
        }
    }

#endif

}