【unity小技巧】实现无限滚动视图和类似CSGO的开箱抽奖功能及Content Size Fitter组件的使用介绍-CSDN博客

一篇一句

我们总喜欢拿顺其自然,敷衍人生道路上的荆棘坎坷,却很少承认,真正的顺其自然,其实是竭尽所能之后的不强求,而非两手一摊的不作为。 ——瑞卡斯

前言

先来看看最终实现效果

无限滚动视图
在这里插入图片描述
滚动选中视图
在这里插入图片描述

素材

链接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。如果你遇到任何问题也欢迎你评论私信找我 虽然有些问题我可能也不一定会但是我会查阅各方资料争取给出最好的建议希望可以帮助更多想学编程的人共勉~
在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go