一、效果图
1.效果1
2.效果2
二、贝塞尔曲线
看了上面的贝塞尔曲线的公式,大家应该能想到unity中的一个函数Lerp吧,我们根据上一个文章来看:
1. 一阶贝塞尔曲线公式是这样的
List<Vector3> pointsList=new List<Vector3>() {Vector3.zero,Vector3.one };
//一阶贝塞尔
public Vector3 FirstOrderBezier(float t)
{
Vector3 a = pointsList[0];
Vector3 b = pointsList[1];
return a + (b - a) * t;
}
如果我们使用Lerp来写的话是这样:
List<Vector3> pointsList=new List<Vector3>() {Vector3.zero,Vector3.one };
//一阶贝塞尔
public Vector3 FirstOrderBezierLerp(float t)
{
Vector3 a = pointsList[0];
Vector3 b = pointsList[1];
return Vector3.Lerp(a,b,t);
}
2. 二阶贝塞尔曲线
List<Vector3> pointsList=new List<Vector3>() {Vector3.zero,Vector3.one, new Vector3(2,2,2)};
//二阶贝塞尔
public Vector3 SecondOrderBezierLerp(float t)
{
Vector3 a = pointsList[0];
Vector3 b = pointsList[1];
Vector3 c = pointsList[2];
Vector3 aa = a + (b - a) * t;
Vector3 bb = b + (c - b) * t;
return aa + (bb - aa) * t;
}
Lerp
//二阶贝塞尔
public Vector3 SecondOrderBezierLerp(float t)
{
Vector3 a = pointsList[0];
Vector3 b = pointsList[1];
Vector3 c = pointsList[2];
Vector3 aa = Vector3.Lerp(a, b, t);
Vector3 bb = Vector3.Lerp(b, c, t);
return Vector3.Lerp(aa, bb, t);
}
好的根据这个规律我们写一个n阶贝塞尔曲线
#region N阶贝塞尔曲线
public List<Vector3> bezierPointList;
//存储计算的贝塞尔曲线结果
List<Vector3> calculateBezierPointList=new List<Vector3>();
public Vector3 NOrderBezierLerp(float t)
{
//点为空或为0
if (bezierPointList==null|| bezierPointList.Count<=0)
return Vector3.zero;
//点为1
if (bezierPointList.Count == 1)
return bezierPointList[0];
Vector3 result = Vector3.zero; //返回的结果
//列表赋值
calculateBezierPointList = bezierPointList;
//当列表的值大于等于2时执行
while (calculateBezierPointList.Count>=2)
{
List<Vector3> pointList = new List<Vector3>();
for (int i = 0; i < calculateBezierPointList.Count-1; i++)
{
Vector3 posA = calculateBezierPointList[i];
Vector3 posB = calculateBezierPointList[i+1];
pointList.Add(Vector3.Lerp(posA, posB,t));
}
//将计算好的贝塞尔曲线列表赋值给calculateBezierPointList
calculateBezierPointList = pointList;
}
//最终计算后列表中只有1个点
result = calculateBezierPointList[0];
return result;
}
#endregion
然后我们用LineRenderer组件来测试这个N阶贝塞尔曲线是否管用
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// N阶贝塞尔曲线测试
/// </summary>
public class N_OrderBezierLerpTest : MonoBehaviour
{
LineRenderer line;
void Start()
{
line = GetComponent<LineRenderer>();
line.positionCount = 100;
}
void Update()
{
//实时创建贝塞尔曲线点
for (int i = 1; i < line.positionCount + 1; i++)
{
Vector3 Bezierposition = N_OrderBezierLerp(i * 0.01f);
line.SetPosition(i - 1, Bezierposition);
}
}
#region N阶贝塞尔曲线
public List<Vector3> bezierPointList;
//存储计算的贝塞尔曲线结果
List<Vector3> calculateBezierPointList=new List<Vector3>();
public Vector3 N_OrderBezierLerp(float t)
{
//点为空或为0
if (bezierPointList==null|| bezierPointList.Count<=0)
return Vector3.zero;
//点为1
if (bezierPointList.Count == 1)
return bezierPointList[0];
Vector3 result = Vector3.zero; //返回的结果
//列表赋值
calculateBezierPointList = bezierPointList;
//当列表的值大于等于2时执行
while (calculateBezierPointList.Count>=2)
{
List<Vector3> pointList = new List<Vector3>();
for (int i = 0; i < calculateBezierPointList.Count-1; i++)
{
Vector3 posA = calculateBezierPointList[i];
Vector3 posB = calculateBezierPointList[i+1];
pointList.Add(Vector3.Lerp(posA, posB,t));
}
//将计算好的贝塞尔曲线列表赋值给calculateBezierPointList
calculateBezierPointList = pointList;
}
//最终计算后列表中只有1个点
result = calculateBezierPointList[0];
return result;
}
#endregion
}
场景布局:
运行程序来看看效果:
看这样我们就可以随心所欲的创建多种曲线效果了。
三、使用贝塞尔曲线制作图片轮播
1.Bezier.cs
贝塞尔曲线脚本,里面记录了N阶贝塞尔曲线公式
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 贝塞尔曲线工具脚本
/// </summary>
public class Bezier
{
//存储计算的贝塞尔曲线结果
static List<Vector3> calculateBezierPointList = new List<Vector3>();
/// <summary>
/// N阶贝塞尔曲线
/// </summary>
/// <param name="bezierPointList">贝塞尔曲线列表</param>
/// <param name="t">t值</param>
/// <returns></returns>
public static Vector3 N_OrderBezierLerp(List<Vector3> bezierPointList, float t)
{
//点为空或为0
if (bezierPointList == null || bezierPointList.Count <= 0)
return Vector3.zero;
//点为1
if (bezierPointList.Count == 1)
return bezierPointList[0];
Vector3 result = Vector3.zero; //返回的结果
//列表赋值
calculateBezierPointList = bezierPointList;
//当列表的值大于等于2时执行
while (calculateBezierPointList.Count >= 2)
{
List<Vector3> pointList = new List<Vector3>();
for (int i = 0; i < calculateBezierPointList.Count - 1; i++)
{
Vector3 posA = calculateBezierPointList[i];
Vector3 posB = calculateBezierPointList[i + 1];
pointList.Add(Vector3.Lerp(posA, posB, t));
}
//将计算好的贝塞尔曲线列表赋值给calculateBezierPointList
calculateBezierPointList = pointList;
}
//最终计算后列表中只有1个点
result = calculateBezierPointList[0];
return result;
}
}
2. item.cs
此脚本是要挂载在图片上,用于记录当前在曲线的位置
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
/// <summary>
/// 图片ietm
/// </summary>
public class Item : MonoBehaviour
{
float bezierT;
SpriteRenderer m_Img;
//封装
public float BezierT { get => bezierT; set => bezierT = value; }
/// <summary>
/// 修改图片精灵
/// </summary>
/// <param name="sprite"></param>
public void SetImageSprite(Sprite sprite)
{
//image为空时初始化
if (m_Img == null)
m_Img = transform.GetChild(0).GetComponent<SpriteRenderer>();
m_Img.sprite = sprite;
}
}
3.BezierController.cs
此脚本控制物体在曲线的排列,以及鼠标控制拖动等功能
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
//贝塞尔曲线控制
public class BezierController : MonoBehaviour
{
//起始和结束点最好有预留,否则拖动时可能会出现左右闪烁问题
[Header("首尾消失距离")]
[Range(0f, 0.5f)]
[SerializeField] float leftDis = 0.05f;
[Range(0.5f, 1f)]
[SerializeField] float rightDis = 0.95f;
[Header("控制鼠标拖动物体速度")]
[SerializeField] float speed = 1;
[Header("物体容器")]
public Transform content;//容器
LineRenderer line;
[Header("图片精灵列表,贝塞尔曲线点位")]
public List<Sprite> spriteList;
public List<Vector3> bezierPointList;
void Start()
{
line = GetComponent<LineRenderer>();
line.positionCount = 100;
//初始化图片布局
InitItem();
}
Vector2 oldPos;
void Update()
{
//生成贝塞尔曲线点 LineRender
Bezier_Creat();
//按下I按键初始化item布局
if (Input.GetKey(KeyCode.I))
InitItem(); //初始化
#region 鼠标拖动控制左右移动
if (Input.GetMouseButtonDown(0))
{
oldPos = Input.mousePosition;
}
if (Input.GetMouseButton(0))
{
Vector2 pos = new Vector2(Input.mousePosition.x - oldPos.x, Input.mousePosition.y - oldPos.y);
for (int i = 0; i < content.childCount; i++)
{
Item obj = content.GetChild(i).GetComponent<Item>();
objMove(pos, obj);
}
oldPos = Input.mousePosition;
}
#endregion
}
#region 初始化item平均布局
public void InitItem()
{
//Debug.Log("初始化");
//计算物体在曲线中的t值(间距)
float space = Mathf.Abs(rightDis - leftDis) / content.childCount;
//循环初始化
for (int i = 0; i < content.childCount; i++)
{
Item obj = content.GetChild(i).GetComponent<Item>();
obj.BezierT = leftDis + (i * space);
Vector3 _pos = Bezier.N_OrderBezierLerp(bezierPointList, obj.BezierT);
obj.transform.position = _pos;
SetImageSprite(1, obj); //修改图片
}
}
#endregion
#region 物体移动
void objMove(Vector2 pos, Item obj)
{
//计算物体BezierT的值
float value = (pos.x * speed * Time.deltaTime) / Screen.width;
obj.BezierT += value;
//物体BezierT的值大于等于1 (到达最右边)
if (obj.BezierT >= 1)
{
//BezierT值等于 除1的余数
obj.BezierT = obj.BezierT % 1;
SetImageSprite(1, obj);//切换下一张图片
}
//物体BezierT的值小于等于0 (到达最左边边)
if (obj.BezierT <= 0)
{
//取除1的余数
float newBezierT = obj.BezierT % 1;
//我们的贝塞尔T取值范围为0~1,不会出现负数 所以“取余的值”要为正,这里进行绝对值计算
obj.BezierT = rightDis- Mathf.Abs(newBezierT);
SetImageSprite(-1, obj);//切换上一张图片
}
//计算在贝塞尔曲线的位置并赋值
Vector3 _pos = Bezier.N_OrderBezierLerp(bezierPointList, obj.BezierT);
obj.transform.position = _pos;
}
#endregion
#region 图片替换
int currentIndex = -1;
/// <summary>
/// 图片替换
/// </summary>
/// <param name="dir">移动方向</param>
/// <param name="item">要替换的图片物体</param>
void SetImageSprite(int dir, Item item)
{
if (dir > 0)
{
currentIndex++;
//超出图片列表最大值 等于0
if (currentIndex > spriteList.Count - 1)
currentIndex = 0;
}
if (dir < 0)
{
currentIndex--;
//超出图片列表最小值 等于列表最后一个
if (currentIndex < 0)
currentIndex = spriteList.Count - 1;
}
//修改item图片
item.SetImageSprite(spriteList[currentIndex]);
}
#endregion
#region 生成贝塞尔曲线
/// <summary>
/// 生成贝塞尔曲线Line
/// </summary>
private void Bezier_Creat()
{
for (int i = 1; i < line.positionCount + 1; i++)
{
Vector3 Bezierposition = Bezier.N_OrderBezierLerp(bezierPointList, i*0.01f);
line.SetPosition(i - 1, Bezierposition);
}
}
#endregion
}
脚本中我都加了很详细的注释,如果有问题或者不懂的可以在评论交流指正。
三、原程序包
版本:2019.4.32f1
链接:https://pan.baidu.com/s/1IB_6opHR6idcrCoGMl6XVg?pwd=syq1
提取码:syq1