1. 程式人生 > >【Unity3d遊戲開發】遊戲中的貝塞爾曲線以及其在Unity中的實現

【Unity3d遊戲開發】遊戲中的貝塞爾曲線以及其在Unity中的實現

轉載收藏:原文連結https://www.cnblogs.com/msxh/p/6270468.html

閱讀目錄

  RT,馬三最近在參與一款足球遊戲的開發,其中涉及到足球的各種運動軌跡和路徑,比如射門的軌跡,高吊球,香蕉球的軌跡。最早的版本中馬三是使用物理引擎加力的方式實現的足球各種運動,後來的版本中使用了根據物理學公式手動計算位置和物體速度的方式實現,現在這個版本中使用的是DoTween+貝塞爾曲線調節來實現。(關於它們之間的各種優缺點我們會在以後單獨開一篇部落格來探討,屆時也會放出原始碼互相學習下)好了,言歸正傳,今天馬三就來和大家一起學習一下游戲中的貝塞爾曲線以及其在Unity中如何實現。

回到頂部

一、簡介

貝塞爾曲線是最基本的曲線,一般用在計算機 圖形學和 影象處理。貝塞爾曲線可以用來建立平滑的曲線的道路、 彎曲的路徑就像 祖瑪遊戲、 彎曲型的河流等。

        一條貝塞爾曲線是由一組定義的控制點 P0到 Pn,在 n 呼叫它的順序 (n = 1 為線性,2 為二次,等.)。第一個和最後一個控制點總是具有終結點的曲線;然而,中間兩個控制點 (如果有的話) 一般不會位於曲線上 。

  (1)貝塞爾曲線包含兩個控制點即 n = 2 稱為線性的貝塞爾曲線

  (2)貝塞爾曲線包含三個控制點即 n = 3 稱為二次貝塞爾曲線

  (3)貝塞爾曲線包含四個控制點即 n = 4,所以稱為三次貝塞爾曲線。

   貝塞爾曲線返回點的貝塞爾函式,使用線性插值的概念作為基礎。

回到頂部

二、公式

1.線性貝塞爾公式:

給定點P0、P1,線性貝茲曲線只是一條兩點之間的直線。這條線由下式給出:

其等同於線性插值。

效果圖(文章中部分圖片轉載自CSDN):

2.二次貝塞爾公式:

二次方貝茲曲線的路徑由給定點P0、P1、P2控制,這條線由下式給出:

效果圖:

3.三次貝塞爾方程:

P0、P1、P2、P3四個點在平面或在三維空間中定義了三次方貝茲曲線。曲線起始於P0走向P1,並從P2的方向來到P3。一般不會經過P1或P2;這兩個點只是用來充當控制點。P0和P1之間的間距,決定了曲線在轉而趨進P3之前,走向P2方向的“長度有多長”。 曲線的引數形式為:
效果圖:

4.一般引數形式的貝塞爾方程:

 N階貝茲曲線可如下推斷。給定點P0、P1、…、Pn,其貝茲曲線即:

如上公式可如下遞迴表達: 用表示由點 P0、 P1、…、 Pn所決定的貝茲曲線。 TIPS:

通過兩個低階的貝塞爾曲線插值的堆疊總能夠獲得更高階的貝塞爾曲線,通俗的來說通過對兩條低階的貝塞爾曲線插值,你可以求得一條高一階的貝塞爾曲線。

比如:二次貝塞爾曲線是點對點的兩個線性貝塞爾曲線的線性插值,三次貝塞爾曲線是兩條二次貝塞爾曲線的線性插值。

回到頂部

三、實現與應用

效果圖:

通過調節起始點(左邊的白球)、控制點(中間的白球)和結束點(右邊的白球)可以獲得到不同的貝塞爾曲線,然後使用LineRender元件將路徑繪製出來,以方便觀察。下面就是實現此功能的程式碼:

using UnityEngine;
using System.Collections.Generic;
[RequireComponent(typeof(LineRenderer))]
public class Bezier : MonoBehaviour
{
    public Transform[] controlPoints;
    public LineRenderer lineRenderer;
 
    private int layerOrder = 0;
    private int _segmentNum = 50;
 
 
    void Start()
    {
        if (!lineRenderer)
        {
            lineRenderer = GetComponent<LineRenderer>();
        }
        lineRenderer.sortingLayerID = layerOrder;
    }
 
    void Update()
    {
 
        DrawCurve();
 
    }
 
    void DrawCurve()
    {
            for (int i = 1; i <= _segmentNum; i++)
            {
                float t = i / (float)_segmentNum;
                int nodeIndex = 0;
                Vector3 pixel = CalculateCubicBezierPoint(t, controlPoints[nodeIndex].position,
                    controlPoints[nodeIndex+1].position, controlPoints[nodeIndex+2].position);
                lineRenderer.numPositions = i;
                lineRenderer.SetPosition(i - 1, pixel);
            }
 
    }
 
    Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
    {
        float u = 1 - t;
        float tt = t * t;
        float uu = u * u;
 
        Vector3 p = uu * p0;
        p += 2 * u * t * p1;
        p += tt * p2;
 
        return p;
    }
     
}

CalculateCubicBezierPoint()函式負責根據T值計算出對應的貝塞爾曲線中的點,DrawCurve()函式通過不斷的改變T值,並呼叫CalculateCubicBezierPoint()獲得座標點,然後通過LineRenderer將這些點繪製出來。

為了使用方便,可以將計算貝賽爾曲線的方法放到一個工具類中——BezierUtils類:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class BezierUtils
{
    /// <summary>
    /// 根據T值,計算貝塞爾曲線上面相對應的點
    /// </summary>
    /// <param name="t"></param>T值
    /// <param name="p0"></param>起始點
    /// <param name="p1"></param>控制點
    /// <param name="p2"></param>目標點
    /// <returns></returns>根據T值計算出來的貝賽爾曲線點
    private static  Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
    {
        float u = 1 - t;
        float tt = t * t;
        float uu = u * u;
 
        Vector3 p = uu * p0;
        p += 2 * u * t * p1;
        p += tt * p2;
 
        return p;
    }
 
    /// <summary>
    /// 獲取儲存貝塞爾曲線點的陣列
    /// </summary>
    /// <param name="startPoint"></param>起始點
    /// <param name="controlPoint"></param>控制點
    /// <param name="endPoint"></param>目標點
    /// <param name="segmentNum"></param>取樣點的數量
    /// <returns></returns>儲存貝塞爾曲線點的陣列
    public static Vector3 [] GetBeizerList(Vector3 startPoint, Vector3 controlPoint, Vector3 endPoint,int segmentNum)
    {
        Vector3 [] path = new Vector3[segmentNum];
        for (int i = 1; i <= segmentNum; i++)
        {
            float t = i / (float)segmentNum;
            Vector3 pixel = CalculateCubicBezierPoint(t, startPoint,
                controlPoint, endPoint);
            path[i - 1] = pixel;
            Debug.Log(path[i-1]);
        }
        return path;
 
    }
}

通過呼叫 GetBeizerList( )方法就可以獲得到一個包含著計算出的貝塞爾曲線的陣列,然後讓Obejct沿著數組裡面的路徑移動就可以模擬出各種曲線運動的效果了,比如炮彈的飛行軌跡,香蕉球、弧圈球等等各種各樣的曲線效果了,比如下面的效果圖:

 

部落格中貝塞爾曲線工程的開源地址:https://github.com/XINCGer/Unity3DTraining/tree/master/BezierTest

 

作者:馬三小夥兒
出處:http://www.cnblogs.com/msxh/p/6270468.html 
請尊重別人的勞動成果,讓分享成為一種美德,歡迎轉載。另外,文章在表述和程式碼方面如有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!