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
{
    //起始和结束点最好有预留,否则拖动时可能会出现左右闪烁问题
    [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

  • 15
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值