Unity之使用贝塞尔曲线制作图片轮播

一、效果图

1.效果1

2.效果2

二、贝塞尔曲线

关于贝塞尔曲线的学习大家可以看这个文章进行学习https://blog.csdn.net/qq_39162826/article/details/119806754?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165313175716782395391583%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165313175716782395391583&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-119806754-null-null.142^v10^pc_search_result_control_group,157^v4^control&utm_term=unity+%E8%B4%9D%E5%A1%9E%E5%B0%94%E6%9B%B2%E7%BA%BF&spm=1018.2226.3001.4187

看了上面的贝塞尔曲线的公式,大家应该能想到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
{
    //此处距离最好是0或1 否则速度过快可能导致图片串位
    [Header("首尾消失距离")]
    [Range(0, 0.5f)]
    [SerializeField] float leftDis = 0;
    [Range(0.5f, 1f)]
    [SerializeField] float rightDis = 1;

    [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;
        Debug.Log(space);

        //循环初始化
        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 = new Vector3(_pos.x, _pos.y, -1);
            SetImageSprite(1, obj); //修改图片
        }
    }
    #endregion

    #region 物体移动
    string valueSt;
    string[] valueArray;
    void objMove(Vector2 pos, Item obj)
    {
        float value = (pos.x * speed * Time.deltaTime) / Screen.width;
        obj.BezierT += value;


        if (obj.BezierT > rightDis)
        {
            valueSt = obj.BezierT.ToString();
            valueArray = valueSt.Split('.');
            valueSt = "0." + valueArray[valueArray.Length - 1];
            obj.BezierT = float.Parse(valueSt);
        }

        if (obj.BezierT <= leftDis)
        {
            valueSt = obj.BezierT.ToString();
            valueArray = valueSt.Split('.');
            valueSt = "0." + valueArray[valueArray.Length - 1];
            obj.BezierT = rightDis - Mathf.Abs(float.Parse(valueSt));
        }


        if (obj.BezierT >= rightDis)
        {
            int index = pos.x > 0 ? 1 : -1;
            obj.BezierT = leftDis;
            //替换图片
            SetImageSprite(index, obj); //修改图片
        }

        Vector3 _pos = Bezier.N_OrderBezierLerp(bezierPointList, obj.BezierT);
        obj.transform.position = new Vector3(_pos.x, _pos.y, -1); 

    }

    #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.4f1

http://链接:https://pan.baidu.com/s/1bwFY80igzlOZQ7QlYx8aHg 提取码:syq1

猜你喜欢

转载自blog.csdn.net/qq_42345116/article/details/124887818