一個貝塞爾曲線編輯工具(2d)

yanghui01發表於2024-07-28

曲線在unity下如何繪製?

類似繪製圓,是用一段一段的線段拼接來模擬的,這邊也是類似,可以用一段一段的線段來模擬曲線。

既然要模擬,那我們也得知道貝塞爾曲線的公式才行。

一般用的比較多的就是3次貝塞爾曲線,該曲線由起點p1,p1的控制點c1,終點p2,p2的控制點c2組成。

一個貝塞爾曲線編輯工具(2d)

公式為:p = p1*(1-t)3 + 3*c1*t*(1-t)2 + 3*c2*t2*(1-t) + p2*t3,t的範圍[0, 1]

下面是unity下的一個貝塞爾編輯工具

public class MyBezierPath2dEdit : MonoBehaviour
{

    public string m_ControlInObjectName = "In"; //作為曲線結束時, c2控制點
    public string m_ControlOutObjectName = "Out"; //作為曲線啟示點時, c1控制點
    public Color m_ControlInColor = Color.red; //c2控制點連線顏色
    public Color m_ControlOutColor = Color.blue; //c1點連線顏色

    public Color m_PathColor = Color.yellow; //路徑繪製顏色

    [Range(1, 60)]
    public int m_Steps = 30; //曲線用多少個直線來模擬
    public bool m_LoopPath = false; //曲線是否首尾連線

    private void OnDrawGizmos()
    {
        var mainPoints = new List<Vector2>();
        var In = new List<Vector2>();
        var Out = new List<Vector2>();
        bool isAddRectTransform = null != GetComponent<RectTransform>();

        foreach (Transform child in transform)
        {
            mainPoints.Add(child.transform.position);
            foreach (Transform child2 in child.transform)
            {
                if (child2.name == m_ControlInObjectName)
                    In.Add(child2.transform.position);
                if (child2.name == m_ControlOutObjectName)
                    Out.Add(child2.transform.position);
            }

            //如果沒有定義的點,則補充上去
            if (In.Count < mainPoints.Count)
            {
                GameObject go = new GameObject(m_ControlInObjectName);
                if (isAddRectTransform)
                    go.AddComponent<RectTransform>();
                go.transform.SetParent(child.transform, false);
                In.Add(go.transform.position);
            }
            if (Out.Count < mainPoints.Count)
            {
                GameObject go = new GameObject(m_ControlOutObjectName);
                if (isAddRectTransform)
                    go.AddComponent<RectTransform>();
                go.transform.SetParent(child.transform, false);
                Out.Add(go.transform.position);
            }
        }

        //曲線控制線
        for (int i = 0; i < mainPoints.Count; i++)
        {
            Gizmos.color = m_ControlInColor;
            Gizmos.DrawLine(mainPoints[i], In[i]);
            Gizmos.color = m_ControlOutColor;
            Gizmos.DrawLine(mainPoints[i], Out[i]);
        }

        Gizmos.color = m_PathColor;
        if (In.Count >= mainPoints.Count && Out.Count >= mainPoints.Count)
        {
            int mainPtCnt = mainPoints.Count;
            if (!m_LoopPath) mainPtCnt--;
            for (int i = 0; i < mainPtCnt; i++)
            {
                int i2 = (i + 1) % mainPoints.Count;
                Vector3 P2 = new Vector3(0, 0, 0);
                float step = 1.0f / m_Steps;
                if (step > 0.01f)
                {
                    for (float t = 0; t < 1 + step; t += step)
                    {
                        Vector3 P1 = P2;
                        P2 = CalcBezierPathPoint(mainPoints[i], Out[i], In[i2], mainPoints[i2], t);
                        if (t > 0)
                        {
                            Gizmos.DrawLine(P1, P2);
                        }
                    }
                }
            }
        }
    }

    private Vector3 CalcBezierPathPoint(Vector3 P0, Vector3 C0, Vector3 C1, Vector3 P1, float t)
    {
        float temp = 1 - t;
        Vector2 result = temp * temp * temp * P0 + 3 * temp * temp * t * C0 + 3 * temp * t * t * C1 + t * t * t * P1;
        return result;
    }

}

相關文章