本文共 4640 字,大约阅读时间需要 15 分钟。
Unity 之 贝塞尔曲线介绍和实际使用
一,什么是贝塞尔曲线
贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。在Flash4中还没有完整的曲线工具,而在Flash5里面已经提供出贝塞尔曲线工具。
说太多概念也没什么用,直接看公式吧:
1.线性公式:
给定点P0、P1,线性贝兹曲线只是一条两点之间的直线。这条线由下式给出:
一阶曲线就是根据t来的线性插值,还不明白,来看张图你应该就明白了; (网图)2.二次方公式:
二次方贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)公式推导:由(P0,P1),(P1,P2)分别求线性公式所得的结果P0‘ 和 P1‘再带入线性公式,整理所得即为二次公式:
P0,P1所求:
P1,P2所求: P0,P1,P2二次方公式: 简化所得:3.三次方公式
P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝兹曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向。P0和P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。
其公式为:
【推导方式(二次方公式同理):(P0,P1,P2),(P1,P2,P3)分别求二次方公式,所得结果在带入线性公式,简化后即为上式】4.一般参数公式
N阶贝塞尔曲线可如下推断。给定点P0、P1、…、Pn,其公式为:
(百度百科图)二,在UNITY中的运用
1.公式转换为代码:
public class BezierCurveTest{ ////// 线性公式 /// Vector3 Bezier(Vector3 p0, Vector3 p1, float t) { return (1 - t) * p0 + t * p1; } ////// 二次方公式 /// Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, float t) { Vector3 p0p1 = (1 - t) * p0 + t * p1; Vector3 p1p2 = (1 - t) * p1 + t * p2; Vector3 result = (1 - t) * p0p1 + t * p1p2; return result; } ////// 三次方公式 /// Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { Vector3 result; Vector3 p0p1 = (1 - t) * p0 + t * p1; Vector3 p1p2 = (1 - t) * p1 + t * p2; Vector3 p2p3 = (1 - t) * p2 + t * p3; Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2; Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3; result = (1 - t) * p0p1p2 + t * p1p2p3; return result; }}
2.举个例子
看了半天公式也转换成代码了,那么它到底用的呢?
看个例子:在Unity中绘制一条曲线 搭建场景如下图: 创建一个空物体作为父物体并挂载下面脚本,然后创建四个cube作为曲线控制点,即可在Scene视图下看到上图效果using System.Collections;using System.Collections.Generic;using System.Linq;using UnityEngine;public class BezierCurveTest : MonoBehaviour{ // 点的半径 public float radius = 1; // 曲线取点的密度 public int densityCurve = 1000; ////// 绘制曲线控制点 -- 此脚本的子物体 /// public ListControlPointList = new List (); /// /// 当前绘制曲线的所有点 /// public ListCurvePointList = new List (); /// /// 编辑状态下子自动绘制曲线 /// private void OnDrawGizmos() { // 绘制前重新添加控制点 ControlPointList.Clear(); foreach (Transform item in transform) { ControlPointList.Add(item.gameObject); } // Select 取每个点的position作为新的元素 ListcontrolPointPos = ControlPointList.Select(point => point.transform.position).ToList(); // 经过三阶运算返回的需要绘制的点 var points = GetDrawingPoints(controlPointPos, densityCurve); Vector3 startPos = points[0]; CurvePointList.Clear(); CurvePointList.Add(startPos); for (int i = 1; i < points.Count; i++) { if (Vector3.Distance(startPos, points[i]) >= radius) { startPos = points[i]; CurvePointList.Add(startPos); } } //绘制曲线 Gizmos.color = Color.blue; foreach (var item in CurvePointList) { Gizmos.DrawSphere(item, radius * 0.5f); } //绘制曲线控制点连线 Gizmos.color = Color.red; for (int i = 0; i < controlPointPos.Count - 1; i++) { Gizmos.DrawLine(controlPointPos[i], controlPointPos[i + 1]); } } /// /// 获取绘制点 /// /// /// ///public List GetDrawingPoints(List controlPoints, int segmentsPerCurve) { List points = new List (); // 下一段的起始点和上段终点是一个,所以是 i+=3 for (int i = 0; i < controlPoints.Count - 3; i += 3) { var p0 = controlPoints[i]; var p1 = controlPoints[i + 1]; var p2 = controlPoints[i + 2]; var p3 = controlPoints[i + 3]; for (int j = 0; j <= segmentsPerCurve; j++) { var t = j / (float)segmentsPerCurve; points.Add(CalculateBezierPoint(t, p0, p1, p2, p3)); } } return points; } // 三阶公式 Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) { Vector3 result; Vector3 p0p1 = (1 - t) * p0 + t * p1; Vector3 p1p2 = (1 - t) * p1 + t * p2; Vector3 p2p3 = (1 - t) * p2 + t * p3; Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2; Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3; result = (1 - t) * p0p1p2 + t * p1p2p3; return result; }}
PS:后续可以根据需求调整点的半径(radius )和曲线取点的密度(densityCurve )进行调整。
三,在实际项目中应用
根据上述示例,很容易想到在指定按照指定路径移动的情况下可以使用(比如:祖玛,保卫萝卜,塔防类)
下面这个Demo制作了一个可视化操作生成曲线点的工具;
实现原理:和二项中一致,我添加了一键保存配置文件扩展,它可以帮助你快速的实现关卡路径的制作,将所得的坐标点保存到本地文件,用的时候在拿出来用就可以了;
场景搭建:
实现效果如下:转载地址:https://czhenya.blog.csdn.net/article/details/111994712 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!