周小結2016/8/22-8/28_Bezier

貝塞爾曲線: 

        項目要求讓角色沿着幾個點做平滑的移動,研究了下貝塞爾曲線,主要是求連續點的一個算法,知道公式的話,寫成代碼還是比較簡單的,先從簡單的入手,方便理解,我們先從倆個點入手,讓對象從一個點到另一個點移動,先看代碼

using System;
using UnityEngine;
using System.Collections;

[Serializable]
public class Bezier
{
    public Vector3 p0;
    public Vector3 p1;
    public Bezier(Vector3 v0, Vector3 v1)
    {
        this.p0 = v0;
        this.p1 = v1;
    }
    public Vector3 GetPointAtTime(float t)
    {
        Vector3 tmpVector3=Vector3.zero;
        tmpVector3 = (1 - t) * p0 + t * p1;
        return tmpVector3;
    }
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MyBezier : MonoBehaviour
{
    private Bezier myBezier;
    public Transform startTransform;//起始位置
    public Transform endTransform;  //終點位置
    private List<Vector3> pothList; //存放中間點
    private int index = 0;
    void Start()
    {
        pothList=new List<Vector3>();
        myBezier = new Bezier(startTransform.position, endTransform.position);
        //獲取2個點中間的100個路徑點
        for (int i = 0; i < 100; i++)
        {
            Vector3 tmpVector3 = myBezier.GetPointAtTime(0.01f*i);
            pothList.Add(tmpVector3);
        }
    }
    void FixedUpdate()
    {
        int leng = pothList.Count;
        if (leng > 0 && index < leng)
        {
            transform.position = pothList[index];
            index++;
        }
    }
}

這裏就用到了貝塞爾曲線的公式

一階貝塞爾曲線   B(t)=(1-t)P0 + t P1   (t的取值範圍是0--1)

如果想讓對象平滑的移動,可以將上面的代碼用lerp實現

二階貝塞爾曲線  B(t)=(1-t)2P0+2t(1-t)P1+t2P2,t∈[0,1]

三階貝塞爾曲線 B(t)=(1-t)3P0+3t(1-t)2P1+3t2(1-t)P2+t3P3,t∈[0,1]

四階貝塞爾曲線 B(t)=(1-t)4P0+4t(1-t)3P1+4t2(1-t)2P2+4t3(1-t)P3+t4P4,t∈[0,1]

依次類推

例如三階的代碼爲了更方便理解,更加直觀,可以寫成下面的形式

using System;
using UnityEngine;
using System.Collections;

[Serializable]
public class Bezier
{
    public Vector3 p0;
    public Vector3 p1;
    public Vector3 p2;
    public Vector3 p3;
    public Bezier(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3)
    {
        this.p0 = v0;
        this.p1 = v1;
        this.p2 = v2;
        this.p3 = v3;
    }

    public Vector3 GetPointAtTime(float t)
    {
        Vector3 tmpVector3 = Vector3.zero;
        tmpVector3 = (1 - t) * (1 - t) * (1 - t) * p0 + 3 * t * (1 - t) * (1 - t) * p1 + 3 * t * t * (1 - t) * p2 + t * t * t * p3;
        return tmpVector3;
    }
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MyBezier : MonoBehaviour
{
    private Bezier myBezier;
    public Transform p0;//起始位置
    public Transform p1;
    public Transform p2;
    public Transform p3;  //終點位置
    private List<Vector3> pothList; //存放中間點
    private int index = 0;
    void Start()
    {
        pothList=new List<Vector3>();
        myBezier = new Bezier(p0.position,p1.position,p2.position,p3.position);
        //獲取2個點中間的100個路徑點
        for (int i = 0; i < 100; i++)
        {
            Vector3 tmpVector3 = myBezier.GetPointAtTime(0.01f*i);
            pothList.Add(tmpVector3);
        }
    }
    void FixedUpdate()
    {
        int leng = pothList.Count;
        if (leng > 0 && index < leng)
        {
            transform.position = pothList[index];
            index++;
        }
    }
}

進過計算,Bezier腳本可以簡化下算法,這樣效率會更高,代碼如下

using System;
using UnityEngine;
using System.Collections;

[Serializable]
public class Bezier
{
    public Vector3 p0;
    public Vector3 p1;
    public Vector3 p2;
    public Vector3 p3;

    private float ax;
    private float ay;
    private float az;
    private float bx;
    private float by;
    private float bz;
    private float cx;
    private float cy;
    private float cz;

    public Bezier(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3)
    {
        this.p0 = v0;
        this.p1 = v1;
        this.p2 = v2;
        this.p3 = v3;
        SetConstant();
    }

    public Vector3 GetPointAtTime(float t)
    {
        Vector3 tmpVector3 = Vector3.zero;
        //tmpVector3 = (1 - t) * (1 - t) * (1 - t) * p0 + 3 * t * (1 - t) * (1 - t) * p1 + 3 * t * t * (1 - t) * p2 + t * t * t * p3;
        float t2 = t * t;
        float t3 = t * t * t;
        float x = this.ax * t3 + this.bx * t2 + this.cx * t + p0.x;
        float y = this.ay * t3 + this.by * t2 + this.cy * t + p0.y;
        float z = this.az * t3 + this.bz * t2 + this.cz * t + p0.z;
        return new Vector3(x, y, z);
    }

    void SetConstant()
    {
        this.ax = p3.x + 3 * p1.x - p0.x - 3 * p2.x;
        this.ay = p3.y + 3 * p1.y - p0.y - 3 * p2.y;
        this.az = p3.z + 3 * p1.z - p0.z - 3 * p2.z;

        this.bx = 3 * (p0.x + p2.x - 2 * p1.x);
        this.by = 3 * (p0.y + p2.y - 2 * p1.y);
        this.bz = 3 * (p0.z + p2.z - 2 * p1.z);

        this.cx = 3 * (p1.x - p0.x);
        this.cy = 3 * (p1.y - p0.y);
        this.cz = 3 * (p1.z - p0.z);
    }

}

本週項目: 虛擬小鳥飛翔的項目

小鳥的軌跡大致按貝塞爾曲線飛翔,人爲操作的時候不能讓鳥偏離曲線太遠,限制一個範圍,開始做的時候用的純數學計算,需求比較多,做的比較複雜。後來想到一個好的解決辦法,把鳥放在空物體下,讓空物體安裝曲線飛,然後我們控制鳥和父級的距離來限制它的範圍。

需要注意的是,兩點之間的距離可能不相同,爲了讓鳥按我們想要的速度飛,需要將飛行的速度做個處理


            timer += Time.fixedDeltaTime;
            Vector3 currentTarget = resultList[index];
            float distance = Vector3.Distance(currentTarget, currentPos);
            distance = 1/distance;   //用來處理不同的距離飛行速度一樣快的一個係數
            transform.position = Vector3.Slerp(currentPos, resultList[index], currentSpeed * timer * distance);
            if (Vector3.Distance(transform.position, resultList[index]) < 0.1f)
            {
                index++;
                timer = 0;
                currentPos = transform.position;    //currentPos記錄位置用
            }




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章