今天偶然法现了一个有趣的工程,蚂蚁行军,发哥之前在博客上讲过,然后我今天突然想到了贝塞尔曲线这个数学公式,很有趣的数学公式,之前应用在2维的平面上, 现在在Unity游戏行业中打出的特效或者是子弹很多都应用到了贝塞尔曲线让我们的游戏更加的有趣。

先从简到难

看一下这一条线 你能想到什么? 

很明显就是一个简单的直线甚至我只需要用我的画图工具就能拖1个 

但是这个 却又是贝塞尔的基础 也就是我们的一阶贝塞尔曲线 

我们先设计一个栗子,一个人从p1到p2 他的位置变化是什么样子的用数学公式可以表达为

B 为小明 t  可以去当参数 代表他走了多远  P1 起始点 p2 目标点

那么如果他是一直动就可以看为

B=  B(t)

= P1+(P2-P1)t

= (1-t)*p1+p2*t   

我们的当前的位置 就是起始点到目标的过程 ,当前位置就是起始点加上目标点减去起始点的距离*t的百分比 t=0-1

这个要是不理解就是去带入一个数值就可以了 

这个就是基础的贝塞尔

那么二阶就是比较常见的场景比如你丢一个手雷,这个手雷要模仿重力去在空中有一个弧度

那么这个又该如何理解他明明是一个直线为啥能够成为曲线呢?

这个就是可以理解为你的数量越多他的弧度越大也就越精准

之后就是我们的弧度了 

我们可以看做P1到P2是一个一阶贝塞尔 p2到p3同理

我们看到弧线链接点

设置为M和N 

我们可以想象在 M= P1到P2是一个一阶贝塞尔

N= p2到p3一个一阶贝塞尔

这个公式就可以设置为

 B(t)=M(1-t)+Nt  (到这个步骤你就可以了)

=((1-t)*p1+p2*t)  *(1-t)+((1-t)*p2+p3*t)*t

=p1(1-t)*p1(1-t)+ 2P2t(1-t)+p3(t*t )

 就是这个原理

往后推到也是一样 

附赠Unity代码

   private Vector3 cubicBezier(float t)//二阶
   {
       Debug.Log("cubicBezier");
       Vector3 a = points[0].position;
       Vector3 b = points[1].position;
       Vector3 c = points[2].position;

       var M = a * (1 - t) + b * t;
       var N = b * (1 - t) + c * t;

       return M * (1 - t) + N * t;
   }

    private Vector3 quardaticBezier(float t)//三阶
    {
        Debug.Log("quardaticBezier");
        Vector3 a = points[0].position;
        Vector3 b = points[1].position;
        Vector3 c = points[2].position;
        Vector3 d = points[3].position;

        var M = a * (1 - t) + b * t;
        var N = b * (1 - t) + c * t;
        var T = c * (1 - t) + d * t;

        var P = M * (1 - t) + N * t;
        var P1 = N * (1 - t) + T * t;

        return P * (1 - t) + P1 * t;
    }

整体代码

using System.Xml.Linq;
using UnityEngine;

public class BerSaier : MonoBehaviour
{
    public Transform[] points;
    public LineRenderer lineRenderer;
    public Camera main;
    // Start is called before the first frame update
    Transform targetTrans;
    public int accuracy = 20;
    void Start()
    {
        lineRenderer.positionCount = accuracy + 1;
    }

    // Update is called once per frame
    void Update()
    {
        // 绘制贝塞尔曲线
        Vector3 prev_pos = points[0].position;
        for (int i = 0; i <= accuracy; ++i)
        {
            Vector3 to = formula(i / (float)accuracy);
            lineRenderer.SetPosition(i, to);
            Debug.DrawLine(prev_pos, to);
            prev_pos = to;
        }

        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.collider.CompareTag("Dot"))
                {
                    // 点中了控制点
                    targetTrans = hit.transform;
                }
            }
        }
        if (targetTrans != null && Input.GetMouseButton(0))
        {

            Vector3 targetScreenPos = Camera.main.WorldToScreenPoint(targetTrans.position);
            Vector3 mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, targetScreenPos.z);
            targetTrans.position = Camera.main.ScreenToWorldPoint(mousePos);




        }
    }
    /// <summary>
    /// 贝塞尔时间公式(二阶、三阶)
    /// </summary>
    /// <param name="t">时间参数,范围0~1</param>
    /// <returns></returns>
    public Vector3 formula(float t)
    {
        switch (points.Length)
        {
            case 3: return cubicBezier(t);
            case 4: return quardaticBezier(t);
        }
        return Vector3.zero;
    }
    public Vector3 lineBezier(float t)
    {
        Vector3 a = points[0].position;
        Vector3 b = points[1].position;
        return a * (1 - t) + b * t;
    }

    private Vector3 cubicBezier(float t)
    {
        Debug.Log("cubicBezier");
        Vector3 a = points[0].position;
        Vector3 b = points[1].position;
        Vector3 c = points[2].position;

        var M = a * (1 - t) + b * t;
        var N = b * (1 - t) + c * t;

        return M * (1 - t) + N * t;
    }

    private Vector3 quardaticBezier(float t)
    {
        Debug.Log("quardaticBezier");
        Vector3 a = points[0].position;
        Vector3 b = points[1].position;
        Vector3 c = points[2].position;
        Vector3 d = points[3].position;

        var M = a * (1 - t) + b * t;
        var N = b * (1 - t) + c * t;
        var T = c * (1 - t) + d * t;

        var P = M * (1 - t) + N * t;
        var P1 = N * (1 - t) + T * t;

        return P * (1 - t) + P1 * t;
    }


    //private void OnDrawGizmos()
    //{
    //    Debug.Log("画线");
    //    Debug.DrawLine(rargets[0].position, rargets[1].position);
    //}
}

最后效果

如果你的相机是透视模式的话就用

这个坐标转换 

如果是正交模式的话就用

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐