Unity游戏中使用贝塞尔曲线
作者:孙广东
泰斗原文:http://www.taidous.com/forum.php?mod=viewthread&tid=33379
一、引言
在3D RPG游戏中,我们常常需要设置不同类型的弹道轨迹。本文的主要目的是为你介绍在游戏中如何使用贝塞尔曲线,提供相关的基本思路。
二、贝塞尔曲线基础
贝塞尔曲线是计算机图形学和图像处理中最基本的曲线类型,它可用于创建平滑的曲线,如道路、弯曲的路径(像祖玛游戏中的路径)以及弯曲型的河流等。
一条贝塞尔曲线由一组控制点 (P_0) 到 (P_n) 定义,其中 (n) 表示曲线的阶数((n = 1) 为线性,(n = 2) 为二次,以此类推)。第一个和最后一个控制点始终是曲线的端点,而中间的控制点(如果存在)通常不在曲线上。
- 线性贝塞尔曲线:包含两个控制点,即 (n = 2)。
- 二次贝塞尔曲线:包含三个控制点,即 (n = 3)。
- 三次贝塞尔曲线:包含四个控制点,即 (n = 4)。
贝塞尔曲线通过贝塞尔函数返回曲线上的点,其基础是线性插值的概念。下面我们先了解一下线性插值。
线性插值
线性插值是指在两个点之间获取一个点,该点的位置由参数 (t) 决定,其中 (0 \leq t \leq 1),类似于Unity中的 Mathf.Lerp 函数。
设两个点为 (P_0) 和 (P_1),插值点 (P) 的计算公式为: [P = P_0 + t (P_1 - P_0),\quad 0 \leq t \leq 1]
当 (t) 取不同值时:
- 当 (t = 0) 时,(P = P_0)。
- 当 (t = 1) 时,(P = P_1)。
- 当 (t = 0.5) 时,(P) 是 (P_0) 和 (P_1) 之间的中点。
三、不同阶数的贝塞尔曲线
线性贝塞尔曲线
线性贝塞尔曲线有两个控制点 (P_0) 和 (P_1),它实际上就是这两个点之间的直线。其曲线方程为: [B(t) = P_0 + t (P_1 - P_0) = (1 - t) P_0 + tP_1 ,\quad 0 \leq t \leq 1]
二次贝塞尔曲线
二次贝塞尔曲线具有三个控制点 (P_0)、(P_1) 和 (P_2),它是两条线性贝塞尔曲线的线性插值。具体来说,是 (P_0) 和 (P_1) 构成的线性贝塞尔曲线与 (P_1) 和 (P_2) 构成的线性贝塞尔曲线的线性插值。
其曲线方程推导如下: 首先,(B(t) = (1 - t) B_{P_0P1}(t) + t B{P_1P2}(t)),其中 (B{P_0P_1}(t) = (1 - t) P_0 + tP1),(B{P_1P_2}(t) = (1 - t) P_1 + tP_2)。
将其代入可得: [B(t) = (1 - t) [(1 - t) P_0 + tP_1] + t [(1 - t) P_1 + tP_2]]
重新排列后得到: [B(t) = (1 - t)^2P_0 + 2 (1 - t) tP_1 + t^2P_2 ,\quad 0 \leq t \leq 1]
三次贝塞尔曲线
三次贝塞尔曲线具有四个控制点 (P_0)、(P_1)、(P_2) 和 (P_3),它是两条二次贝塞尔曲线的线性插值。具体是 (P_0)、(P_1) 和 (P_2) 构成的二次贝塞尔曲线与 (P_1)、(P_2) 和 (P_3) 构成的二次贝塞尔曲线的线性插值。
其曲线方程推导如下: (B(t) = (1 - t) B_{P_0,P_1,P2}(t) + t B{P_1,P_2,P3}(t)),其中 (B{P_0,P_1,P_2}(t) = (1 - t)^2P_0 + 2 (1 - t) tP_1 + t^2P2),(B{P_1,P_2,P_3}(t) = (1 - t)^2P_1 + 2 (1 - t) tP_2 + t^2P_3)。
代入可得: [B(t) = (1 - t) [(1 - t)^2P_0 + 2 (1 - t) tP_1 + t^2P_2] + t [(1 - t)^2P_1 + 2 (1 - t) tP_2 + t^2P_3]]
重新排列后得到: [B(t) = (1 - t)^3P_0 + 3(1 - t)^2tP_1 + 3 (1 - t) t^2P_2 + t^3P_3 ,\quad 0 \leq t \leq 1]
一般来说,(n) 阶贝塞尔曲线可以通过对两个 (n - 1) 阶贝塞尔曲线进行线性插值得到。
四、贝塞尔曲线的应用与选择
在大多数应用中,通常使用二次或三次贝塞尔函数。虽然可以使用更高阶的贝塞尔函数绘制更复杂的曲线,但高阶贝塞尔函数的计算较为复杂,会增加处理开销。因此,为了绘制更复杂的曲线,我们可以多次使用二次或三次贝塞尔函数。下面是一个使用三次贝塞尔函数循环绘制 ∞ 形曲线的示例。
五、创建曲线的示例
若要创建一条曲线,需按以下步骤创建场景:
现在,将 Bezier.cs 脚本附加到 Bezier Manager。
Bezier.cs 脚本代码
using UnityEngine;
using System.Collections.Generic;
[RequireComponent(typeof(LineRenderer))]
public class Bezier : MonoBehaviour
{
public Transform[] controlPoints;
public LineRenderer lineRenderer;
private int curveCount = 0;
private int layerOrder = 0;
private int SEGMENT_COUNT = 50;
void Start()
{
if (!lineRenderer)
{
lineRenderer = GetComponent<LineRenderer>();
}
lineRenderer.sortingLayerID = layerOrder;
curveCount = (int)controlPoints.Length / 3;
}
void Update()
{
DrawCurve();
}
void DrawCurve()
{
for (int j = 0; j < curveCount; j++)
{
for (int i = 1; i <= SEGMENT_COUNT; i++)
{
float t = i / (float)SEGMENT_COUNT;
int nodeIndex = j * 3;
Vector3 pixel = CalculateCubicBezierPoint(t, controlPoints[nodeIndex].position, controlPoints[nodeIndex + 1].position, controlPoints[nodeIndex + 2].position, controlPoints[nodeIndex + 3].position);
lineRenderer.positionCount = ((j * SEGMENT_COUNT) + i);
lineRenderer.SetPosition((j * SEGMENT_COUNT) + (i - 1), pixel);
}
}
}
Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
float u = 1 - t;
float tt = t * t;
float uu = u * u;
float uuu = uu * u;
float ttt = tt * t;
Vector3 p = uuu * p0;
p += 3 * uu * t * p1;
p += 3 * u * tt * p2;
p += ttt * p3;
return p;
}
}
代码解释
CalculateCubicBezierPoint函数实现了三次贝塞尔函数,用于计算曲线上的点。DrawCurve函数用于绘制两条三次贝塞尔曲线,具体是在 (P_0)、(P_0) - 控制点1、(P_1) - 控制点1 和 (P_1) 之间,以及 (P_1)、(P_1) - 控制点1、(P_0) - 控制点2 和 (P_0) 之间绘制曲线。
任何控制点都可以控制其相应曲线的曲率,你可以在任何时间通过拖动任意控制点来改变曲线的形状。