文章目录
- 一篇一句
- 前言
- 素材
- 一、无限滚动视图
- 1. 绘制视图
- 2. Content Size Fitter是布局控件
- (1)在文本框中使用
- (2)控制Scroll View(Scroll Rect组件)控件下Content的大小
 
- 3. 控制视图无限滚动
- 4. 向右拉无限滚动
- 5. 修复滚动视图一卡一卡的问题
 
- 二、滚动选中视图
- 1. 和前面差不多 添加滚动视图
- 2. 挂载代码并配置参数
 
- 源码
- 完结
一篇一句
我们总喜欢拿顺其自然,敷衍人生道路上的荆棘坎坷,却很少承认,真正的顺其自然,其实是竭尽所能之后的不强求,而非两手一摊的不作为。 ——瑞卡斯
前言
先来看看最终实现效果
无限滚动视图
 
 滚动选中视图
 
素材
链接:https://pan.baidu.com/s/159PuQjxYA0jdSLQ6y65mYw?pwd=qy5q
 提取码:qy5q
一、无限滚动视图
1. 绘制视图
新增滚动视图,并禁止垂直滚动
 
添加布局组件和内容大小控制容器(设置为水平首选)
 为什么使用Content Size Fitter是布局控件下面会介绍
 
 添加遮罩组件Rect Mask 2D
 
 添加字体
 
 最终效果
 
2. Content Size Fitter是布局控件
这里我觉得有必要解释一下Content Size Fitter是布局控件的作用
(1)在文本框中使用
在文本框中添加这个Content Size Fitter组件,并设置为Preferred Size之后
 文本框就会跟随文字的大小自由变化了,也是一个小技巧,但是就不可以再自定义控制文本框的大小了
 
(2)控制Scroll View(Scroll Rect组件)控件下Content的大小
- 在使用Scroll View组件的时候,一般会设置Content的大小来调节现实的内容
- 如果Content下的东西太多,就会拖不到最后面的模块了,所以这个时候给Content添加一个Content Size Fitter组件,将Vertical Fit的值设置为Preferred Size,那我们就不需要关心Content的Heigh高度了,这个时候就不怕子物体的多少了,都会正常显示出来。
演示一下
 未使用Content Size Fitter组件的情况:
 
 使用Content Size Fitter组件的情况:
 
 可以看到未添加Content Size Fitter组件时,因为Content的大小我并没有手动调节到一个合适的大小,导致下面的拖不到
 即使鼠标拖过去了,松开的时候也会返回到原来的位置
所以这个时候添加Content Size Fitter组件后,我们就可以达到一个理想的效果了
 不用在考虑Content的大小调节了!
3. 控制视图无限滚动
using UnityEngine;
using UnityEngine.UI;
public class InfiniteScroll : MonoBehaviour
{
    public ScrollRect scrollRect;  // 滚动视图组件,用于控制滚动行为
    public RectTransform viewPortTransform;  // 可视区域的RectTransform组件,用于获取可视区域的大小
    public RectTransform contentPanelTransform;  // 内容面板的RectTransform组件,用于放置项的容器
    public HorizontalLayoutGroup HLG;  // 水平布局组件,用于计算项之间的间距和对齐方式
    public RectTransform[] ItemList;  // 项的列表,包含了所有可能的项
    void Start()
    {
        // 按照空白间距(spacing)和项的宽度(width),计算可视区域所需的项数
        int ItemsToAdd = Mathf.CeilToInt(viewPortTransform.rect.width / (ItemList[0].rect.width + HLG.spacing));
        Debug.Log(ItemsToAdd);
        // 根据上面计算出来的项数,创建轮播的初始项
        for (int i = 0; i < ItemsToAdd; i++)
        {
            // 创建首批项并放置在内容面板的末尾
            RectTransform RT = Instantiate(ItemList[i % ItemList.Length], contentPanelTransform);
            //将新创建的项放置在内容面板的末尾,确保它们按顺序排列
            RT.SetAsLastSibling();
        }
        // 创建轮播的补位项,确保用户向左或向右滚动时都有相应的项可供显示
        for (int i = 0; i < ItemsToAdd; i++)
        {
            // 计算下一批项在ItemList中的索引
            int num = ItemList.Length - i - 1;
            while (num < 0)
            {
                // 对索引进行循环处理,确保不超过ItemList的长度
                num += ItemList.Length;
            }
            // 创建更多的项并放置在内容面板的开头
            RectTransform RT = Instantiate(ItemList[num], contentPanelTransform);
            //将新创建的项放置在内容面板的开头,确保它们按顺序排列
            RT.SetAsFirstSibling();
        }
		// 计算并设置内容面板的初始位置,使得第一批轮播项的左侧与可视区域的左侧对齐
        contentPanelTransform.localPosition = new Vector3(
            (0 - (ItemList[0].rect.width + HLG.spacing) * ItemsToAdd),  // 需要向左偏移的距离
            contentPanelTransform.localPosition.y,  // 不需要上下偏移
            contentPanelTransform.localPosition.z  // 不需要前后偏移
        );
    }
}
挂载脚本,配置好参数
 
效果
 
4. 向右拉无限滚动
void Update()
{
    // 如果内容面板偏移到可视区域左侧之外,则将其向右偏移一个完整的项的宽度
    if (contentPanelTransform.localPosition.x > 0)
    {
        // 强制更新画布,确保UI显示正确
        Canvas.ForceUpdateCanvases();
        contentPanelTransform.localPosition -= new Vector3(ItemList.Length * (ItemList[0].rect.width + HLG.spacing), 0, 0);
    }
    // 如果内容面板偏移到可视区域右侧之外,则将其向左偏移一个完整的项的宽度
    if (contentPanelTransform.localPosition.x < 0 - (ItemList.Length * (ItemList[0].rect.width + HLG.spacing)))
    {
        // 强制更新画布,确保UI显示正确
        Canvas.ForceUpdateCanvases();
        contentPanelTransform.localPosition += new Vector3(ItemList.Length * (ItemList[0].rect.width + HLG.spacing), 0, 0);
    }
}
效果
 
5. 修复滚动视图一卡一卡的问题
内容面板的速度会出现奇怪的行为,出现这种行为是因为当我们更改内容面板的位置时,它会影响速度计算,为了解决这个问题,当我们重置内容面板的位置时,我们需要忽略帧上的这些计算
修改代码
Vector2 Oldvelocity;// 上一帧的滚动速度
bool isUpdated;// 是否需要更新滚动速度
void Start()
{
    isUpdated = false;
    Oldvelocity = Vector2.zero;
    
    //。。。
}
void Update()
{
    // 如果需要更新滚动速度,则将当前速度设置为上一帧的速度
    if(isUpdated){
        isUpdated = false;
        scrollRect.velocity = Oldvelocity;
    }
    if (contentPanelTransform.localPosition.x > 0)
    {
        // 。。。
        // 更新滚动速度
        Oldvelocity = scrollRect.velocity;
        isUpdated = true;
    }
    // 如果内容面板偏移到可视区域右侧之外,则将其向左偏移一个完整的项的宽度
    if (contentPanelTransform.localPosition.x < 0 - (ItemList.Length * (ItemList[0].rect.width + HLG.spacing)))
    {
        //。。。
        
        // 更新滚动速度
        Oldvelocity = scrollRect.velocity;
        isUpdated = true;
    }
}
效果,滚动就很平滑了
 
二、滚动选中视图
类似CSGO的开箱抽奖功能
1. 和前面差不多 添加滚动视图

 效果
 
添加代码控制
x取整获得物品序号;ScrollRect.velocity判断滚动状态;Mathf.MoveTowards做平滑吸附;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class SnapToItem : MonoBehaviour
{
    public ScrollRect scrollRect;           // 滚动视图组件
    public RectTransform contentPanel;      // 内容面板组件
    public RectTransform sampleListItem;    // 样本列表项组件
    public HorizontalLayoutGroup HLG;       // 水平布局组件
    public TMP_Text NameLabel;              // 显示当前选中项的标签
    public string[] ItemNames;              // 列表项的名称数组
    bool isSnapped;                         // 是否已经对齐到了一个物品
    public float snapForce;                 // 对齐时的强度
    float snapSpeed;                        // 对齐时的速度
    void Start()
    {
        isSnapped = false;
    }
    void Update()
    {
        // 当前选中项的索引
        int currentItem = Mathf.RoundToInt((0 - contentPanel.localPosition.x) / (sampleListItem.rect.width + HLG.spacing));
        Debug.Log(currentItem);
        // 如果滚动速度小于200且没有对齐到一个物品,则进行对齐操作
        if (scrollRect.velocity.magnitude < 200 && !isSnapped)
        {
            scrollRect.velocity = Vector2.zero;
            snapSpeed += snapForce * Time.deltaTime;
            contentPanel.localPosition = new Vector3(
                Mathf.MoveTowards(contentPanel.localPosition.x, 0 - (currentItem * (sampleListItem.rect.width + HLG.spacing)), snapSpeed),
                contentPanel.localPosition.y,
                contentPanel.localPosition.z);
            
            // 更新当前选中项的标签
            if (currentItem >= 0 && currentItem < ItemNames.Length)
            {
                NameLabel.text = ItemNames[currentItem];
            }
            else
            {
                NameLabel.text = "_____";
            }
            // 如果已经对齐到了一个物品,则停止对齐
            if (contentPanel.localPosition.x == 0 - (currentItem * (sampleListItem.rect.width + HLG.spacing)))
            {
                isSnapped = true;
            }
        }
        // 如果滚动速度大于200,则重置对齐状态
        if(scrollRect.velocity.magnitude > 200)
        {
            NameLabel.text = "_____";
            isSnapped = false;
            snapSpeed = 0;
        }
    }
}
2. 挂载代码并配置参数

 效果
 
源码
为了防止大家变懒,源码就不提供了,大家直接可以照着文章思路进行学习
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。点赞越多,更新越快哦!当然,如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!
好了,我是向宇,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
 













![[Docker]一.Docker 简介与安装](https://img-blog.csdnimg.cn/b240c91596204ffa974936dc021e0129.png)





