【Unity3d游戏开发】游戏中的贝塞尔曲线以及其在Unity中的实现

article/2025/10/18 19:35:18

  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组件将路径绘制出来,以方便观察。下面就是实现此功能的代码:

 1 using UnityEngine;
 2 using System.Collections.Generic;
 3 [RequireComponent(typeof(LineRenderer))]
 4 public class Bezier : MonoBehaviour
 5 {
 6     public Transform[] controlPoints;
 7     public LineRenderer lineRenderer;
 8  
 9     private int layerOrder = 0;
10     private int _segmentNum = 50;
11  
12  
13     void Start()
14     {
15         if (!lineRenderer)
16         {
17             lineRenderer = GetComponent<LineRenderer>();
18         }
19         lineRenderer.sortingLayerID = layerOrder;
20     }
21  
22     void Update()
23     {
24  
25         DrawCurve();
26  
27     }
28  
29     void DrawCurve()
30     {
31             for (int i = 1; i <= _segmentNum; i++)
32             {
33                 float t = i / (float)_segmentNum;
34                 int nodeIndex = 0;
35                 Vector3 pixel = CalculateCubicBezierPoint(t, controlPoints[nodeIndex].position,
36                     controlPoints[nodeIndex+1].position, controlPoints[nodeIndex+2].position);
37                 lineRenderer.numPositions = i;
38                 lineRenderer.SetPosition(i - 1, pixel);
39             }
40  
41     }
42  
43     Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
44     {
45         float u = 1 - t;
46         float tt = t * t;
47         float uu = u * u;
48  
49         Vector3 p = uu * p0;
50         p += 2 * u * t * p1;
51         p += tt * p2;
52  
53         return p;
54     }
55      
56 }

CalculateCubicBezierPoint()函数负责根据T值计算出对应的贝塞尔曲线中的点,DrawCurve()函数通过不断的改变T值,并调用CalculateCubicBezierPoint()获得坐标点,然后通过LineRenderer将这些点绘制出来。

为了使用方便,可以将计算贝赛尔曲线的方法放到一个工具类中——BezierUtils类:

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4  
 5 public class BezierUtils
 6 {
 7     /// <summary>
 8     /// 根据T值,计算贝塞尔曲线上面相对应的点
 9     /// </summary>
10     /// <param name="t"></param>T值
11     /// <param name="p0"></param>起始点
12     /// <param name="p1"></param>控制点
13     /// <param name="p2"></param>目标点
14     /// <returns></returns>根据T值计算出来的贝赛尔曲线点
15     private static  Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
16     {
17         float u = 1 - t;
18         float tt = t * t;
19         float uu = u * u;
20  
21         Vector3 p = uu * p0;
22         p += 2 * u * t * p1;
23         p += tt * p2;
24  
25         return p;
26     }
27  
28     /// <summary>
29     /// 获取存储贝塞尔曲线点的数组
30     /// </summary>
31     /// <param name="startPoint"></param>起始点
32     /// <param name="controlPoint"></param>控制点
33     /// <param name="endPoint"></param>目标点
34     /// <param name="segmentNum"></param>采样点的数量
35     /// <returns></returns>存储贝塞尔曲线点的数组
36     public static Vector3 [] GetBeizerList(Vector3 startPoint, Vector3 controlPoint, Vector3 endPoint,int segmentNum)
37     {
38         Vector3 [] path = new Vector3[segmentNum];
39         for (int i = 1; i <= segmentNum; i++)
40         {
41             float t = i / (float)segmentNum;
42             Vector3 pixel = CalculateCubicBezierPoint(t, startPoint,
43                 controlPoint, endPoint);
44             path[i - 1] = pixel;
45             Debug.Log(path[i-1]);
46         }
47         return path;
48  
49     }
50 }

通过调用 GetBeizerList( )方法就可以获得到一个包含着计算出的贝塞尔曲线的数组,然后让Obejct沿着数组里面的路径移动就可以模拟出各种曲线运动的效果了,比如炮弹的飞行轨迹,香蕉球、弧圈球等等各种各样的曲线效果了,比如下面的效果图:

 

博客中贝塞尔曲线工程的开源地址:https://github.com/XINCGer/Unity3DTraining/tree/master/BezierTest

 

作者:马三小伙儿
出处:http://www.cnblogs.com/msxh/p/6270468.html 
请尊重别人的劳动成果,让分享成为一种美德,欢迎转载。另外,文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!


http://chatgpt.dhexx.cn/article/yEtoVtPY.shtml

相关文章

Bezier Curve贝塞尔曲线概念

一、贝塞尔曲线的概念 对于两点之间的连线&#xff0c;我们可以用直线进行连接效果如下&#xff1a; 其中的每一个F点都在AB连接的线段上。 这就是一阶贝塞尔曲线。 如果我们加入一个控制点C&#xff0c;那么做图如下&#xff1a; 1、连接AC,BC&#xff1b; 2、在AC,B…

贝塞尔(贝兹尔)曲线介绍

2019独角兽企业重金招聘Python工程师标准>>> 贝塞尔(贝兹尔)曲线介绍 什么是贝塞尔曲线&#xff1f; “贝赛尔曲线”是由法国数学家Pierre Bzier所发明&#xff0c;由此为计算机矢量图形学奠定了基础。它的主要意义在于无论是直线或曲线都能在数学上予以描述。贝塞尔…

【ARM】-栈帧

ARM 栈帧 本系列均以 corter-A7(armv7-a) 为例 在 ARM 中&#xff0c;通常为满减栈&#xff08;Full Descending FD&#xff09;, 也就是说&#xff0c;堆栈指针指向堆栈内存中最后一个填充的位置&#xff0c;并且随着每个新数据项被压入堆栈而递减。 栈的本质 要理解栈的本…

JVM 栈和栈帧

tag: jvm,stack,stack frame,栈,栈帧 原文&#xff1a;JVM Stacks and Stack Frames 翻译&#xff1a;陈同学 欢迎访问陈同学博客原文&#xff0c;文章可读性更佳 前情提要 对于没有深度递归的函数来说&#xff0c;无需担心上篇文章中的算法。当知道正在处理数据集有限时&am…

C语言的函数栈帧

⭐️前面的话⭐️ &#x1f4d2;博客主页&#xff1a;未见花闻的博客主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4cc;本文由未见花闻原创&#xff0c;CSDN首发&#xff01; ✉️坚持和努力一定能换来诗与远方&#xff01; &…

【C语言】函数栈帧的创建和销毁(1)

前言&#xff1a; ❓ 在我们前期学习C语言时&#xff0c;你是否曾产生过很多困惑&#xff1f; &#x1f4ad; 比如&#xff1a;局部变量是怎么创建出来的&#xff1f;因为局部变量的值是随机值&#xff0c;我们建议将它初始化&#xff0c;那么为什么局部变量的值是随机值&am…

函数栈帧(详解版)

文章目录 前言1.浅谈C语言内存1.1 内存分配1.2 栈1.3 寄存器 2. 为main()函数开辟栈帧3.变量的初始化及函数调用4.ADD函数4.1 ADD函数的创建4.2 ADD函数使用与销毁 5.总结 前言 前期学习的时候&#xff0c;我们可能有很多困感? 比如: ●局部变量是怎么创建的?I为什么局部变量…

也谈栈和栈帧

&#xfeff;&#xfeff; 一个码农要是没遇见过coredump&#xff0c;那他就是神仙了。core file(coredump的转储文件)中保存的最重要内容之一&#xff0c;就是函数的call trace。还原这部分内容 (栈回溯) &#xff0c;并与原代码对应上&#xff0c;尽快找出程序崩溃的位置和…

(栈帧和函数调用一)栈帧,函数调用与栈的关系

&#xff08;栈帧和函数调用一&#xff09;栈帧&#xff0c;函数调用与栈的关系 一&#xff0c;栈帧的介绍二&#xff0c;函数调用与栈的关系三&#xff0c;汇编演示四&#xff0c;总结 在计算机科学中&#xff0c;栈是一个特殊的容器&#xff0c;用户可以将数据压入栈中&#…

理解栈帧和栈的运行原理

栈中的数据都是以栈帧&#xff08;Stack Frame&#xff09;的格式存在&#xff0c;栈帧是一个内存区块&#xff0c;是一个数据集&#xff0c;是一个有关方法 (Method) 和运行期数据的数据集&#xff0c;当一个方法A被调用时就产生了一个栈帧 F1&#xff0c;并被压入到栈中&…

函数栈帧的形成与释放

✅作者简介&#xff1a;嵌入式入坑者&#xff0c;与大家一起加油&#xff0c;希望文章能够帮助各位&#xff01;&#xff01;&#xff01;&#xff01; &#x1f4c3;个人主页&#xff1a;rivencode的个人主页 &#x1f525;系列专栏&#xff1a;玩转C语言 &#x1f4ac;推荐一…

【函数栈帧的创建和销毁】(超详细图解)

想必大家在学完C语言函数章节之后&#xff0c;是否有这样的困惑&#xff1a; 局部变量是怎么创建的 &#xff1f; 为什么局部变量的值是随机值 &#xff1f; 函数是怎么传参的&#xff1f;传参的顺序又是什么样的 &#xff1f; 形参和实参是什么关系 &#xff1f; 函数调用…

C语言函数栈帧详解

系列文章目录 前言 最近正在学习栈帧方面的知识&#xff0c;由于本人对汇编不太熟悉&#xff0c;对其中频繁出现的ESP寄存器和EBP寄存器一直没搞清楚&#xff0c;在查找资料后&#xff0c;在此进行整理&#xff0c;方便以后温故知新。 一、预备知识 要清楚理解栈帧的概念&…

详解栈帧结构

https://www.1024do.com/?p367 栈帧结构 含义&#xff1a;C语言中&#xff0c;每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。栈帧也叫过程活动记录&#xff0c;是编译器用来实现过程函数调用的一种数据结构。 从逻辑上讲&#xff0c;栈帧就是…

函数栈帧详解

目录 一.什么是函数栈帧 1.寄存器&#xff1a; 2.函数栈帧 3.栈帧的作用和维护 4.栈帧结构 二.函数栈帧的创建 1.汇编代码 2.main函数函数栈帧的创建 1.汇编语言讲解&#xff1a; 2.栈帧创建&#xff1a; 3.详细步骤 3.ADD函数栈帧的创建 栈帧创建&#xff1a; 3.函…

栈帧

首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(地址地)。下图为典型的存取器安排,观察栈在其中的位置 入栈操…

什么是函数栈帧

函数栈帧的创建与销毁 一、函数栈帧的创建1.寄存器2.函数栈帧3.函数中调用函数 二、函数栈帧的销毁总结 一、函数栈帧的创建 1.寄存器 一般来说&#xff0c;计算机中的寄存器有六种 分别是&#xff1a;eax, ebx, ecx,edx,ebp,esp 而ebp,esp这两个寄存器中存放的是地址&#…

栈帧 stack frame

栈帧 stack frame 每一次函数调用都会维护一个栈帧&#xff08;stack frame&#xff09;&#xff0c;栈帧主要用于传递参数、保存返回地址、保存局部变量等。先直接上一个《深入理解计算机系统》上的原图。 其中&#xff0c;%rsp 指向栈顶位置&#xff0c;%rbp 指向栈底位置。…

C/函数栈帧

&#x1f331;博客主页&#xff1a;大寄一场. &#x1f331;系列专栏&#xff1a;C语言学习笔记 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ​ 目录 前言 寄存器 1. 寄存器的种类与功能 C语言汇编指令介绍 函数栈帧的创建与销毁过程 1.函数栈帧的…

浅谈函数栈帧(Stack Frame)

&#x1f499;作者&#xff1a;阿润菜菜 &#x1f4d6;专栏&#xff1a;C 本文目录 什么是栈帧 在调试中观察 总结 什么是栈帧 那我们先来看看什么是栈&#xff1a; 栈(stack)是限定仅在表尾进行插入或者删除的线性表。栈是一种数据结构&#xff0c;它按照后进先出的原则存储…