Unity的ScrollView无限循环滚动

本文介绍了ScrollView无限循环滚动工具ScrollCircleMaker v1.0。当ScrollView下物体众多时,该工具可优化drawcall。它功能完善,支持4个方向无限滚动,能自适应布局。文中还讲解了使用教程、设计思路、代码片段,最后提供了Demo工程下载链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

此篇文章当作知识学习即可,需要使用循环复用的小伙伴可以尝试博主近期开发的ScrollCircleMaker v1.0,此插件功能比较完整,使用方便,如果遇到问题可以联系我进行修改。

当ScrollView下的物体很多上百上千的时候,不可能去实例化出来这么多物体,这个时候需要优化了,不然drawcall会很大很大 ,所以笔者花了1,2天时间去写了一个比较实用的工具,那就是ScrollView无限循环滚动,接下来给大家展示一下效果(剪掉了一些帧数因为太大上传不了,实际效果更加丝滑),如下:

 自我感觉写的还是比较完善的,item样式刷新和点击事件的注册等等的基本功能,支持4个方向的无限滚动的,自适应Content大小进行布局,下面是使用的教程和设计的基本思路,最下面也将附上Demo工程的下载路径。

需要在Content下添加GridLayoutGroup,代码会读取GridLayoutGroup下的一些参数,然后会把GridLayoutGroup组件禁用掉,美术可以设置GridLayoutGroup的参数去查看无限滚动的预期效果,接下来和大家讲解一下设计的思路和一些代码的片段, 首先需要计算可显示区域行列最大值是多少,如果上下滚动的话,计算出来最大行还需要加1,这样子最大行*最大列就是需要初始化的item个数,当然需要把所有的item数据存在List<object>下用来刷新item,所以初始化这个工具类需要做这些事情,如图下:

可以看到这里还将一个函数传递进去,这个Text类是继承了ItemBase这个抽象类,一个Item将对应上一个Text的实例,实例化一个Item的时候就调用一下传递进去的委托创建一个Text实例对应上,初始化显示的时候按顺序调用ItemBase的函数,如图下:

 之后就是刷新的问题(这里就单单讲从上到下显示的刷新,其他都差不多),刷新需要先监听ScollView的拖动,所以如图下:

刷新的思路是如果向下移动的时候,第一行完全被覆盖了,这样就把第一行刷新到最下面的一行,改变位置和调用UpdaView改变Item样式,以此循环,向上刷新的时候第一行间距大于纵向间距,将最后的一行刷新到最前面的一行,接下展示一下代码的片段,如图下:

工程的下载链接:https://download.youkuaiyun.com/download/m0_37920739/11186683

### Unity 中实现 ScrollView无限循环滚动Unity 中,为了提高性能并减少 DrawCall 数量,在处理大量数据时通常不会一次性实例化所有的对象。取而代之的是通过复用机制来动态加载和卸载视口中的可见项。以下是基于 UGUI 和 `ScrollView` 组件的一种常见方法[^1]。 #### 1. 基本原理 无限循环滚动的核心在于仅渲染当前屏幕范围内的项目,并利用索引来模拟整个列表的连续性。具体来说: - 计算出当前屏幕上应该显示哪些项目的索引。 - 复用已经存在的 UI 对象(即池化技术),而不是频繁销毁和重新创建它们。 - 当用户滚动到接近边界区域时,调整这些对象的位置使其无缝衔接成环形结构。 #### 2. 关键代码示例 以下是一个简单的实现方案: ```csharp using UnityEngine; using System.Collections.Generic; public class InfiniteScroll : MonoBehaviour { public Transform contentPanel; // ScrollView的内容面板 public GameObject itemPrefab; // 单个项目预制体 public int totalItemCount = 1000; // 总共的数据条目数量 private List<GameObject> itemList = new List<GameObject>(); private float spacing = 5f; // 物品之间的间距 void Start() { InitializeItems(); } /// <summary> /// 初始化一定数量的对象用于复用 /// </summary> void InitializeItems() { int visibleCount = Mathf.CeilToInt(Screen.height / (itemPrefab.transform.rectTransform.sizeDelta.y + spacing)) + 2; for(int i=0;i<visibleCount;i++) { var newItem = Instantiate(itemPrefab, contentPanel); itemList.Add(newItem); UpdateItemPosition(i % totalItemCount, newItem); // 初始位置设置 } } /// <summary> /// 更新某个特定物品的位置以及其上的文字标签等内容 /// </summary> void UpdateItemPosition(int index, GameObject go) { RectTransform rt = go.GetComponent<RectTransform>(); rt.localPosition = new Vector3(0, -(index * (rt.sizeDelta.y + spacing)), 0); Text textComponent = go.GetComponentInChildren<Text>(); if(textComponent != null){ textComponent.text = $"Item {(index+totalItemCount)%totalItemCount}"; } } /// <summary> /// 检测是否需要移动顶部或底部超出视野外的元素至另一端 /// </summary> void CheckAndUpdatePositions() { foreach(var go in itemList) { if(go.transform.position.y > Screen.height || go.transform.position.y < -Screen.height ) { int currentIndex = GetIndexFromObject(go); // 移动到底部还是顶部取决于方向 bool moveUpward = Input.GetAxis("Mouse Y") > 0 ; int newIndex = CalculateNewIndex(currentIndex ,moveUpward ); UpdateItemPosition(newIndex ,go ); } } } /// <summary> /// 获取指定游戏对象对应的逻辑索引值 /// </summary> int GetIndexFromObject(GameObject obj) { return System.Convert.ToInt32(obj.name.Split(' ')[1]); } /// <summary> /// 根据当前位置计算新的有效索引 /// </summary> int CalculateNewIndex(int oldIndex,bool upwardDirection) { if(upwardDirection ){ return ((oldIndex -1 )%totalItemCount +totalItemCount )%totalItemCount ;// 向上滚则前移一位 }else{ return (oldIndex +1 )%totalItemCount;//向下滚则后退一位 } } void LateUpdate(){ CheckAndUpdatePositions(); } } ``` 此脚本实现了基本的功能框架,包括初始化必要的可视项目、更新它们的位置以反映最新的滚动状态等操作[^3]。 #### 注意事项 - **性能优化**:考虑到内存消耗与运行效率,应尽量控制每次刷新所涉及的游戏物件数目;同时也要注意纹理图集的应用以便进一步降低Draw Calls开销[^4]。 - **用户体验**:确保过渡过程平滑自然无明显卡顿现象发生,可通过插值算法改善视觉效果[^2]。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值