在Unity中绘制贝塞尔(Bezier)曲线

在Unity中绘制贝塞尔(Bezier)曲线

  • 1 效果
  • 2 公式
  • 3 绘制曲线
  • 4 编辑器扩展

作业有道题,让算出三个贝塞尔曲线的点,并画出草图。其实题目本身没有什么难度,就是带进去算一下就能出来了。不过还是想直接把他画出来。
在Unity中绘制贝塞尔(Bezier)曲线_第1张图片

1 效果

如果发现视频不是曲线的绘制的话(貌似在手机上就不是,电脑上是。两个视频时分p的),可以直接到 原视频 看。

2 公式

先根据老师PPT里面的公式,写出对应的函数。
在Unity中绘制贝塞尔(Bezier)曲线_第2张图片
贝塞尔曲线的函数

    public Vector2 Bezier (float t, int n, List<Vector2> cPoints) {
        Vector2 res = new Vector2 ();

        for (int i = 0; i <= n; i++) {
            float temp = BaseBezier (t, i, n);
            res.x += cPoints[i].x * temp;
            res.y += cPoints[i].y * temp;
        }

        return res;
    }

在Unity中绘制贝塞尔(Bezier)曲线_第3张图片
贝塞尔基函数

    public float BaseBezier (float t, int i, int n) {
        float res = Fa (n) * Mathf.Pow (t, i) * Mathf.Pow (1 - t, n - i) / (Fa (i) * Fa (n - i));
        return res;
    }

以及根据需要写了一个求阶乘的函数

// Factorial 阶乘
    public int Fa (int i) {
        int res = 1;
        for (int j = i; j > 1; j--) {
            res *= j;
        }
        return res;
    }

3 绘制曲线

大体的思想就是把曲线分成很多个小片段,然后用直线把这些线段连起来。

具体一点就是先通过划分多少份来计算t值,然后带进去公式算出来的点存到List里面,全部算完之后再通过Gizmos.DrawLine画出来。

直接放出完整的代码(还有B样条曲线的也加上了,不过B样条画出来总感觉怪怪的)

using System.Collections.Generic;
using UnityEngine;

public class DrawBezierAndB : MonoBehaviour {
    public bool autoUpdate = true; // 修改值时更新曲线
    [Tooltip ("次数")]
    public int n; // 次数
    public enum Type {
        Bezier,
        BSpline
    }
    public Type drawType = Type.Bezier; // 绘制曲线的方式
    public Color curveColor; // 曲线的颜色
    public List<Vector2> points = new List<Vector2> (); // 用于绘制的点
    [Range (1, 50)]
    public int part = 20; // 分段
    public List<Vector2> controlPoints = new List<Vector2> (); // 控制点

    public void Draw () {
        switch (drawType) {
            case Type.Bezier:
                CaculateBezier ();
                break;
            case Type.BSpline:
                CaculateBSpline ();
                break;
            default:
                break;
        }
    }
    private void OnDrawGizmos () {
        // 画控制点
        Gizmos.color = Color.red;
        if (controlPoints.Count > 0) {
            for (int i = 0; i < controlPoints.Count; i++) {
                Gizmos.DrawSphere (controlPoints[i], 0.1f);
            }
        }
        if (controlPoints.Count > 0) {
            for (int i = 0; i < controlPoints.Count - 1; i++) {
                Gizmos.DrawLine (controlPoints[i], controlPoints[i + 1]);
            }
        }

        // 画曲线
        Gizmos.color = curveColor;
        if (points.Count > 0) {
            for (int i = 0; i < points.Count - 1; i++) {
                Gizmos.DrawLine (points[i], points[i + 1]);
            }
        }
    }

    public void CaculateBSpline () {
        points.Clear ();
        for (int i = 0; i <= part; i++) {
            points.Add (BSpline (i / (float) part, n, controlPoints));
        }
    }
    public void CaculateBezier () {
        points.Clear ();
        for (int i = 0; i <= part; i++) {
            points.Add (Bezier (i / (float) part, n, controlPoints));
        }
    }

    // 贝塞尔曲线
    public Vector2 Bezier (float t, int n, List<Vector2> cPoints) {
        Vector2 res = new Vector2 ();

        for (int i = 0; i <= n; i++) {
            float temp = BaseBezier (t, i, n);
            res.x += cPoints[i].x * temp;
            res.y += cPoints[i].y * temp;
        }

        return res;
    }
    // B 样条曲线
    public Vector2 BSpline (float t, int n, List<Vector2> cPoints) {
        Vector2 res = new Vector2 ();

        for (int l = 0; l <= n; l++) {
            float temp = BaseBSpline (t, l, n);
            res.x += cPoints[l].x * temp;
            res.y += cPoints[l].y * temp;
        }

        return res;
    }

    // B样条基函数
    public float BaseBSpline (float t, int l, int n) {
        float res = 0;
        for (int j = 0; j <= n - l; j++) {
            res += Mathf.Pow (-1, j) *
                Mathf.Pow (t + n - l - j, n) *
                Fa (n + 1) / (Fa (j) * Fa (n + 1 - j));
        }
        return res / Fa (n);
    }

    // 贝塞尔基函数
    public float BaseBezier (float t, int i, int n) {
        float res = Fa (n) * Mathf.Pow (t, i) * Mathf.Pow (1 - t, n - i) / (Fa (i) * Fa (n - i));
        return res;
    }

    // Factorial 阶乘
    public int Fa (int i) {
        int res = 1;
        for (int j = i; j > 1; j--) {
            res *= j;
        }
        return res;
    }
}

4 编辑器扩展

为了方便在编辑器里面修改控制点,对编辑器进行了一些扩展,不需要播放就直接能画了。

using UnityEditor;
using UnityEngine;

[CustomEditor (typeof (DrawBezierAndB))]
public class DrawBezierAndBEditor : Editor {

    DrawBezierAndB drawBAB;

    void OnEnable () {
        //获取当前编辑自定义Inspector的对象
        drawBAB = (DrawBezierAndB) target;
    }
    public override void OnInspectorGUI () {
        // 如果面板上有值改变返回值true
        if (DrawDefaultInspector ()) {
            // 确保次数要小于控制点的个数
            if (drawBAB.n > drawBAB.controlPoints.Count - 1) {
                drawBAB.n = drawBAB.controlPoints.Count - 1;
                Debug.LogWarning ("次数要小于控制点的个数");
            }

            // 如果是改变值时也生成就生成
            if (drawBAB.autoUpdate) {
                drawBAB.Draw ();
            }
        }
        // 点下Generate按钮就生成
        if (GUILayout.Button ("Generate")) {
            drawBAB.Draw ();
        }
    }
}

你可能感兴趣的:(Unity)