Unity 之 贝塞尔曲线介绍和实际使用

简介: Unity 中对贝塞尔曲线的实战应用,制作可视化操作曲线工具,文末附工具源码链接~

一,什么是贝塞尔曲线

百度百科诠释:

   贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。在Flash4中还没有完整的曲线工具,而在Flash5里面已经提供出贝塞尔曲线工具。

说太多概念也没什么用,直接看公式吧:

1.线性公式:

给定点P0、P1,线性贝兹曲线只是一条两点之间的直线。这条线由下式给出:
1
一阶曲线就是根据t来的线性插值,还不明白,来看张图你应该就明白了;
1.1.1
(网图)


2.二次方公式:

二次方贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)公式推导:由(P0,P1),(P1,P2)分别求线性公式所得的结果P0‘ 和 P1‘再带入线性公式,整理所得即为二次公式:

P0,P1所求:
1.1.1
P1,P2所求:
1.2.2
P0,P1,P2二次方公式:
1.2.3
简化所得:
1.2.4


3.三次方公式

P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝兹曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向。P0和P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。

其公式为:
1.3.1
【推导方式(二次方公式同理):(P0,P1,P2),(P1,P2,P3)分别求二次方公式,所得结果在带入线性公式,简化后即为上式】


4.一般参数公式

N阶贝塞尔曲线可如下推断。给定点P0、P1、…、Pn,其公式为:
1.4.1

贝塞尔曲线
(百度百科图)


二,在UNITY中的运用

1.公式转换为代码:

public class BezierCurveTest
{    
    /// <summary>
    /// 线性公式
    /// </summary>
    Vector3 Bezier(Vector3 p0, Vector3 p1, float t)
    {
        return (1 - t) * p0 + t * p1;
    }

    /// <summary>
    /// 二次方公式
    /// </summary>
    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;
    }

    /// <summary>
    /// 三次方公式
    /// </summary>
    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中绘制一条曲线

2.2.1
搭建场景如下图:
2.2.2
创建一个空物体作为父物体并挂载下面脚本,然后创建四个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;
    /// <summary>
    /// 绘制曲线控制点 -- 此脚本的子物体
    /// </summary>
    public List<GameObject> ControlPointList = new List<GameObject>();
    /// <summary>
    /// 当前绘制曲线的所有点
    /// </summary>
    public List<Vector3> CurvePointList = new List<Vector3>();

    /// <summary>
    /// 编辑状态下子自动绘制曲线
    /// </summary>
    private void OnDrawGizmos()
    {
        // 绘制前重新添加控制点
        ControlPointList.Clear();
        foreach (Transform item in transform)
        {
            ControlPointList.Add(item.gameObject);
        }

        // Select 取每个点的position作为新的元素
        List<Vector3> controlPointPos = 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]);
        }

    }

    /// <summary>
    /// 获取绘制点
    /// </summary>
    /// <param name="controlPoints"></param>
    /// <param name="segmentsPerCurve"></param>
    /// <returns></returns>
    public List<Vector3> GetDrawingPoints(List<Vector3> controlPoints, int segmentsPerCurve)
    {
        List<Vector3> points = new List<Vector3>();
        // 下一段的起始点和上段终点是一个,所以是 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制作了一个可视化操作生成曲线点的工具;

实现原理:和二项中一致,我添加了一键保存配置文件扩展,它可以帮助你快速的实现关卡路径的制作,将所得的坐标点保存到本地文件,用的时候在拿出来用就可以了;

场景搭建:
3.1.1
实现效果如下:
3.1.2

Demo链接

相关文章
|
7月前
|
图形学
浅谈Unity之贝塞尔曲线
贝塞尔曲线的创建
|
2天前
|
图形学
【Unity Shader 描边效果_案例分享】
【Unity Shader 描边效果_案例分享】
|
4月前
|
算法框架/工具 图形学
Unity 四元数
Unity 四元数
|
4月前
|
图形学
Unity 坐标系
Unity 坐标系
Unity 坐标系
|
4月前
|
数据可视化 图形学
Unity射线编程详解
Unity射线编程详解
|
7月前
|
iOS开发
iOS 贝塞尔曲线绘制滴水效果
iOS 贝塞尔曲线绘制滴水效果
33 0
|
图形学 开发者
Unity3D刚体
Unity3D刚体
135 0
|
图形学
Unity3D旋转
Unity3D旋转
98 0
|
API Android开发 异构计算
OpenGL 实践之贝塞尔曲线绘制
说到贝塞尔曲线,大家肯定都不陌生,网上有很多关于介绍和理解贝塞尔曲线的优秀文章和动态图。
726 0
OpenGL 实践之贝塞尔曲线绘制