1. 程式人生 > >unity 實現物體沿指定的平滑曲線移動(通過貝塞爾曲線實現)

unity 實現物體沿指定的平滑曲線移動(通過貝塞爾曲線實現)

在實際專案開發中,為了實現某種動畫或者特效,策劃都會要求讓物體實現沿編輯的軌跡進行移動,今天這裡就講一下如何讓物體沿可編輯的路線進行移動,這裡主要是通過貝塞爾曲線實現。

首先要了解貝塞爾曲線的基礎知識及原理,具體可參考改連結:

這裡的思路就是首先就是把關鍵節點儲存起來,然後在通過貝塞爾的計算公式計算出兩個節點之間的擬合點的集合,最後可以控制小球在運動的過程中通過走過的路徑的長度作為下標,取出該長度下對應的座標資訊,從而實現小球沿規定路徑移動。

下面是具體的程式碼實現:

首先是計算出指定軌跡的擬合點,儲存到list中,就是隻在初始化的時候計算出來,然後儲存,後面可以一直沿用,如果是在執行邊計算邊使用計算量會偏大:

    #region 計算貝塞爾曲線的擬合點
    //初始化算出所有的點的資訊
    void InitPoint()
    {
        //獲取指定的點的資訊
        Vector3[] pointPos = new Vector3[basePoint.Length];
        for (int i = 0; i < pointPos.Length; i++)
        {
            pointPos[i] = basePoint[i].transform.position;
        }
        GetTrackPoint(pointPos);
    }

    /// <summary>
    /// 根據設定節點 繪製指定的曲線
    /// </summary>
    /// <param name="track">所有指定節點的資訊</param>
    public void GetTrackPoint(Vector3[] track)
    {
        Vector3[] vector3s = PathControlPointGenerator(track);
        int SmoothAmount = track.Length * baseCount;
        lineRender.positionCount = SmoothAmount;
        for (int i = 1; i < SmoothAmount; i++)
        {
            float pm = (float)i / SmoothAmount;
            Vector3 currPt = Interp(vector3s, pm);
            lineRender.SetPosition(i, currPt);
            lsPoint.Add(currPt);
        }
    }

    /// <summary>
    /// 計算所有節點以及控制點座標
    /// </summary>
    /// <param name="path">所有節點的儲存陣列</param>
    /// <returns></returns>
    public Vector3[] PathControlPointGenerator(Vector3[] path)
    {
        Vector3[] suppliedPath;
        Vector3[] vector3s;

        suppliedPath = path;
        int offset = 2;
        vector3s = new Vector3[suppliedPath.Length + offset];
        Array.Copy(suppliedPath, 0, vector3s, 1, suppliedPath.Length);
        vector3s[0] = vector3s[1] + (vector3s[1] - vector3s[2]);
        vector3s[vector3s.Length - 1] = vector3s[vector3s.Length - 2] + (vector3s[vector3s.Length - 2] - vector3s[vector3s.Length - 3]);
        if (vector3s[1] == vector3s[vector3s.Length - 2])
        {
            Vector3[] tmpLoopSpline = new Vector3[vector3s.Length];
            Array.Copy(vector3s, tmpLoopSpline, vector3s.Length);
            tmpLoopSpline[0] = tmpLoopSpline[tmpLoopSpline.Length - 3];
            tmpLoopSpline[tmpLoopSpline.Length - 1] = tmpLoopSpline[2];
            vector3s = new Vector3[tmpLoopSpline.Length];
            Array.Copy(tmpLoopSpline, vector3s, tmpLoopSpline.Length);
        }
        return (vector3s);
    }


    /// <summary>
    /// 計算曲線的任意點的位置
    /// </summary>
    /// <param name="pos"></param>
    /// <param name="t"></param>
    /// <returns></returns>
    public Vector3 Interp(Vector3[] pos, float t)
    {
        int length = pos.Length - 3;
        int currPt = Mathf.Min(Mathf.FloorToInt(t * length), length - 1);
        float u = t * (float)length - (float)currPt;
        Vector3 a = pos[currPt];
        Vector3 b = pos[currPt + 1];
        Vector3 c = pos[currPt + 2];
        Vector3 d = pos[currPt + 3];
        return .5f * (
           (-a + 3f * b - 3f * c + d) * (u * u * u)
           + (2f * a - 5f * b + 4f * c - d) * (u * u)
           + (-a + c) * u
           + 2f * b
       );
    }
    #endregion

然後就是通過在update中移動小球,根據小球軌跡長度取出對應長度下的座標資訊賦值:

    void Start()
    {
        lineRender = gameObject.GetComponent<LineRenderer>();
        InitPoint();
    }

    void Update()
    {
        length += Time.deltaTime * speed;
        if (length >= lsPoint.Count - 1)
        {
            length = lsPoint.Count - 1;
        }
        sphere.transform.localPosition = lsPoint[(int)(length)];
    }

上述就是小球沿指定軌跡的具體思路。

工程連結:連結:https://pan.baidu.com/s/1h4FQNI1A9992k7T29J2GqQ 密碼:mql1

想了解更多unity開發相關資訊,可以關注下方公眾號或者新增QQ交流群879354767,來分享學習遊戲中的開發心得: