关于贝塞尔曲线在游戏开发中的应用

发表于2019-01-25
评论0 5.5k浏览
贝塞尔曲线用到的地方太多了,unity里的曲线编辑器基本都是贝塞尔曲线,还有photoshop里的钢笔路径等等,那我们自己平时开发的时候能不能用上这个东西呢?答案是肯定的,比如各种动画,移动轨迹,还有一些漂亮的几何图形我们都可以使用它来完成,那我们我们来尝试自己应用一下这个神奇的曲线吧

贝塞尔曲线有好几种,最常用的是3次方的公式,就是由p0-3的4个点来确定的一条曲线,

看一个效果:

首先看一下公式,来自百度百科:

公式看着很简单,只有一个变量t,我们可以把它看成从起点到终点的距离的百分比/100.

来看一下把公式放到c#中的样子:
    public static float GetBezierat(float a, float b, float c, float d, float t)
    {
        return (Mathf.Pow(1 - t, 3) * a +
                3 * t * (Mathf.Pow(1 - t, 2)) * b +
                3 * Mathf.Pow(t, 2) * (1 - t) * c +
                Mathf.Pow(t, 3) * d);
    }

现在我们尝试用这个公式在游戏中绘制一条贝塞尔曲线

创建测试脚本BezierTest,并放到游戏场景中。

定义一个数组,用来存储曲线的控制点
public List<GameObject> obj;

在编辑器中创建四个点作为曲线的控制点。然后放入到测试脚本的数组中。


然后实现一个方法,作用是输入4个坐标点,获取有这些点生成的曲线上的关键点,并存储到坐标列表中,用来绘制曲线。
    public List<Vector3> poslist = new List<Vector3>();
    void CreateLinePoint(Vector3[] vlist)
    {
        float rate = 0;
        while (rate < 1)
        {
            rate += 1f / step;
            float x = GetBezierat(
                vlist[0].x,
                vlist[1].x,
                vlist[2].x,
                vlist[3].x, rate);
            float y = GetBezierat(
                vlist[0].y,
                vlist[1].y,
                vlist[2].y,
                vlist[3].y, rate);
            poslist.Add(new Vector3(x, y, 0));
        }
    }

注意在实际的使用过程中,我们需要分别计算x和y坐标。然后在Start方法中找到四个物体并把坐标点传入CreateLinePoint方法中:
	void Start () {
        Vector3[] vlist = new Vector3[4];
        for (int i = 0; i < obj.Count; i ++){
            vlist[i] = (obj[i].transform.position);
        }
        CreateLinePoint(vlist);
	} 

最后按照关键点绘制直线即可:
    public void OnDrawGizmos()
    {
#if UNITY_EDITOR
        Gizmos.color = Color.blue;
        Handles.color = Color.blue;
        for (int i = 0; i < poslist.Count; i++)
        {
            if (i == poslist.Count - 1) continue;
            Gizmos.DrawLine(poslist[i], poslist[i + 1]);
        }
#endif
    }

这里需要引入命名空间:
#if UNITY_EDITOR
using UnityEditor;
#endif

看下效果:

我们可以看到曲线只会经过第1和第4个点,要想让曲线经过每个点的话,我们可以使用每2个相邻的点作为起点和终点,然后计算出2个辅助点的位置,这里我们使用最简单的办法,即把起始点的坐标向后平移最为第一个辅助点,然后把终点左标向前平移作为第二个辅助点,如下图:

现在来修改我们的代码,把Start中的代码改成这样:
void Start () {
        for (int i = 0; i < obj.Count; i++)
        {
            Vector3 v1 = obj[i].transform.position;
            int lastindex = Mathf.Min(i + 1, obj.Count - 1);
            Vector3 v4 = obj[lastindex].transform.position;
            Vector3 v2 = new Vector3(v1.x + (v4.x - v1.x) * 0.5f, v1.y, 0);
            Vector3 v3 = new Vector3(v4.x - (v4.x - v1.x) * 0.5f, v4.y, 0);
            Vector3[] vlist = new Vector3[4];
            vlist[0] = v1;
            vlist[1] = v2;
            vlist[2] = v3;
            vlist[3] = v4;
            CreateLinePoint(vlist);
        }
	}

看看效果:

这样我们可以向数组中加入任意多的点都可以了,像这样:

曲线的应用就是这样,不是很复杂,但是想要使用它做出很多酷炫的效果就要靠大家的想象力了。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引